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