VIM and [Python] block jumping

C. Laurence Gonsalves clgonsal at kami.com
Fri Jul 9 07:04:51 EDT 1999


Angus MacKay wrote:
> 
> I am not looking for indentation but just code navigation.
> 
> when code uses identifiers for blocks (such as "()" in lisp, "{}" is
> most sane languages, "#if/#endif" for C preproc ...) vim can navigate
> blocks very easily (with [{/]} for "{}", [#/]# for "#if/#endif" ...)
> 
> with python this becomes harder. is there any way to do it? (I think I
> could write a function for it but I thought I'd see if it had already
> been done)

That's a neat idea. I just wrote this up, so it hasn't been tested a
lot, but it seems to work. The way I implemented this requires that you
have python support compiled into VIM. It's probably possible to
reimplement this as a pure-VIM script, but the python version is
probably orders of magnitude faster (not to mention being easier to
write). :-)

(I had once implemented the C-style [{ ]} motion keys as vim-scripts
before I realized that VIM already had built-in support for that. They
were quite slow.)

First, make a file called "vimMotion.py":

## begin vimMotion.py ##

import vim
import string
import re

# returns a tuple containing the indentation, and the line number where
we got
# that indentation. That can be different than lineNo if lineNo refers
to an
# empty (ie: only whitespace) line.
def getIndent(lineNo):
    for line in vim.current.buffer[lineNo:]:
        result = getIndentQuick(line)
        if result>=0:
            return (result,lineNo)
        lineNo = lineNo + 1
    return (0,len(vim.current.buffer)-1)
        
# grabs the initial whitespace and the stuff after it into two groups
indentRE = re.compile( r"([\t ]*)(.*)" )

# returns the indentation for a line, or -1 if it can't be determined
(ie:
# only whitespace)
def getIndentQuick(line):
    match = indentRE.match(line)
    if len(match.group(2)) == 0:
        # if the line is just whitespace, we don't know the indentation
        return -1
    else:
        # make sure we expand tabs like python does...
        return len(string.expandtabs(match.group(1)))
        
# if dir is positive, goes down, negative, goes up. Other than that, the
value
# is irrelevant
def blockMotion( dir ):
    lineNo0 = vim.current.window.cursor[0]-1
    indent0,lineNo = getIndent(lineNo0)
    newLine = lineNo0

    if indent0 == 0:
        # this is an optimization
        if dir>0:
            newLine = len(vim.current.buffer)-1
        else:
            newLine = 0
    elif dir>0:
        indent = indent0
        numLines = len(vim.current.buffer)
        while lineNo<numLines and indent >= indent0:
            lineNo = lineNo + 1
            indent,lineNo = getIndent(lineNo)
        newLine = lineNo
    else:
        indent = indent0
        lineNo = lineNo0
        while lineNo>0 and indent >= indent0:
            lineNo = lineNo - 1
            qIndent = getIndentQuick(vim.current.buffer[lineNo])
            if qIndent>=0:
                indent = qIndent
        newLine = lineNo
    # move the cursor
    vim.current.window.cursor = (newLine+1,0)

## end vimMotion.py ##

Then add the following three lines to your .vimrc:

pyfile vimMotion.py
nmap [{ :python blockMotion(-1)<CR>^
nmap ]} :python blockMotion(1)<CR>^

You should probably just execute these when editting a python file.

Note that python is a little bit funny as to where blocks can end,
because multiple blocks can all end at exactly the same position. That
means that doing ']}' can potentially (and often does) take you out of
several blocks. In a language with braces, ]} will always take you out
of one block at a time.

The other tricky part was figuring out the indentation of a line. The
two things that make this tricky are tabs (but string.expandtabs made
that pretty easy), and the fact that python seems to ignore
empty/whitespace-only lines when figuring out block levels. The "right"
thing to do seems to be to scan forward until we find a line with some
non-whitespace on it.

Let me know if you have any problems.

(note: this was posted to both comp.lang.python and comp.editors, since
people in both those groups might be interested in this)

-- 
  C. Laurence Gonsalves              "Any sufficiently advanced
  clgonsal at kami.com                   technology is indistinguishable
  http://www.cryogen.com/clgonsal/    from magic" -- Arthur C. Clarke




More information about the Python-list mailing list