Vim Tips Wiki
Register
(Change <tt> to <code>, perhaps also minor tweak.)
(11 intermediate revisions by 10 users not shown)
Line 3: Line 3:
 
|previous=383
 
|previous=383
 
|next=385
 
|next=385
|created=December 6, 2002
+
|created=2002
 
|complexity=intermediate
 
|complexity=intermediate
 
|author=bubbleboy
 
|author=bubbleboy
Line 43: Line 43:
 
'''Cons'''
 
'''Cons'''
 
* Nothing's perfect.
 
* Nothing's perfect.
  +
  +
==Using ctags==
  +
ctags files can be used to find a file very quickly. Just add the "--extra=+f" option in the ctags line.
  +
You may then open new files manually with autocompletion, with :tag myfile.cpp
  +
Or you may adapt a script like a.vim or FSwitch.vim to call :tag with the correct filename.
  +
  +
'''Pros'''
  +
* Very quick
  +
  +
'''Cons'''
  +
* ctags files need to be generated
   
 
==By modifying ftplugins==
 
==By modifying ftplugins==
Line 53: Line 64:
 
'''Cons'''
 
'''Cons'''
 
* You have to modify filetype plugins (not that bad).
 
* You have to modify filetype plugins (not that bad).
* <tt>:find</tt> can be slow, especially when the file you are switching to does not exist.
+
* <code>:find</code> can be slow, especially when the file you are switching to does not exist.
 
* Error when file does not exist.
 
* Error when file does not exist.
   
Line 59: Line 70:
   
 
Let's say that you're editing C files, so all you would have to do is edit your ftplugin/c_extra.vim file and include
 
Let's say that you're editing C files, so all you would have to do is edit your ftplugin/c_extra.vim file and include
 
 
<pre>
 
<pre>
 
nmap ,s :find %:t:r.c<CR>
 
nmap ,s :find %:t:r.c<CR>
Line 66: Line 76:
   
 
to switch to the corresponding source file, and
 
to switch to the corresponding source file, and
 
 
<pre>
 
<pre>
 
nmap ,h :find %:t:r.h<CR>
 
nmap ,h :find %:t:r.h<CR>
Line 74: Line 83:
 
to switch to the corresponding header file.
 
to switch to the corresponding header file.
   
The built-in <tt>:find</tt> command will search (recursively or not) for the specified file anywhere in your vim 'path' setting. The <tt>:sf</tt> is short for split-find, meaning that if vim finds your file it will open it in a split window. Simply add the <tt>vert</tt> keyword before <tt>sf</tt> if you want a vertical split.
+
The built-in <code>:find</code> command will search (recursively or not) for the specified file anywhere in your vim 'path' setting. The <code>:sf</code> is short for split-find, meaning that if vim finds your file it will open it in a split window. Simply add the <code>vert</code> keyword before <code>sf</code> if you want a vertical split.
   
The mappings can (and indeed, should) be made local to buffer with a <tt><buffer></tt> modifier.
+
The mappings can (and indeed, should) be made local to buffer with a <code><buffer></code> modifier.
   
 
You may also want to use this function that lets you to only use one binding for switching:
 
You may also want to use this function that lets you to only use one binding for switching:
Line 82: Line 91:
 
function! SwitchSourceHeader()
 
function! SwitchSourceHeader()
 
"update!
 
"update!
if (expand ("%:t") == expand ("%:t:r") . ".cpp")
+
if (expand ("%:e") == "cpp")
 
find %:t:r.h
 
find %:t:r.h
 
else
 
else
Line 94: Line 103:
 
See these help pages for a full description of these built-in features:
 
See these help pages for a full description of these built-in features:
   
*{{help|expand()}} for a description of the %, :t, :r expansion
+
*{{help|expand()}} for a description of the %, :t, :r, :e expansion
 
*{{help|:find}} for a description of the 'find' and 'sf' features
 
*{{help|:find}} for a description of the 'find' and 'sf' features
 
*{{help|ftplugin}} for a description of how filetype plugins work
 
*{{help|ftplugin}} for a description of how filetype plugins work
Line 105: Line 114:
 
==Single line solution==
 
==Single line solution==
 
'''Pros'''
 
'''Pros'''
* Only takes one line in <tt>.vimrc</tt>, and so is very quick to set up
+
* Only takes one line in <code>.vimrc</code>, and so is very quick to set up
   
 
'''Cons'''
 
'''Cons'''
Line 111: Line 120:
 
* Does not handle multiple extension styles simultaneously
 
* Does not handle multiple extension styles simultaneously
   
For programmers that want to switch from <tt>foo.cpp</tt> to <tt>foo.h</tt> (or vice versa) on a single key stroke, this might help:
+
For programmers that want to switch from <code>foo.cpp</code> to <code>foo.h</code> (or vice versa) on a single key stroke, this might help:
 
 
<pre>
 
<pre>
 
map <F4> :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>
 
map <F4> :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>
Line 120: Line 128:
   
 
You could use ".hpp" or ".c" filename endings by changing it in the replacement statements.
 
You could use ".hpp" or ".c" filename endings by changing it in the replacement statements.
  +
  +
For more explanation on how this function operates, see {{help|filename-modifiers}}.
   
 
==A short function==
 
==A short function==
Line 126: Line 136:
   
 
'''Cons'''
 
'''Cons'''
* <tt>:find</tt> can be slow.
+
* <code>:find</code> can be slow.
 
* overwrites path
 
* overwrites path
   
 
Put these lines in ~/.vimrc and F2 will flip between .c and .h, .hxx and .cxx, etc. Moreover the .h file need not be in the same dir, it can be found via path variable.
 
Put these lines in ~/.vimrc and F2 will flip between .c and .h, .hxx and .cxx, etc. Moreover the .h file need not be in the same dir, it can be found via path variable.
 
 
<pre>
 
<pre>
 
set path=.,,..,../..,./*,./*/*,../*,~/,~/**,/usr/include/*
 
set path=.,,..,../..,./*,./*/*,../*,~/,~/**,/usr/include/*
Line 148: Line 157:
 
map <F2> :call Mosh_Flip_Ext()<CR>
 
map <F2> :call Mosh_Flip_Ext()<CR>
 
</pre>
 
</pre>
  +
  +
You can save your place when you switching back and forth by checking the buffers before opening the file.
  +
<pre>
  +
function! CocoaMondo_LoadFile(filename)
  +
let s:bufname = bufname(a:filename)
  +
if (strlen(s:bufname)) > 0
  +
exe ":buffer" s:bufname
  +
else
  +
exe ":find " a:filename
  +
endif
  +
endfun
  +
</pre>
  +
  +
==Manual switching==
  +
'''Pro''': No need af changing anything - works with every VIM
  +
  +
'''Con''': A little more work to use than a full-featured script that "just works"
  +
  +
A simple way to manually switch between headers and sources is by using the %< built-in variable, that resolves to the file name without extension. To switch from a header to a source you can do:
  +
  +
:e %<.c
  +
  +
Or:
  +
  +
:split %<.c
  +
  +
if you want to open it in a new "window".
   
 
==Comments==
 
==Comments==
Line 166: Line 202:
 
...
 
...
   
go there with "ggW" and tell vim to open it with "gf" (go back with "CTRL_o")
+
go there with "ggW" and tell vim to open it with "gf" (go back with "CTRL_o"). Add "set path=.,<relative include dir>" for searching for header in particular directories. For more information do ":help file-searching".
   
 
'''Bonus''' Typing "CRTL-W s" in advance shows you both the original file and its header file
 
'''Bonus''' Typing "CRTL-W s" in advance shows you both the original file and its header file
   
 
'''Pro''': No need af changing anything - works with every vim
 
'''Pro''': No need af changing anything - works with every vim
  +
  +
'''Con''': Doesn't work backwards (from file.h to file.cpp)

Revision as of 05:27, 13 July 2012

Tip 384 Printable Monobook Previous Next

created 2002 · complexity intermediate · author bubbleboy · version 5.7


Switching between source and header files is a frequent operation, and so many ways to do it have been suggested. This article tries to list them all, and also list their advantages and disadvantages.

Script a.vim

A vim script that does this can be found here: script#31, and is called a.vim.

Pros

  • Ease of use.
  • Handles multiple filetypes (not just C and C++), can be easily configured for more.
  • Code is very well commented.
  • Works with different extension styles (.C .c .cxx .cpp .CPP, .cc <--> .h .hpp .H .HPP) all at the same time - a switch will work from any source to any header extension and vice-versa.
  • The path where the associated file must be searched can be configured.

Cons

  • Quite large (~ 350 lines of code, not counting comments).

Remarks

  • It was hard to add new filetypes in older versions of the script, but that is no longer the case.

Script FSwitch.vim

Another vim script that does this can be found here: script#2590, and is called FSwitch.vim.

Pros

  • Essentially same as a.vim
  • More flexible path definitions for searches
  • File creation as part of switching is handled well (a sore point with a.vim, apparently)
  • Smaller code size than a.vim (I'm not sure a.vim's code size was ever relevant though))
  • Heavily documented
  • The author is still current and willing to add features and fixes (as of Mar 2009)

Cons

  • Nothing's perfect.

Using ctags

ctags files can be used to find a file very quickly. Just add the "--extra=+f" option in the ctags line. You may then open new files manually with autocompletion, with :tag myfile.cpp Or you may adapt a script like a.vim or FSwitch.vim to call :tag with the correct filename.

Pros

  • Very quick

Cons

  • ctags files need to be generated

By modifying ftplugins

This was the original suggestion for this article.

Pros

  • Easy to add new filetypes
  • highly configurable.

Cons

  • You have to modify filetype plugins (not that bad).
  • :find can be slow, especially when the file you are switching to does not exist.
  • Error when file does not exist.

To switch between header and source files very quickly, all you need to do is add a few key mappings in your filetype plugin files. Let me explain with an example:

Let's say that you're editing C files, so all you would have to do is edit your ftplugin/c_extra.vim file and include

nmap ,s :find %:t:r.c<CR>
nmap ,S :sf %:t:r.c<CR>

to switch to the corresponding source file, and

nmap ,h :find %:t:r.h<CR>
nmap ,H :sf %:t:r.h<CR>

to switch to the corresponding header file.

The built-in :find command will search (recursively or not) for the specified file anywhere in your vim 'path' setting. The :sf is short for split-find, meaning that if vim finds your file it will open it in a split window. Simply add the vert keyword before sf if you want a vertical split.

The mappings can (and indeed, should) be made local to buffer with a <buffer> modifier.

You may also want to use this function that lets you to only use one binding for switching:

function! SwitchSourceHeader()
  "update!
  if (expand ("%:e") == "cpp")
    find %:t:r.h
  else
    find %:t:r.cpp
  endif
endfunction

nmap ,s :call SwitchSourceHeader()<CR>

See these help pages for a full description of these built-in features:

This method is also highly configurable. All you have to do is change the 'path' setting when switching to different projects, and modify the corresponding filetype plugin to support other languages.

Because this method uses :find, it may be useful to add file paths to your Vim path automatically when you edit them.

Single line solution

Pros

  • Only takes one line in .vimrc, and so is very quick to set up

Cons

  • Only works if the header and source are in the same directory
  • Does not handle multiple extension styles simultaneously

For programmers that want to switch from foo.cpp to foo.h (or vice versa) on a single key stroke, this might help:

map <F4> :e %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>

it maps (on F4) the change of the current filename. The endings ".h" and ".cpp" are exchanged (via the magic ending ".X123X").

You could use ".hpp" or ".c" filename endings by changing it in the replacement statements.

For more explanation on how this function operates, see :help filename-modifiers.

A short function

Pros

  • Still short enough to put into vimrc.

Cons

  • :find can be slow.
  • overwrites path

Put these lines in ~/.vimrc and F2 will flip between .c and .h, .hxx and .cxx, etc. Moreover the .h file need not be in the same dir, it can be found via path variable.

set path=.,,..,../..,./*,./*/*,../*,~/,~/**,/usr/include/*

function! Mosh_Flip_Ext()
  " Switch editing between .c* and .h* files (and more).
  " Since .h file can be in a different dir, call find.
  if match(expand("%"),'\.c') > 0
    let s:flipname = substitute(expand("%"),'\.c\(.*\)','.h\1',"")
    exe ":find " s:flipname
  elseif match(expand("%"),"\\.h") > 0
    let s:flipname = substitute(expand("%"),'\.h\(.*\)','.c\1',"")
    exe ":sp " s:flipname
  endif
endfun

map <F2> :call Mosh_Flip_Ext()<CR>

You can save your place when you switching back and forth by checking the buffers before opening the file.

function! CocoaMondo_LoadFile(filename)
  let s:bufname = bufname(a:filename)
  if (strlen(s:bufname)) > 0
    exe ":buffer" s:bufname
  else
    exe ":find " a:filename
  endif
endfun

Manual switching

Pro: No need af changing anything - works with every VIM

Con: A little more work to use than a full-featured script that "just works"

A simple way to manually switch between headers and sources is by using the %< built-in variable, that resolves to the file name without extension. To switch from a header to a source you can do:

 :e %<.c

Or:

 :split %<.c

if you want to open it in a new "window".

Comments

The Single line solution can also easily be made to split the window horizontally:

 map <F4> :sp %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>

or vertically:

 map <F4> :vs %:p:s,.h$,.X123X,:s,.cpp$,.h,:s,.X123X$,.cpp,<CR>


Sometimes this works

If the file begins by including the header file like this:

 #include "somefile.h"
 ...

go there with "ggW" and tell vim to open it with "gf" (go back with "CTRL_o"). Add "set path=.,<relative include dir>" for searching for header in particular directories. For more information do ":help file-searching".

Bonus Typing "CRTL-W s" in advance shows you both the original file and its header file

Pro: No need af changing anything - works with every vim

Con: Doesn't work backwards (from file.h to file.cpp)