Vim is a great text editor – but Vimscript makes it even better. Vimscript, Vim’s built-in scripting language, allows you to extend Vim however you want. You can write your own commands and functions to fit what you most often want to do, so that over time, you can make Vim exactly the editor that you want it to be. The DIY syntax highlighting I wrote about recently is a form of Vimscript, but in this piece I’m going to look at scripting more generally.
Before you get started, there are a few basic rules to bear in mind when writing Vim scripts. First, a function name must start with an uppercase letter, and you should declare it with function! to make sure it’s unique. The ! will redefine it if it’s already defined (e.g. if you reload the script file). Secondly, there are a bunch of different ways of scoping variables. Consider the following:
function! Myfunction(foo, bar) let first=a:foo let second=a:bar let g:total = first + second
By default, if you just name a variable without specifying the scope (as with first and second here), the variable is limited to that function. If you want a globally accessible function, you label it with g:, as with g:total here. b:total would scope the variable within the buffer (file) that you’re executing the function within. a:foo is used to refer to parameters. Another point to bear in mind with parameters is that you can’t change a parameter within the function; let a:foo=4 would be invalid.
To actually use the function, you need to source the file that contains it, with :so filename.vim. You can put this line in your ~/.vimrc:
so filename.vim
You could of course just write the function directly in your ~/.vimrc, but it’s more maintainable to use a separate script file, which is normally called functionname.vim. If you have a bunch of small utility functions you could keep them all in a single file. These are usually stored in ~/.vim/plugin/, or for system-wide functions, $VIMHOME/plugin.
Finally, if you’re writing a script that you might release into the wild at a later stage (and arguably even if you’re not), it’s good practice to stick to the default template for formatting:
" Comment here explaining what the script/plugin does
" Last Changed: date
" Maintainer: contact details
" License: what license you're releasing the script under. (optional)
if exists("script_name")
finish endif let script_name = 1
The comment info is straightforward; the if...endif block is to check whether the script is already loaded, and if so to avoid reloading it and redefining everything.
The Function Library
To avoid reinventing the wheel, take a look at the built-in functions in Vim’s function library. They include a lot of things you might want to do with files, lines, and buffers, and they’re a good starting point for whatever you’re trying to code.
You can also use any ex command (that is, any command you can enter in ex mode) in a function. For example, here is a function that looks for a word character followed by a newline and replaces the newline with a space (thus turning multiple lines into one line, which I do before sending an article in to OpenLogic):
fu! RemoveNewLines() %s/\(\w\)\n/\1 / endfunction
That’s just a single line that I could call equally well with just :%s/\(\w\)\n/\1 / – but then I would have to remember it each time!
For flow control, Vimscript has if/else/elseif functionality, as well aswhile/endwhile, for/endfor, and continue/break:
for i in range(10) echo i endfor
You can also use lists and dictionaries for more complicated scripts.
Example Script: Counting Words
Here’s another example: a script that will tell you the number of words in the current buffer (file):
function! Wc()
let file = bufname("%")
exec '!wc -w' file endfunction
bufname returns the name of the buffer passed into it, and % refers to the current buffer. To execute an external command, you can use exec as here; note that the variable name is outside the quote marks. If it were inside the quote marks, it wouldn’t be substituted.
That’s all very well, but how do we go about calling it? Add this line at the end of your wc.vim file, then source the file again.
map <unique> <Leader>w :call Wc()
<unique> means that the mapping will fail if the same mapping already exists – that is, if something else is already mapped to <Leader>w. <Leader> refers to the value of the mapleader variable (by default or if undefined the value should be \). If mapleader is \ then this mapping means that \w will call the function. I have mine set as , so ,w calls the function.
You can also use noremap rather than map; this avoids recursive mappings.
Calling an external function, while useful, is heavy on the overhead. Can we do this from within Vim itself? To count the words in a single line, this function will do the job:
fu! WordsThisLine()
let words = len(split(getline(".")))
echo words endfu
getline returns a String containing the specified line, and "." refers to the current position; so this is the String of the line that the cursor is currently on. split by default splits on whitespace and returns a List (you can also tell it what to split on), and len returns the length of that List – which gives us the number of words.
That’s the words in a single line; what about words in the whole buffer?
fu! WordsThisBuffer()
let words = 0
for linenum in range("1", line('$'))
let words = words + len(split(getline(linenum)))
endfor
echo words endfu
line('$') returns the linenumber of the last line in the buffer. (So, similarly, to get the linenumber of the line that the cursor is currently on, you’d use line(".").) 1 is just the first line in the buffer. The for loop runs through each line in the range between 1 and the last line, counting the words each time.
If you add a call to this last function to your statusline, it will update at every keystroke, giving you a live wordcount:
:set statusline=%<%f\ %h%m%r%=%-14.(%l,%c%V%)\ wc:%{WordsThisBuffer()}\ %P
However, this is a bit inefficient; it would be better to streamline this function a bit to keep a global variable that holds the wordcount of the rest of the lines, and only keep updating the current line. If you’re interested in doing this, here’s a sample of a live word count that works that way. Note that a buffer variable (one that is kept for the lifetime of the buffer) is denoted by b:variablename. By default, variables that aren’t explicitly declared will have function scope, or global scope if declared outside a function.
Finding Useful Scripts
If you’d prefer to check first whether anyone has already done what you want, or if you just like to learn from reading other people’s scripts, there are plenty of resources available. The Vim Wikia is a great source of useful scripts, and there’s a huge list of scripts available at vim-scripts.org. They can be good jumping-off points for your own experimentation.
Related posts:
- Tips for Using Vim as an IDE
- Create Your Own Syntax Highlighting in Vim
- More Fun with Vimscript
- Vim Undo Tips and Tricks
- Generating Web Charts with Gnuplot and Web Scripting
Related Open-Source Packages
| Vim: | See all Vim Articles » | Get Vim Support at OLEX » |
|---|---|---|
| Vimscript: | See all Vimscript Articles » | Get Vimscript Support at OLEX » |














[...] http://olex.openlogic.com/wazi/2011/extending-vim-with-scripting/ Leave a Comment TrackBack URI [...]
[...] Extending Vim with Scripting [...]
[...] my last article, I looked at some of the ways in which you can use Vimscript, Vim‘s built-in scripting [...]
[...] my last article, I looked at some of the ways in which you can use Vimscript, Vim‘s built-in scripting language, [...]