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 command
vim.eval- evaluate a vim expression and return the result
vim.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 = "hello, world" # change first line vim.current.buffer.append("last line!") del vim.current.buffer # also works with slices
You have to always check the bounds, or you will get
IndexErrors, 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
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
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.