usr_29.txt ForVim version 9.2. Last change: 2026 Feb 14 VIM USER MANUALbyBramMoolenaar Moving through programsThe creator of Vimisa computer programmer. It's no surprise that Vimcontains many features to aid inwriting programs. Jump around to find whereidentifiers are defined and used. Preview declarations ina separate window.Thereis more in the next chapter.29.1 Usingtags29.2 The previewwindow29.3 Moving througha program29.4 Finding global identifiers29.5 Finding local identifiers Next chapter:usr_30.txt Editing programs Previous chapter:usr_28.txtFoldingTable of contents:usr_toc.txt==============================================================================29.1 UsingtagsWhatisa tag? Itisa location where an identifieris defined. An exampleisa function definition inaC or C++ program.Alist oftagsis kept inatags file. This can be used by Vim to directly jump from any place to thetag, the place where an identifieris defined. To generate thetags file for allC files in the current directory, use thefollowing command:ctags *.c"ctags"isa separate program. MostUnix systems already haveit installed.If youdo not haveit yet, you can find Universal/Exuberantctags at:http://ctags.iohttp://ctags.sf.netUniversalctagsis preferred, Exuberantctagsis no longer being developed.Now when you are in Vim and you want togo toa function definition, you canjump toit by using the following command::tag startlistThis command will find the function "startlist" even ifitis in another file. TheCTRL-] command jumps to thetag of theword thatis under the cursor.This makesiteasy to explorea tangle ofC code. Suppose, for example, thatyou are in the function "write_block". You can see thatit calls"write_line". But what does "write_line" do? By placing the cursor on thecall to "write_line" and pressingCTRL-], you jump to the definition of thisfunction. The "write_line" function calls "write_char". You need to figure out whatit does. So you position the cursor over the call to "write_char" and pressCTRL-]. Now you areat the definition of "write_char".+-------------------------------------+|void write_block(char **s; int cnt) ||{ || int i; || for (i= 0;i< cnt; ++i) || write_line(s[i]); ||} | |+-----------|-------------------------+ |CTRL-] | | +----------------------------+ +--> |void write_line(char *s) | |{ | | while(*s != 0) | |write_char(*s++); | |} | | +--------|-------------------+ |CTRL-] | | +------------------------------------+ +--> |void write_char(char c) | |{ | | putchar((int)(unsigned char)c); | |} | +------------------------------------+The ":tags" command shows thelist oftags that you traversed through::tags # TO tag FROM line in file/text 1 1 write_line 8 write_block.c 2 1 write_char 7 write_line.c>Now togo back. TheCTRL-T command goes to the preceding tag. In the exampleabove you get back to the "write_line" function, in the call to "write_char". This command takesacount argument that indicates how manytags to jumpback. You have gone forward, and now back. Let'sgo forward again. Thefollowing command goes to thetag on top of the list::tagYou can prefixit withacount and jump forward that many tags. For example:":3tag".CTRL-T also can be preceded witha count. These commands thus allow you togo downa call tree withCTRL-] and backup again withCTRL-T. Use ":tags" to find out where you are.SPLIT WINDOWSThe ":tag" command replaces the file in the currentwindow with the onecontaining the new function. But suppose you want to see not only the oldfunction but also the new one? You can split thewindow using the ":split"command followed by the ":tag" command. Vim hasa shorthand command that doesboth::stag tagnameTo split the currentwindow and jump to thetag under the cursor use thiscommand:CTRL-W ]Ifacountis specified, the newwindow will be that many lines high.MORE TAGS FILESWhen you have files in many directories, you can createatags file in each ofthem. Vim will then only be able to jump totags within that directory. To find moretags files, set the'tags' option to include all the relevanttags files. Example::set tags=./tags,./../tags,./*/tagsThis findsatags file in the same directoryas the current file, onedirectory level higher and in all subdirectories. Thisis quitea number oftags files, butit may still not be enough. Forexample, when editinga file in "~/proj/src", you will not find thetags file"~/proj/sub/tags". For this situation Vim offers to searcha whole directorytree fortags files. Example::set tags=~/proj/**/tagsONE TAGS FILEWhen Vim has to search many places fortags files, you can hear the diskrattling. It may geta bit slow. In thatcase it's better to spend thistime while generating one bigtags file. You mightdo this overnight. This requires the Universal or Exuberantctags program, mentioned above.It offers an argument to searcha whole directory tree:cd ~/projctags -R .The nice thing about thisis that Universal/Exuberantctags recognizesvariousfile types. Thus this doesn't work just forC and C++ programs, also forEiffel and even Vim scripts. See thectags documentation to tune this. Now you only need to tell Vim where your bigtags file is::set tags=~/proj/tagsMULTIPLE MATCHESWhena functionis defined multiple times (oramethod in several classes),the ":tag" command will jump to the first one. If thereisa match in thecurrent file, that oneis used first. You can now jump to other matches for the sametag with::tnextRepeat this to find further matches. If there are many, you can select whichone to jump to::tselect tagnameVim will present you withalist of choices: # pri kind tag file 1 Ff mch_init os_amiga.c mch_init() 2 Ff mch_init os_mac.c mch_init() 3 Ff mch_init os_msdos.c mch_init(void) 4 Ff mch_init os_riscos.c mch_init()Enter nr of choice (<CR> to abort):You can now enter the number (in the first column) of the match that you wouldlike to jump to. The information in the other columns give youa good idea ofwhere the matchis defined.To move between the matching tags, these commands can be used::tfirstgo to first match:[count]tpreviousgo to[count] previous match:[count]tnextgo to[count] next match:tlastgo to last matchIf[count]is omitted then oneis used.GUESSING TAG NAMESCommand line completionisa good way to avoid typinga longtag name. Justtype the first bit and press<Tab>::tag write_<Tab>You will get the first match. If it's not the one you want, press<Tab> untilyou find the right one. Sometimes you only know part of the name ofa function. Or you have manytags that start with the same string, butend differently. Then you can tellVim to useapattern to find the tag. Suppose you want to jump toatag that contains "block". First typethis::tag /blockNow use command line completion: press<Tab>. Vim will find alltags thatcontain "block" and use the first match. The "/" beforeatag name tells Vim that what followsis nota literaltagname, buta pattern. You can use all the items for search patterns here. Forexample, suppose you want to selectatag that starts with "write_"::tselect /^write_The "^"specifies that thetag starts with "write_". Otherwiseit would alsobe found halfwayatag name. Similarly "$"at theend makes sure thepatternmatches until theend ofa tag.A TAGS BROWSERSinceCTRL-] takes you to the definition of the identifier under the cursor,you can usealist of identifier namesasa table of contents. Hereis anexample. First createalist of identifiers (this requires Universal or Exuberantctags):ctags --c-types=f -f functions *.cNow start Vim withouta file, and edit this file in Vim, ina vertically splitwindow:vim:vsplit functionsThewindow containsalist of all the functions. Thereis some more stuff,but you can ignore that. Do ":setlocal ts=99" to cleanit upa bit. In this window, definea mapping::nnoremap <buffer> <CR> 0ye<C-W>w:tag <C-R>"<CR>Move the cursor to the line that contains the function you want togo to.Now press<Enter>. Vim willgo to the otherwindow and jump to the selectedfunction.RELATED ITEMSTo makecase intag names be ignored, you can set'ignorecase' while leaving'tagcase'as "followic", or set'tagcase' to "ignore".The'tagbsearch' option tells if thetags fileis sorted or not. The defaultis to assumea sortedtags file, which makesatags searcha lot faster, butdoesn't work if thetags file isn't sorted.The'taglength' option can be used to tell Vim the number of significantcharacters ina tag.Cscopeisa free program. It does not only find places where an identifierisdeclared, but also whereitis used. Seecscope.==============================================================================29.2 The previewwindowWhen you edit code that containsa function call, you need to use the correctarguments. To know what values to pass you can lookat how the functionisdefined. Thetags mechanism works very well for this. Preferably thedefinitionis displayed in another window. For this the previewwindow can beused. To opena previewwindow to display the function "write_char"::ptag write_charVim will opena window, and jumps to thetag "write_char". Thenit takes youback to the original position. Thus you can continue typing without the needto useaCTRL-W command. If the name ofa function appears in the text, you can get its definitionin the previewwindow with:CTRL-W }Thereisascript that automatically displays the text where theword underthe cursor was defined. SeeCursorHold-example.To close the previewwindow use this command::pcloseTo edita specific file in the preview window, use ":pedit". This can beuseful to edita header file, for example::pedit defs.hFinally, ":psearch" can be used to findaword in the current file and anyincluded files and display the match in the preview window. Thisisespecially useful when using library functions, for which youdo not haveatags file. Example::psearch popenThis will show the "stdio.h" file in the preview window, with the functionprototype for popen():FILE*popen __P((const char *, const char *));You can specify the height of the preview window, whenitis opened, with the'previewheight' option.==============================================================================29.3 Moving througha programSincea programis structured, Vim can recognize items in it. Specificcommands can be used to move around.C programs often contain constructs like this:#ifdef USE_POPEN fd = popen("ls", "r")#else fd = fopen("tmp", "w")#endifBut then much longer, and possibly nested. Position the cursor on the"#ifdef" and press %. Vim will jump to the "#else". Pressing% again takesyou to the "#endif". Another% takes you to the "#ifdef" again. When the constructis nested, Vim will find the matching items. Thisisagood way to check if you didn't forget an "#endif". When you are somewhere insidea "#if"- "#endif", you can jump to the startofit with:[#If you are not aftera "#if" or "#ifdef" Vim will beep. To jump forward tothe next "#else" or "#endif" use:]#These two commands skip any "#if"- "#endif" blocks that they encounter.Example:#if defined(HAS_INC_H) a = a + inc();# ifdef USE_THEME a += 3;# endif set_width(a);With the cursor in the last line, "[#" moves to the first line. The "#ifdef"- "#endif" block in the middleis skipped.MOVING IN CODE BLOCKSInC code blocks are enclosed in {}. These can get pretty long. To move tothe start of the outer block use the "[[" command. Use "][" to find the end.This assumes that the "{" and "}" are in the first column. The "[{" command moves to the start of the current block. It skips overpairs of{} at the same level. "]}" jumps to the end. An overview:function(inta) +->{ | if (a) | +->{[[ | |for (;;) --+ | | +->{ | |[{ | | foo(32); | --+ | |[{ | if (bar(a)) --+ |]} | +-- | +--break; |]} | | |} <-+ | |][ +--foobar(a) | |} <-+ |} <-+Whenwriting C++ or Java, the outer{} blockis for the class. The next levelof{}is fora method. When somewhere insideaclass use "[m" to find theprevious start ofa method. "]m" finds the next start ofa method.Additionally, "[]" moves backward to theend ofa function and "]]" movesforward to the start of the next function. Theend ofa functionis definedbya "}" in the first column.int func1(void){return 1; +---------->} |[] |int func2(void) | +->{ |[[ |if (flag)start +-- +--return flag; |][ |return 2; | +->}]] | |int func3(void) +---------->{return 3;}Don't forget you can also use "%" to move between matching (),{} and [].That also works when they are many lines apart.MOVING IN BRACESThe "[(" and "])" commands work similar to "[{" and "]}", except that theywork on () pairs instead of{} pairs. [( <-------------------------------- <-------if (a == b && (c == d || (e > f)) && x > y) --------------> --------------------------------> ])MOVING IN COMMENTSTo move back to the start ofa comment use "[/". Move forward to theend ofacomment with "]/". This only works for/*- */ comments. +-> +->/* |[/ | *A comment about --+[/ | +-- * wonderful life.|]/ | */ <-+ | +-- foo=bar * 3; --+|]//*a short comment */ <-+==============================================================================29.4 Finding global identifiersYou are editingaC program and wonder ifa variableis declaredas "int" or"unsigned".A quick way to find thisis with the "[I" command. Suppose the cursoris on theword "column". Type:[IVim willlist the matching linesit can find. Not only in the current file,but also in all included files (and files included in them, etc.). The resultlooks like this:structs.h 1: 29 unsigned column; /* column number */The advantage over usingtags or the previewwindowis that included files aresearched. In most cases this results in the right declaration to be found.Also when thetags fileis out of date. Also when you don't havetags for theincluded files. However,a few thingsmust be right for "[I" todo its work. First of all,the'include' optionmust specify howa fileis included. The default valueworks forC and C++. For other languages you will have to change it.LOCATING INCLUDED FILES Vim will find included files in the places specified with the'path'option. Ifa directoryis missing, some include files will not be found. Youcan discover this with this command::checkpathIt willlist the include files that could not be found. Also files includedby the files that could be found. An example of the output:--- Included files not found in path ---<io.h>vim.h --> <functions.h> <clib/exec_protos.h>The "io.h" fileis included by the current file and can't be found. "vim.h"can be found, thus ":checkpath" goes into this file and checks whatitincludes. The "functions.h" and "clib/exec_protos.h" files, included by"vim.h" are not found.Note:Vimis nota compiler. It does not recognize "#ifdef" statements.This means every "#include" statementis used, also whenit comesafter "#if NEVER".To fix the files that could not be found, adda directory to the'path'option.A good place to find out about thisis the Makefile. Look out forlines that contain "-I" items, like "-I/usr/local/X11". To add this directoryuse::set path+=/usr/local/X11When there are many subdirectories, you can use the "*" wildcard. Example::set path+=/usr/*/includeThis would find files in "/usr/local/include"as wellas "/usr/X11/include".When working ona project witha whole nested tree of included files, the "**"itemsis useful. This will search down in all subdirectories. Example::set path+=/projects/invent/**/includeThis will find files in the directories:/projects/invent/include/projects/invent/main/include/projects/invent/main/os/includeetc.There are even more possibilities. Check out the'path' option for info. If you want to see which included files are actually found, use thiscommand::checkpath!You will geta (very long)list of included files, the files they include, andso on. To shorten thelista bit, Vim shows "(Already listed)" for files thatwere found before and doesn'tlist the included files in there again.JUMPING TO A MATCH"[I" producesalist with only one line of text. When you want to haveacloser lookat the first item, you can jump to that line with the command:[<Tab>You can also use "[CTRL-I", sinceCTRL-Iis the sameas pressing<Tab>.Thelist that "[I" produces hasa numberat the start of each line. When youwant to jump to another item than the first one, type the number first:3[<Tab>Will jump to the third item in the list. Remember that you can useCTRL-O tojump back to where you started from.RELATED COMMANDS[ionly lists the first match]Ionly lists items below the cursor]ionly lists the first item below the cursorFINDING DEFINED IDENTIFIERSThe "[I" command finds any identifier. To find only macros, defined with"#define" use:[DAgain, this searches in included files. The'define' optionspecifies whataline looks like that defines the items for "[D". You could changeit to makeit work with other languages thanC or C++. The commands related to "[D" are:[donly lists the first match]Donly lists items below the cursor]donly lists the first item below the cursor==============================================================================29.5 Finding local identifiersThe "[I" command searches included files. To search in the current file only,and jump to the first place where theword under the cursoris used:gDHint: Goto Definition. This commandis very useful to finda variable orfunction that was declared locally("static", inC terms). Example (cursor on"counter"): +-> static int counter= 0; | | int get_counter(void)gD |{ | ++counter; +-- return counter;}To restrict the search even further, and look only in the current function,use this command:gdThis willgo back to the start of the current function and find the firstoccurrence of theword under the cursor. Actually,it searches backwards toan empty line abovea "{" in the first column. From thereit searches forwardfor the identifier. Example (cursor on "idx"):int find_entry(char *name){ +-> int idx; |gd | for (idx= 0; idx< table_len; ++idx) |if (strcmp(table[idx].name, name)== 0) +-- return idx;}==============================================================================Next chapter:usr_30.txt Editing programsCopyright: seemanual-copyright vim:tw=78:ts=8:noet:ft=help:norl: