47

Suppose I have a folder with lots of .h and .cpp files. I frequently need to do the following:

  1. open a file prefix_SomeReallyLongFileName.h,
  2. make some changes to it,
  3. and then open prefix_SomeReallyLongFileName.cpp.

I can do this using :e <filename> using auto-complete, but as the prefix is same for many of the files, this becomes inconvenient.

Is there a quick way to open a file with same name as current file, but a different extension?

Do other people come across this situation too, and if so what is your preferred way of navigating the C++ files in a directory? Thanks.

Neha Karanjkar
  • 3,390
  • 2
  • 29
  • 48
  • 1
    I'm not sure if this happens to many people. Usually you have your header files in a separate folder than the source files (for example `include/` and `src/`), so what you ask for doesn't happen (unless looking for the file in `../include/` or `../src/`). What I usually do is open two terminals, keep the header file open in one, and the source file in the other, since usually I go back and forth between the files. – Shahbaz Jun 18 '13 at 14:03
  • 4
    You can type `:e `, then hit ctrl-r %. This will put the current filename on the line, then you can replace the extension. – Vaughn Cato Jun 18 '13 at 14:14
  • 4
    Related questions [Using a.vim for C++](http://stackoverflow.com/q/2192152) and [Vim script to switch between header and implementation file using cscope](http://stackoverflow.com/q/5108484). – glts Jun 18 '13 at 17:08
  • different directories superset: http://stackoverflow.com/questions/3385491/opening-the-header-file-to-a-c-c-source-file-with-vim-from-multiple-directorie || SU http://superuser.com/questions/313064/switching-between-src-and-include-files-in-vim – Ciro Santilli OurBigBook.com Feb 24 '16 at 13:45

7 Answers7

76

You can use the :r (root) filename modifier which removes the last extension (check out :h filename-modifiers for more information)

:e %:r.cpp

where

  • % is shorthand for current filename.
  • :r removes the extension
  • .cpp simply appends that string at the end.

This effectively substitutes the current file's extension with another, then open the file with the newer extension.


An even shorter way (courtesy of Peter Rincker),

:e %<.cpp

Relevant documentation at :h extension-removal

Community
  • 1
  • 1
doubleDown
  • 8,048
  • 1
  • 32
  • 48
  • 1
    For me, in file `a.c`, `:e %:e.h` becomes `c.h` instead of expected `a.h` – Shahbaz Jun 18 '13 at 15:49
  • @Shahbaz, I apologize, I should be using `:r`. I'll correct the post. – doubleDown Jun 18 '13 at 15:56
  • 13
    Vim golf time! `:e %<.cpp` – Peter Rincker Jun 18 '13 at 18:39
  • @PeterRincker, You win. Updated post. – doubleDown Jun 18 '13 at 18:47
  • 6
    @PeterRincker A note: The help page for extension removal now says that the < operator is included for backwards compatibility, and the :r notation is preferred. One difference that I've found is that :r has a safeguard preventing hidden files from being removed (e.g. ".vimrc:r" will yield ".vimrc", but ".vimrc<" will yield "". For this case, either works fine, but might be worth people keeping this difference in mind. – Matt Walck Sep 16 '19 at 16:29
30

According to the Vim wiki there are quite a few suggested ways.

I will outline a few options from the article:

  • a.vim or FSwitch.vim plugins
  • using ctags
  • :e %<.c or :e %<.h. %< represents the current file w/o the extension
  • A quick mapping nnoremap <F4> :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>. Add this to your ~/.vimrc.
Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
  • 3
    Another plugin really worth mentioning is [*altr*](http://www.vim.org/scripts/script.php?script_id=4202), a better *a.vim*. It supports not only `.h` and `.cpp` but also other alternates and can be customized. – glts Jun 18 '13 at 16:41
  • I am using the above command, but I use `tag` instead of `e`. However, it says `tag not found`. Is it because it is being called from the `.vimrc` file and not from the source file? – Paschalis Mar 30 '15 at 07:50
  • 1
    @Paschalis `:tag` and `:e` are completely different commands. `:e` edits a given file while `:tag` jumps to a given tag. The command above (the mapping) basically takes the current filename, `%`, and applies some substitution trickery to change `.h` -> `.cpp` and `.cpp` -> `.h`. This means the command will switch between a source file and a header file (and back again). By using `:tag` with this mapping you are basically searching for the filename with the extension switched out which doesn't make any sense. For more help see `:h :tag`, `:h :e`, and `:h c_%`. – Peter Rincker Mar 30 '15 at 15:55
  • I should have scrolled down and read your answer before I tinkered for an hour with a vimscript doing the same as your one-liner, duh! – Christian Benke May 11 '17 at 14:01
11

Install “unimpaired” and then use ]f and [f to go the previous and next file. Since source and header have they same name except for the suffix, they are next and previous files.

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92
  • 4
    FYI for googlers: `]f` and `[f` only go through the files in the current directory. So this does answer the OP's question, but will not help those who split header and source files into different directories, etc. – Matthew Aug 24 '16 at 20:59
  • Also note that the option `'suffixes'` includes `.h` by default, and unimpaired filters out `'suffixes'`. I added `set suffixes-=.h` to my `.vimrc` to ensure `]f`/`[f` traverse header files. – jmou Mar 09 '23 at 23:18
4

This is just using simple(?!) vimscript, so you can put it into your vimrc, now it works for .c files, but can be modified pretty easily for .cpp (obviously), it even has some "error handling" in the inner if-statements (that is probably pointless), but if anyone needs it, hey, it's there! Without it it's way much shorter (just leave the :e %<.h, for example), so choose whatever you want.

function! HeaderToggle() " bang for overwrite when saving vimrc
let file_path = expand("%")
let file_name = expand("%<")
let extension = split(file_path, '\.')[-1] " '\.' is how you really split on dot
let err_msg = "There is no file "

if extension == "c"
    let next_file = join([file_name, ".h"], "")

    if filereadable(next_file)
    :e %<.h
    else
        echo join([err_msg, next_file], "")
    endif
elseif extension == "h"
    let next_file = join([file_name, ".c"], "")

    if filereadable(next_file)
        :e %<.c
    else
        echo join([err_msg, next_file], "")
    endif
endif
endfunction

then add further to your vimrc something along these lines:

let mapleader = "," " <Leader>
nnoremap <Leader>h :call HeaderToggle()<CR>

Now whenever you're in normal mode, you press comma , (this is our <Leader> button) then h and function from the above gets called, and you will toggle between files. Tada!

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
SanderC
  • 133
  • 6
3

Adding my two cents ;) to the above great answers:

  1. Install Exuberant Ctags
  2. Put the following code into your .vimrc
" Jump to a file whose extension corresponds to the extension of the current
" file. The `tags' file, created with:
" $ ctags --extra=+f -R .
" has to be present in the current directory.
function! JumpToCorrespondingFile()
    let l:extensions = { 'c': 'h', 'h': 'c', 'cpp': 'hpp', 'hpp': 'cpp' }
    let l:fe = expand('%:e')
    if has_key(l:extensions, l:fe)
        execute ':tag ' . expand('%:t:r') . '.' . l:extensions[l:fe]
    else
        call PrintError(">>> Corresponding extension for '" . l:fe . "' is not specified") 
    endif
endfunct

" jump to a file with the corresponding extension (<C-F2> aka <S-F14>)
nnoremap <S-F14> :call JumpToCorrespondingFile()<CR>
inoremap <S-F14> <C-o>:call JumpToCorrespondingFile()<CR>

" Print error message.
function! PrintError(msg) abort
    execute 'normal! \<Esc>'
    echohl ErrorMsg
    echomsg a:msg
    echohl None
endfunction
1

https://github.com/ericcurtin/CurtineIncSw.vim is an option.

Once configured searches the current directory recursively and the directory your source file is in recursively for the file you want to switch to.

ericcurtin
  • 1,499
  • 17
  • 20
-1

You can switch from .cc to .h files with :VH.