Learning the vi Editor/Vim/Enhancing Vim
Enhancing Vim
[edit | edit source]The .vimrc file
[edit | edit source]You can make a configuration-file called .vimrc in your home directory and save any particular settings. The existence of a vimrc has the side effect of making Vim enable all of Vim's incompatible changes to vi, making it more user-friendly. The name of the file depends on the operation system and user interface used:
.vimrc | Text-User-Interface on UNIX and VMS |
_vimrc | Text-User-Interface on MS-Windows and VMS |
.gvimrc | Graphical-User-Interface on UNIX and VMS |
_gvimrc | Graphical-User-Interface on MS-Windows and VMS |
The alternatives with the underscore are for compatibility with older filesystem. If you use Vim on several operating system and use a modern MS-Windows filesystem you don't have to maintain two configurations files. It is perfectly OK to set _vimrc to:
source ~/.vimrc
and do all your configurations in .vimrc.
This is an example .vimrc file here:
"Everything after a double quote is a comment. "Wrap text after 72 characters set textwidth=72 "Set tabs to 4 spaces set tabstop=4 set shiftwidth=4 set stselect=4 set expandtab "Tell Vim I use a dark background. Syntax highlighting (color coded text) will adjust to more appropriate colors. set background=dark "Misc overwrites of default color highlighting. hi Comment ctermfg=DarkGreen hi String ctermfg=DarkMagenta hi pythonPreCondit ctermfg=Green "Make sure that bottom status bar is running. set ruler set laststatus=2 "Make a mapping for "Q" which will reformat the current paragraph, comment, "or code block according to the formatoptions setting: map Q gqap
Syntax Highlighting
[edit | edit source]Syntax highlighting is what allows you to highlight program code and other files for better readability, using colorization, bold, and other font modifications.
You may want to write simple syntax highlighting statements for easily detected patterns in your file. That said, if you are thinking you need syntax highlighting for HTML, don't worry: most users do not need to define a syntax highlighting file for common filetypes--most of the file types common developers are interested have already been given a default syntax highlighting definition with Vim. Even if it doesn't come with vim, you can usually find someone who has shared their work on vim.org. However, if you need to write something simple, this section is for you. (If you need a syntax highlighting definition that will correctly show Perl code even inside an HTML "pre" tag inside a Perl print statement within a shell heredoc in a shell script, you're probably out of luck and this section probably won't meet your needs--but you might as well search vim.org, just in case someone has done it for you already).
Syntax highlighting is one of the most powerful features of Vim. However, it can also be one of the most difficult things to set up--if you don't know what you're doing (or if you just don't have the patience, or if you're dealing with complex program language grammars). So lets have a look at some easy highlighting definitions:
Lesson 1: Highlight Tabs
[edit | edit source]- ... or how to highlight a special characters
Say you need to know where in your file are tabs and where are spaces. With the following highlight you make tabs visible:
:syntax match Special "\t"
A syntax match matches a regular expression and applies the given color to it. In this case it is the color "Special". You must make sure that the color "Special" has a non-standard background - otherwise you won't see a difference:
:highlight Special guifg=SlateBlue guibg=GhostWhite
You can also create a map in your .vimrc - so you can always activate the tab highlight:
:nnoremap <F12><Tab> :syntax match Special "\t"<CR> :inoremap <F12><Tab> <C-O>:syntax match Special "\t"<CR>
Another approach is to use listchars. These would be tabs, trailing spaces and line ends. Note the use of ">-" instead of the default "^I". That prevents the layout change when showing/hidding listchars:
:set lcs=tab:>-,trail:%,eol:$ <CR> :map <F12> :set list!<CR>
Lesson 2: Highlight Space errors
[edit | edit source]- ... or how to highlight spaces at the end of line
- ... or how to find spaces and/or tabs
:syntax match Error "\s\+$"
Lesson 3: Highlight Tab errors
[edit | edit source]- ... or how to not highlight characters serving as delimiter
Knowing where tabs are is good. But what about spaces before tabs? They just waste space in your file. The following highlight will show them to you:
:syntax match Error " \+\t"me=e-1
The regular expression " \+\t"
searches for one or more space followed by a tab. That alone would solve the problem but would highlight the tab as error as well. Even better would be if we could highlight only the spaces and leave the tab as it is. And indeed this is possible and done by the me=e-1
. Basically it says: End the highlight one character before the last character found.
Lesson 4: Highlight Line length
[edit | edit source]- ... or how to highlight at a specific column
- ... or how to highlight inside other patterns
- ... or how to allow other pattern inside
The following match will highlight the column 78 when the line is 78 characters long. This can serve as a warning when you have a line which is longer than you wish it to be. Of course, such a highlight should not interfere with any other highlight you might use:
:syntax match Error "\(^.\{79\}\)\@<=." contains=ALL containedin=ALL
Here is a description of how it works:
- The regular expression
\(^.\{79}\)
searches for exactly 79 characters from the beginning of the line^
and group\( \)
the result. \@<=
will now "zero match" the group from 1. "zero match" means the text must be present, but is ignored once found.- with
.
one more character is matched. This character is highlighted using the Error colour. - With
contains=ALL
we allow other highlight patterns to start inside our pattern. containedin=ALL
we allow our highlight pattern to start inside another pattern.
An alternative method is suggested by the Vim help system. This pair of commands will highlight all characters in virtual column 79 and more:
:highlight rightMargin ctermfg=lightblue :match rightMargin /.\%>79v/
And this will highlight only column 79:
:highlight col79 ctermbg=red :highlight col79 guibg=red :match col79 /\%<80v.\%>79v/
Note the use of two items to also match a character that occupies more than one virtual column, such as a TAB. In the last example, a separate ctermbg and guibg definition was added so that col79 does something in both Vim and gVim.
Omni Completion
[edit | edit source]From version 7 onwards Vim supports omni completions. This form of completions should work over several files and support all the twirks of a programming language. However, for it to work you need an appropriate "*complete.vim" script in your "autoload/" directory. This script must define a function called ...#Complete which does all the completion work for the programming language at hand.
However writing a useful complete function can be a difficult task. All the provided complete functions span several hundred lines of code.
Here a simple implementation used for the Ada programming language described in detail so you can create your own. This implementation need a "tags" file which - for ada - you can create with gnat xref -v
.
The full version can be download from the vim.org side
Step by Step walkthrough
[edit | edit source]Set completion with <C-X> <C-O> to autoloaded function. This check is in place in case this script is sourced directly instead of using the autoload feature.
if exists ('+omnifunc') && &omnifunc == "" setlocal omnifunc=adacomplete#Complete endif
Omnicompletion and autoload won't work with any version of Vim less than 7.00.
if version < 700 finish endif
All complete#Complete functions have to cover two options: a:findstart == 1
and a:findstart != 1
.
function adacomplete#Complete (findstart, base)
Findstart equals 1
[edit | edit source]When a:findstart == 1
then we have to find out how many characters left of the cursor could be a part of an completion:
if a:findstart == 1
For our simple example finding the beginning of the word is pretty simple. We look left until we find a character which cannot be part of a word. For most languages searching for “\i” should do the trick. However, for Ada we want to expand Attributes as well - hence we add “'” to the list of word characters.
let line = getline ('.') let start = col ('.') - 1 while start > 0 && line[start - 1] =~ '\i\|''' let start -= 1 endwhile return start
Findstart not equal 1
[edit | edit source]When a:findstart != 1
then we need to find possible completions for a:base
. There are two options open to return the found completions:
- returning them all as a List with return.
- calling complete_add for each completion found.
You can also use a combination of both - they will be merged then - so beware not to create duplicates. A completion can either be a String or a Directory.
In this example we use complete_add.
else
The search pattern should look for a:base at the beginning of the text matched.
let l:Pattern = '^' . a:base . '.*$'
In a first step we add all known Ada Keywords, Pragmas, Attributes and Types. They have been prepared as a List of Directorys by the Ada file-type plugin. All we have to to is iterate over the list and add all where the Directory entry “word” matches the pattern.
if exists ('g:Ada_Keywords') for Tag_Item in g:Ada_Keywords if l:Tag_Item['word'] =~? l:Pattern
Add the value - incl. simple error handling.
if complete_add (l:Tag_Item) == 0 return [] endif {{vi/Ex|if} complete_check () return [] endif endif endfor endif
Searching for matches
[edit | edit source]Here the real work is done: We search for matches inside the tag file. Of course you need a tag file first. There are many tools to create Vim compatible tag files. Just have a look around for one.
let l:Tag_List = taglist (l:Pattern)
Again we need to iterate over all List elements found.
for Tag_Item in l:Tag_List if l:Tag_Item['kind'] == '' let l:Tag_Item['kind'] = 's' endif
Since the Directory structure for tags and completions are different the data needs to be converted.
The informations available inside the tag depend on the tag-file creation tool. But the minimum is:
- “name”
- Name of the tag.
- “filename”
- Name of the file where the tag is defined.
- “cmd”
- Example command used to locate the tag in the file.
- “kind”
- Type of the tag. The value for this entry depends on the language specific kind values generated by the ctags tool.
The contest of the completion is fixed and contains the following:
- “word”
- The actual completion
- “kind”
- The type of completion, one character, that is, “v” for variable.
- “menu”
- Short extra info displayed inside the completion menu.
- “word”
- Long extra info displayed inside an extra window.
- “icase”
- Ignore case
So for simple tags without any extras the conversion could look like this:
let l:Match_Item = { \ 'word': l:Tag_Item['name'], \ 'menu': l:Tag_Item['filename'], \ 'info': "Symbol from file " . l:Tag_Item['filename'] . " line " . l:Tag_Item['cmd'], \ 'kind': l:Tag_Item['kind'], \ 'icase': 1} if complete_add (l:Match_Item) == 0 return [] endif if complete_check () return [] endif endfor
- Please note
- The current Ada plugin has been extended to also support
ctags
which gives more information thangnat xref -v
. However we have not updated the walkthrough as we want to keep the example simple and easy to follow.
We already added all matches via complete_add so we only return an empty list.
return [] endif endfunction adacomplete#Complete finish endif
One last piece of advice: It your tag tool does not sort the entries then you should sort them separately. Searches on sorted tag-files are significantly faster.