1
0
mirror of https://github.com/gryf/pythonhelper.git synced 2025-12-18 12:00:27 +01:00

Version 0.7: Initial upload

This commit is contained in:
Michal Vitecek
2002-10-02 00:00:00 +00:00
committed by Able Scraper
commit 6a3cd0da42
2 changed files with 363 additions and 0 deletions

5
README Normal file
View File

@@ -0,0 +1,5 @@
This is a mirror of http://www.vim.org/scripts/script.php?script_id=435
Vim script to help moving around in larger Python source files. It displays current class, method or function the cursor is placed in in the status line for every python file. It's more clever than Yegappan Lakshmanan's taglist.vim because it takes into account indetation and comments to determine what tag the cursor is placed in and from version 0.80 doesn't need exuberant ctags utility.
Note: The script displays current tag on the status line only in NORMAL mode. This is because CursorHold event in VIM is fired up only in this mode. However if you'd like to know what tag you are in even in INSERT or VISUAL mode, contact me (email specified in the script) and I'll send you a patch that enables firing up CursorHold event in those modes as well.

358
plugin/pythonhelper.vim Normal file
View File

@@ -0,0 +1,358 @@
" File: pythonhelper.vim
" Author: Michal Vitecek <fuf-at-mageo-dot-cz>
" Version: 0.7
" Last Modified: Oct 2, 2002
"
" Overview
" --------
" Vim script to help moving around in larger Python source files. It displays
" current class, method or function the cursor is placed in in the status
" line for every python file. It's more clever than Yegappan Lakshmanan's
" taglist.vim because it takes into account indetation and comments to
" determine what tag the cursor is placed in.
"
" Requirements
" ------------
" This script needs VIM compiled with Python interpreter and relies on
" exuberant ctags utility to generate the tag listing. You can determine
" whether your VIM has Python support by issuing command :ver and looking for
" +python in the list of features.
"
" The exuberant ctags can be downloaded from http://ctags.sourceforge.net/ and
" should be reasonably new version (tested with 5.3).
"
" Note: The script doesn't display current tag on the status line only in
" NORMAL mode. This is because CursorHold event is fired up only in this mode.
" However if you badly need to know what tag you are on even in INSERT or
" VISUAL mode, contact me on the above specified email address and I'll send
" you patch that enables it.
"
" Installation
" ------------
" 1. Make sure your Vim has python feature on (+python). If not, you will need
" to recompile it with --with-pythoninterp option to the configure script
" 2. Copy script pythonhelper.vim to the $HOME/.vim/plugin directory
" 3. Edit the script and modify the location of your exuberant tags utility
" (variable CTAGS_PROGRAM).
" 4. Run Vim and open any python file.
"
python << EOS
# import of required modules {{{
import vim
import os
import popen2
import time
import sys
# }}}
# CTAGS program and parameters {{{
CTAGS_PROGRAM = "/usr/local/bin/ctags"
CTAGS_PARAMETERS = "--language-force=python --format=2 --sort=0 --fields=+nK -L - -f - "
# }}}
# global dictionaries of tags and their line numbers, keys are buffer numbers {{{
TAGS = {}
TAGLINENUMBERS = {}
BUFFERTICKS = {}
# }}}
def getNearestLineIndex(row, tagLineNumbers):
# DOC {{{
"""Returns index of line in tagLineNumbers list that is nearest to the
current cursor row.
Parameters
row -- current cursor row
tagLineNumbers -- list of tags' line numbers (ie. their position)
"""
# }}}
# CODE {{{
nearestLineNumber = -1
nearestLineIndex = -1
i = 0
for lineNumber in tagLineNumbers:
# if the current line is nearer the current cursor position, take it {{{
if (nearestLineNumber < lineNumber <= row):
nearestLineNumber = lineNumber
nearestLineIndex = i
# }}}
# if we've got past the current cursor position, let's end the search {{{
if (lineNumber >= row):
break
# }}}
i += 1
return nearestLineIndex
# }}}
def getTags(bufferNumber, changedTick):
# DOC {{{
"""Reads the tags for the specified buffer number. It does so by executing
the CTAGS program and parsing its output. Returns tuple
(taglinenumber[buffer], tags[buffer]).
Parameters
bufferNumber -- number of the current buffer
changedTick -- ever increasing number used to tell if the buffer has
been modified since the last time
"""
# }}}
# CODE {{{
global CTAGS_PROGRAM, CTAGS_PARAMETERS
global TAGLINENUMBERS, TAGS, BUFFERTICKS
# return immediately if there's no need to update the tags {{{
if ((BUFFERTICKS.has_key(bufferNumber)) and (BUFFERTICKS[bufferNumber] == changedTick)):
return (TAGLINENUMBERS[bufferNumber], TAGS[bufferNumber],)
# }}}
# read the tags and fill the global variables {{{
currentBuffer = vim.current.buffer
currentWindow = vim.current.window
row, col = currentWindow.cursor
# create a temporary file with the current content of the buffer {{{
fileName = "/tmp/.%s.%u.ph" % (os.path.basename(currentBuffer.name), os.getpid(),)
f = open(fileName, "w")
for line in currentBuffer:
f.write(line)
f.write('\n')
f.close()
# }}}
# run ctags on it {{{
try:
ctagsOutPut, ctagsInPut = popen2.popen4("%s %s" % (CTAGS_PROGRAM, CTAGS_PARAMETERS,))
ctagsInPut.write(fileName + "\n")
ctagsInPut.close()
except:
os.unlink(fileName)
return
# }}}
# parse the ctags' output {{{
tagLineNumbers = []
tags = {}
while 1:
line = ctagsOutPut.readline()
# if empty line has been read, it's the end of the file {{{
if (line == ''):
break
# }}}
# if the line starts with !, then it's a comment line {{{
if (line[0] == '!'):
continue
# }}}
# split the line into parts and parse the data {{{
# the format is: [0]tagName [1]fileName [2]tagLine [3]tagType [4]tagLineNumber [[5]tagOwner]
tagData = line.split('\t')
name = tagData[0]
# get the tag's indentation {{{
start = 2
j = 2
while ((j < len(tagData[2])) and (tagData[2][j].isspace())):
if (tagData[2][j] == '\t'):
start += 8
else:
start += 1
j += 1
# }}}
type = tagData[3]
line = int(tagData[4][5:])
if (len(tagData) == 6):
owner = tagData[5].strip()
else:
owner = None
# }}}
tagLineNumbers.append(line)
tags[line] = (name, type, owner, start)
ctagsOutPut.close()
# }}}
# clean up the now unnecessary stuff {{{
os.unlink(fileName)
# }}}
# update the global variables {{{
TAGS[bufferNumber] = tags
TAGLINENUMBERS[bufferNumber] = tagLineNumbers
BUFFERTICKS[bufferNumber] = changedTick
# }}}
# }}}
return (TAGLINENUMBERS[bufferNumber], TAGS[bufferNumber],)
# }}}
def findTag(bufferNumber, changedTick):
# DOC {{{
"""Tries to find the best tag for the current cursor position.
Parameters
bufferNumber -- number of the current buffer
changedTick -- ever increasing number used to tell if the buffer has
been modified since the last time
"""
# }}}
# CODE {{{
try:
# get the tags data for the current buffer
tagLineNumbers, tags = getTags(bufferNumber, changedTick)
# link to vim internal data {{{
currentBuffer = vim.current.buffer
currentWindow = vim.current.window
row, col = currentWindow.cursor
# }}}
# get the index of the nearest line
nearestLineIndex = getNearestLineIndex(row, tagLineNumbers)
# if any line was found, try to find if the tag is appropriate {{{
# (ie. the cursor can be below the last tag but on a code that has nothing
# to do with the tag, because it's indented differently, in such case no
# appropriate tag has been found.)
if (nearestLineIndex > -1):
nearestLineNumber = tagLineNumbers[nearestLineIndex]
# walk through all the lines in range (nearestTagLine, cursorRow) {{{
for i in xrange(nearestLineNumber + 1, row):
line = currentBuffer[i]
# count the indentation of the line, if it's lower that the tag's, the found tag is wrong {{{
if (len(line)):
# compute the indentation of the line {{{
lineStart = 0
j = 0
while ((j < len(line)) and (line[j].isspace())):
if (line[j] == '\t'):
lineStart += 8
else:
lineStart += 1
j += 1
# if the line contains only spaces, it doesn't count {{{
if (j == len(line)):
continue
# }}}
# if the next character is # (python comment), this line doesn't count {{{
if (line[j] == '#'):
continue
# }}}
# }}}
# if the line's indentation starts before the nearest tag's one, the tag is wrong {{{
if (lineStart < tags[nearestLineNumber][3]):
nearestLineNumber = -1
break
# }}}
# }}}
# }}}
else:
nearestLineNumber = -1
# }}}
# describe the cursor position (what tag it's in) {{{
tagDescription = ""
if (nearestLineNumber > -1):
tagInfo = tags[nearestLineNumber]
# use the owner if any exists {{{
if (tagInfo[2] != None):
fullTagName = "%s.%s()" % (tagInfo[2].split(':')[1], tagInfo[0],)
# }}}
# otherwise use just the tag name {{{
else:
fullTagName = tagInfo[0]
# }}}
tagDescription = "[in %s (%s)]" % (fullTagName, tagInfo[1],)
# }}}
# update the variable for the status line so it will be updated next time
vim.command("let w:PHStatusLine=\"%s\"" % (tagDescription,))
except:
# spit out debugging information {{{
ec, ei, tb = sys.exc_info()
while (tb != None):
if (tb.tb_next == None):
break
tb = tb.tb_next
print "ERROR: %s %s %s:%u" % (ec.__name__, ei, tb.tb_frame.f_code.co_filename, tb.tb_lineno,)
time.sleep(0.5)
# }}}
# }}}
def deleteTags(bufferNumber):
# DOC {{{
"""Removes tags data for the specified buffer number.
Parameters
bufferNumber -- number of the buffer
"""
# }}}
# CODE {{{
global TAGS, TAGLINENUMBERS, BUFFERTICKS
try:
del TAGS[bufferNumber]
del TAGLINENUMBERS[bufferNumber]
del BUFFERTICKS[bufferNumber]
except:
pass
# }}}
EOS
function! PHCursorHold()
" only python is supported {{{
if (exists('b:current_syntax') && (b:current_syntax != 'python'))
let w:PHStatusLine = ''
return
endif
" }}}
" call python function findTag() with the current buffer number and changed ticks
execute 'python findTag(' . expand("<abuf>") . ', ' . b:changedtick . ')'
endfunction
function! PHBufferDelete()
" call python function deleteTags() with the cur
execute 'python deleteTags(' . expand("<abuf>") . ')'
endfunction
" autocommands binding
autocmd CursorHold * silent call PHCursorHold()
autocmd BufWinEnter * silent call PHCursorHold()
autocmd BufDelete * silent call PHBufferDelete()
" time that determines after how long time of no activity the CursorHold event
" is fired up
set updatetime=1000
" color of the current tag in the status line (bold cyan on black)
highlight User1 gui=bold guifg=cyan guibg=black
" color of the modified flag in the status line (bold black on red)
highlight User2 gui=bold guifg=black guibg=red
" the status line will be displayed for every window
set laststatus=2
" set the status variable for the current window
let w:PHStatusLine = ''
" set the status line to display some useful information
set stl=%-f%r\ %2*%m%*\ \ \ \ %1*%{w:PHStatusLine}%*%=[%l:%c]\ \ \ \ [buf\ %n]