Python and vim: Two great tastes that go great together
Sean Reifschneider
tummy.com, ltd.
And now for something completely different...
Presentation Overview
10 to 15 minutes of slides and examples.
15 to 20 minutes audience-directed.
What?
Python scripting in vim.
(Not coding Python with vim)
vim compiled with "+python"
As present in:
Fedora/CentOS (vim-enhanced)
Debian/Ubuntu (vim-python)
"Macros" in vim, but with a serious language behind them
Like Emacs use of LISP (but, you know, better :-)
More powerful than "vim script"
Much more familiar to Python programmers than "vim script"
Example: Auto Indentation Settings
" indent.vimrc
autocmd BufRead * python setIndentation()
python << EOF
def setIndentation():
import vim
maxSearch = 1000 # max number of lines to search through
indentSpaces = None
cb = vim.current.buffer
indentCount = { ' ' : 0, '\t' : 0 }
justSawDefOrClassLine = 0
for i in xrange(0, min(maxSearch, len(cb))):
line = cb[i]
if not line: continue
Example: Auto Indentation Settings (cont)
#for i in xrange(0, min(maxSearch, len(cb))): [CONTINUED]
# count spaces after a class or def line
if justSawDefOrClassLine:
justSawDefOrClassLine = 0
if line[0] == ' ':
indentSpaces = 0
for c in line:
if c != ' ': break
indentSpaces = indentSpaces + 1
if line[:4] == 'def ' or line[:6] == 'class ':
justSawDefOrClassLine = 1
vim script You May Want To Use
autocmd FileType python [commands]
Run commands when this type of file is edited
python [python code]
Evaluate the python code given
python << EOF
Read python commands until a line with "EOF" is read
pyfile [filename]
Read python commands from a python file
map [keys] [command]
Run the specified command when the key(s) are pressed
Example: Auto Indentation Settings (cont)
#for i in xrange(0, min(maxSearch, len(cb))): [CONTINUED]
# add to tab versus space count
if line[0] in ' \t':
indentCount[line[0]] = indentCount.get(line[0], 0) + 1
# more lines started with space
if indentCount[' '] > indentCount['\t']:
vim.command('set smarttab tabstop=8 expandtab')
if indentSpaces:
vim.command('set ts=%d sw=%d' % ( indentSpaces, indentSpaces ))
# more lines started with tab
else:
vim.command('set softtabstop=3 ts=3 sw=3')
EOF
Python+vim Code Scraps
import vim
Load the vim Python module
vim.current.line
The current line in the editor
vim.current.buffer
A list-like object of all lines in the buffer
vim.current.buffer
A list-like object of all lines in the buffer
lineno, col = vim.current.window.cursor
Get location of cursor
vim.current.window.cursor = lineno, col
Change location of cursor
vim.command(str(Command))
Run a vim command (like typing ":Command" in vim)
vim.eval(str(vimExpression))
Return the value of the vim expression
tabSize = vim.eval('&ts')
vim.error
Exception raised when the vim module has problems
Example: DNS Serial Update
" dns.vimrc
autocmd FileType dns map S :python updateDnsSerial()^M
python << EOF
def updateDnsSerial():
import re, time, vim, string
maxSearch = 20
cb = vim.current.buffer
foundSoa = 0
for i in xrange(0, min(maxSearch, len(cb))):
line = cb[i]
if foundSoa:
# look for serial
rx = re.match(r'^\s*(\d+).*', line)
if not rx:
print 'Unable to find Serial'
return
serial = rx.group(1)
Example: DNS Serial Update (cont)
#for i in xrange(0, min(maxSearch, len(cb))): [CONTINUED]
# generate new serial
now = time.time()
today = time.strftime('%Y%m%d00', time.localtime(now))
todayVal = long(today)
serialVal = long(serial)
if todayVal <= serialVal: todayVal = serialVal + 1
# update serial
cb[i] = string.replace(line, serial, '%d' % todayVal)
# display update string
print 'Updated serial from "%s" to "%d"' % ( serial, todayVal )
break
if re.match(r'^@\s+IN\s+SOA\s+', line): foundSoa = 1
EOF
vim Buffer Use
lines = vim.current.buffer[1] = str(newLine)
Replace a line
lines = vim.current.buffer[10:11] = [str(newLine1), newLine2]
Replace many lines
del(lines[10])
Delete a line
del(lines[10:20])
Delete 10 lines
Example: Python Block Motion
" pyblock.vim
map ( :python pythonblockFind(forward = 0)^M
map ) :python pythonblockFind()^M
python << EOF
def countIndent(line):
i = 0
for s in line:
if s != ' ' and s != '\t': return(i)
i = i + 1
return(0)
def isEmptyLine(line):
return(not line.strip())
Example: Python Block Motion (cont)
def pythonblockNonblank(lineno, forward = 1):
import vim
cb = vim.current.buffer
col = vim.current.window.cursor[1]
end, increment = ( 0, -1 )
if forward == 1: end, increment = ( len(cb), 1 )
for i in xrange(lineno, end, increment):
cline = cb[i - 1]
if not isEmptyLine(cline):
if i >= len(cb): i = len(cb) - 1
if i < 1: i = 1
vim.current.window.cursor = ( i, col )
return()
Example: Python Block Motion (cont)
def pythonblockFind(forward = 1):
import vim
cb = vim.current.buffer
lineno, col = vim.current.window.cursor
cline = cb[lineno - 1]
cIndent = countIndent(cline)
end, increment = ( 0, -1 )
if forward == 1: end, increment = ( len(cb), 1 )
for i in xrange(lineno, end, increment):
cline = cb[i - 1]
if isEmptyLine(cline): continue
if countIndent(cline) < cIndent:
if forward == 1:
return(pythonblockNonblank(i - 1, not forward))
return(pythonblockNonblank(i + 1, not forward))
if forward == 1: vim.current.window.cursor = ( len(cb) - 1, col )
else: vim.current.window.cursor = ( 1, col )
EOF
HOWTO Get Started
Not very well documented outside of vim.
In vim: help python
Look at other python scripts at vim.org