Scripting Vim with Python
I've created and released a small Vim plugin that will try to mimic TextMate's behaviour to insert the closing pair of quotes, brackets, parentheses, braces etc. Download simple pairs! (I've also created one for Emacs)
I used Vim's built-in python scripting support, and seeing that it wasn't documented very well, I present some useful patterns here.
Python inside Vim script
The first thing to know is how to define a block as Python code inside Vim script. You can do it like that:
python << endpython
# your python code here
endpython
endpython
can be whatever you want, as long as it is in the beginning of the line by itself. Other people use EOF
, but I find it confusing.
Interfacing with Vim
Inside Python code, you can import vim
and you have access to a lot of vim objects, the most useful IME being:
vim.command
- execute a vim commandvim.eval
- evaluate a vim expression and return the resultvim.current.window.cursor
- get the cursor position as (row, col) (row is 1-based, col is 0-based)vim.current.buffer
- a list of the lines in the current buffer (0-based, unfortunately)
So to get the current line, you can use:
import vim
(row, col) = vim.current.window.cursor
line = vim.current.buffer[row-1] # 0 vs 1 based
prevChar, nextChar = line[col-1], line[col] # will IndexError if at start or end of line
Of course, you can also update the cursor position and change the line contents:
vim.current.window.cursor = (1, 0) # move to top left
vim.current.buffer[0] = "hello, world" # change first line
vim.current.buffer.append("last line!")
del vim.current.buffer[3] # also works with slices
You have to always check the bounds, or you will get IndexError
s, which are very annoying.
Python inside a Vim function
This is where things become interesting. In the simple pairs, I wanted to use the <C-R>=expression
functionality, that will eval the expression
and insert the results into the buffer, from insert mode. Turns out that python expr
is not a valid Vim expression, so I had to wrap my Python functions inside Vim functions. This is the way to do it:
function! MyCoolFunction(anArg)
python << endpython
import vim
anArg = vim.eval("a:anArg")
# do important stuff
vim.command("return 1") # return from the Vim function!
endpython
endfunction
You use vim.eval
to get the values of the vim function arguments, then you use vim.command
to return from the vim function. Doing a plain Python return
will not work.
You can of course set the values of variables as well, and use them later in vim script:
python << endpython
vim.command("let l:something = 1")
endpython
if l:something == 1
return 'hi'
else
return 'bye'
endif
Miscellaneous
You can freely mix and match vim script and python code inside a vim file, as long as you use the python << endpython
markers. Each Python block is executed in the same context, so you can define your helper functions and do whatever initialization you want in a big python block in the beginning, and then just call functions and access global state as needed.
One drawback is that the tracebacks you get when you have an error don't give you line numbers (the filename is <string>
), but you can still understand what went wrong from the messages themselves.