Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

VIM and [Python] block jumping

2 views
Skip to first unread message

C. Laurence Gonsalves

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
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
clgo...@kami.com technology is indistinguishable
http://www.cryogen.com/clgonsal/ from magic" -- Arthur C. Clarke

C. Laurence Gonsalves

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
Of course, right after I posted that, realized that there's a problem.
The blockMotion function doesn't account for lines that are
continuations of other lines. That means that triple-quoted string
literals, lines with more {[('s that }])'s, and lines joined with \ can
screw it up. Are there any other situations? I'll try to post a
corrected version later. Some of those problems are pretty nasty though
(triple-quoted strings in particular).

Neel Krishnaswami

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
In article <3785DA06...@kami.com>,

C. Laurence Gonsalves <clgo...@kami.com> wrote:
>Of course, right after I posted that, realized that there's a problem.
>The blockMotion function doesn't account for lines that are
>continuations of other lines. That means that triple-quoted string
>literals, lines with more {[('s that }])'s, and lines joined with \ can
>screw it up. Are there any other situations? I'll try to post a
>corrected version later. Some of those problems are pretty nasty though
>(triple-quoted strings in particular).

Being a faithful Emacs-ite, I've never used vim. But since you can
script vim with Python, it might be possible to use the tokenizer in
the standard library (module tokenize) to handle this cleanly. Likely
you'd need to extend it a bit to handle the syntax errors that show up
in half-written code gracefully, but taking vim that one step past
syntax-awareness and into structure-awareness would be a very slick
hack.


Neel

C. Laurence Gonsalves

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to

## begin vimMotion.py ##

## end vimMotion.py ##

--

C. Laurence Gonsalves

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
Of course, right after I posted that, realized that there's a problem.
The blockMotion function doesn't account for lines that are
continuations of other lines. That means that triple-quoted string
literals, lines with more {[('s that }])'s, and lines joined with \ can
screw it up. Are there any other situations? I'll try to post a
corrected version later. Some of those problems are pretty nasty though
(triple-quoted strings in particular).

--

Neel Krishnaswami

unread,
Jul 9, 1999, 3:00:00 AM7/9/99
to
In article <3785DA06...@kami.com>,
C. Laurence Gonsalves <clgo...@kami.com> wrote:
>Of course, right after I posted that, realized that there's a problem.
>The blockMotion function doesn't account for lines that are
>continuations of other lines. That means that triple-quoted string
>literals, lines with more {[('s that }])'s, and lines joined with \ can
>screw it up. Are there any other situations? I'll try to post a
>corrected version later. Some of those problems are pretty nasty though
>(triple-quoted strings in particular).

Being a faithful Emacs-ite, I've never used vim. But since you can

0 new messages