I know there's a file called handler.ts in the same directory as the currently opened file handler.test.ts.

How do I open that file in a split window (horizontal or vertical) without doing this:

Ctrl+w followed by s, then typing :edit /full/path/to/file

I'd also like to avoid typing the full path, if possible.

  • 1
    Would using the command :Sex (:Sexplore for the long form) be good enough? It opens netrw in a split window and you can then choose you file with <CR>. Otherwise a fuzzy finder like fzf could work too.
    – statox
    Jun 28 '21 at 14:07
  • Just curious as to why you do not use an Integrated Development Environment for coding? No offence to Vi/Vim, but not every file is a nail ;-) Visual Studio code is impressive & free, but I will never regret the money invested in WebStorm (YkMMV) Jun 29 '21 at 7:14

First off, you can use :split (or just :sp) instead of typing :e and then manually splitting.

Using the % register

You could do:

:split <press ctrl+r, then type a % sign>

This will paste from register % onto the command line. And since % contains the path to the current file, you'll get this:

:split /path/to/current/file/handler.test.ts

and you can then just delete the filename using some quick presses of CTRL + w

:split /path/to/current/file/

and type the file you actually want.

Or use some wildcards if the file is somewhere else in the system

:split ~/*/*/*/my<press tab>

to get to something like this:

:split ~/path/to/place/my_file.py

then hit enter

Or use a fuzzy finder

If you're open to using fzf-vim, then you can use CTRL+h to open a file in a split.

See how to start fzf from another directory


If you have the /full/path/to/handler.ts file opened in one window, you can open a new window and then type the following command to open handler.test.ts from the /full/path/to directory:

:e #:h/handler.test.ts

There are a number of ways to go about this, depending on what you actually want.

You can use wildcards, as mattb has already mentioned. If, however, these either cause ambiguities or you just don't want to type out the directory structure as wildcards, there's alternatives.

If you're only looking to open files, I highly recommend a fuzzy finder. There's a number of these available, but fzf (and the matching Vim plugin) does allow you to open in splits with a keybind. Unfortunately, both the above options don't really care about the directory of the file you're editing, unless it happens to be the current working directory. If you have several files relative to your current working directory with the same name, you may need to scroll or search for the path to find what you're actually looking for. Also note that FZF doesn't allow file creation or manipulation (see #107), which may be a problem if you have a .ts file you'd like to make a test file from.

On the more visual side of things, there are a number of trees (such as NERDTree), which again don't care about the directory the file you're currently editing is in, but if you've browsed your way to the current file, the cursor should be right next to it when you switch back to the NERDTree buffer (or in general the buffer of whatever tree you end up using). NERDTree does allow file creation, as well as file manipulation, and different plugins may work differently with regard to the initially focused section of the tree. For a built-in solution, :Sexplore (:Sex for short) may also be useful.

And on the command side of things, we can get a bit more fancy. How fancy really depends on what the exact scope of your split needs are. All these options allow file creation.

For an instance, I have a general command for editing a file, which interprets the input relative to the file currently being edited. Modified to split instead of edit in the current buffer, you can have something like this:

command! -nargs=1 Sp :sp %:h/<args>

A caveat with this approach is that I don't really know how to set up file completion, as -complete=file checks relative to the current working directory and not your current file.Custom completion sources are a thing that would work here, though, I just haven't taken the time to actually get into how that's done. See :h expand() for the meaning of %:h (TL;DR: take current file path relative to the current working directory without the file name, i.e. the current folder, and append the new extra path to it)

Note that you can also explicitly type out :sp %:h/somefile.ts, if that's more your thing. Autocomplete (pressing tab) works provided you're in a real file, though it does that by expanding %:h. It's a super handy shortcut if you're primarily looking to reduce your overall typing, and typing it explicitly gives you full control over the path, if that's something you feel you need.

And finally, if you just want to switch between .ts and .test.ts, there's yet another expand hack.

" This function is only necessary if you only want the maps in TS files. If you don't care, feel free to remove the function and just use the content of it directly.
" Note that the maps will probably do some weird stuff if you invoke it from an incompatible file
fun! TSMaps()
    " Command explanation: 'sp ' is obvious. For the record, .. is string concatenation
    " We need %:r:r to remove both extensions. Note that if the file name is file.ts, only .ts is removed in spite of double removal.
    " Double removal is necessary here to strip file.test.ts down to "file",
    " which we can better use to append the new file name.
    " expand('%:e:e') == 'test.ts' is an alternative to the regex approach, 
    " but I didn't feel like throwing two double removal at it (largely for clarity).
    " Note: if you're not including the autocmd and function for scoping, remember to remove -buffer, or it'll only be loaded for the first buffer
    command! -buffer -nargs=0 TSSwitch execute 'sp ' .. expand('%:r:r') .. (expand('%') =~ '\v\.test\.ts$' ? '.ts' : '.test.ts')

    nnoremap <buffer> INSERT A MAPPING HERE :TSSwitch<CR>


" If you only care about the command and/or mapping, this autocmd is also redundant.
autocmd FileMaps
    autocmd FileType typescript call TSMaps()
autocmd END

Then running the command or using some mapping of your choice, you can switch directly between the .ts and the .test.ts - no file finder, no useful shortcuts, no registers, and no typing the file name. Note that for obvious reasons, if you're in handler.ts and want to switch to someotherfile.ts, this last approach obviously doesn't work. But it lets you switch between someotherfile.ts and someotherfile.test.ts without modifications.I use an equivalent bit of code for switching between .hpp and .cpp files, and I've really come to like it. Also note that this method can be abstracted to any file type pattern you may have, including files that are in equivalent folders, but with a slightly different root. (for an instance src/something.js and test/something.test.js - I'm leaving this as an exercise to the reader, however, as the number of variations makes it impractical to list)

Note that this type of approach is somewhat static in terms of buffer management - if you're looking to dynamically select split types, and you're not okay with copy-pasting the first command and making the sp a vsp, explicitly typing %:h is still a perfectly good choice (and it works regardless of which command you use, be that e, sp, vsp, tab<something>, or a custom command (provided it has file completion enabled). Again, refer to :h expand() for the other modifiers you can use.


Also worth a mention is autochdir:

When on, Vim will change the current working directory whenever you open a file, switch buffers, delete a buffer or open/close a window. It will change to the directory containing the file which was opened or selected.
Note: When this option is on some plugins may not work.

So all you would have to do is to type :sp file.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.