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