Python论坛  - 讨论区

标题:[python-chinese] ANN:pyCallTips 0.7 released

2004年09月13日 星期一 09:52

tocer tootoo at yeah.net
Mon Sep 13 09:52:17 HKT 2004

这是个Vim的脚本文件。简单的说,这个脚本的功能就是在Vim中实现python程序的代码提示。但因为python是动态语言,只有执行的时候,变量类型才能够确认,所以很难做到完美的代码提示,只能尽力去做。

0.7新增:
1、更多的变量可以显示提示。
2、在Vim的“normal"模式下,如果5秒钟不按键,脚本自动刷新(F4)


脚本文件见附件。主页 http://vim.sourceforge.net/scripts/script.php?script_id=1074
-------------- next part --------------
'''
	PythonCallTips  0.7
    (C) Copyright 2004 by tocer deng   MAIL: write2tocer at homtmail.com
					
	This script simualate code calltips in a new bottow window of Vim.
    In fact, it display python help doc strings of word under the cursor
    by scanning the imported modules and functions in the current file.

    It require Python and Vim compiled with "+python" 
    and MAKE SURE "set iskeyword+=."
    
    It work well in my box in Win2000 + Vim6.3 + python2.3. 
    It can also work in GNU/Linux.
    If it can work in other platform or other version of Vim, let me know.
    Note: as I know, Vim6.2 come into conflict with Python2.3, becase Vim6.2
          is compiled with Python2.1. you can update vim version to 6.3.
          Maybe yours not.
          
	Install:
        1. putting below the sentence into your "vimrc" file.
           (in linux, '/' instead of '\\'). So once you open a python file, the 
           script begin running. (SUGGESTION)
           
           autocmd FileType python pyfile \\pyCallTips.py
           
           note: is the path of pyCallTips.py.
           
        2. Or opening a python file you requied, and execute:
             
             :pyfile  pyCallTips.py
             
        Notes: 
           1. It split a new little window in the bottom in Vim when the script
              begin running. This is the python calltips windows.
           2. DO NOT "close" the window, otherwise it can't work.
            
     Usage: coding and coding and nothing:)
            
        1. Once you type new "import xxx" or "from xxx import yyy" or other 
           evaluation statement, you MUST press "F4" key. I call it
           "refresh key". This key tell the script read the whole file again
           to get modules and methods. Otherwise the calltips don't be display.

           Note: from 0.7, the script can refresh  automatic if you don't
                 press any key until beyond 5 seconds in the "normal" mode in 
                 Vim. And the time setting is in LINE 393
            
        2. Except "F4", the script also remap five keys:  ...  as
           "auto complete key". As you know, Vim use  and  to 
           complete word automatically. But when you input word like 
           "module.method(xxx).y", you wish Vim could complete the rest part the
           word automatically. In fatc, the Vim can't.(I have think for a long 
           time, but still don't know how to do. If you know, tell me,thanks)
           So, I deside to remap other keys to complete word automatically.
           using  ...  key to select which one to complete from
           calltips window. If you still don't see, try it yourself.
           
        3. If you want to pause this script, type:
             :py CT_unmapkeys()
           Remember: DO NOT "close" the calltips window.
           
           If you want to resume this script, type:
             :py CT_mapkeys()

        4. If the keys that the script remap come into conflict with yours,
           you can change the key mapping in this file as desired.

	TODO: 
        1. Add map key to close and open calltips window.

    THANKS:
        montumba, Guilherme Salgado

    English is not my native language, so there may be many mistakes of expression.
    If you have any question, feel free to mail to write2tocer at homtmail.com
    If you enjoy it, mail me too, I'm happy to share your joy.
'''

import vim
import __builtin__
from string import letters
try:
    from sets import Set
except:
    pass

def CT_GetWordUnderCursor():
    """ Returns word under the current buffer's cursor."""
    stack = []
    leftword = rightword = popword = word = ''
    WORD = vim.eval('expand("")')  #return a big WORD

    #According '.', seperate the WORD into left part and right part
    rindex = WORD.rfind('.')
    if rindex == -1:      #if a WORD is not include '.'
        leftWORD = ''
        rightWORD = WORD
    else:
        leftWORD = WORD[:rindex]        
        rightWORD = WORD[rindex+1:]
    #print "WORD=%s, leftWORD=%s rightWORD=%s" % (WORD, leftWORD, rightWORD)

    #analyzing left part of the WORD
    for char in leftWORD:
        if char in letters + '.':
            popword += char
            continue
        elif char == '(' or char == '[':
            stack.append(popword)
            popword = ''
            continue
        elif char == ')' or char == ']':
            leftword=stack.pop()
            popword = ''
            continue
        else:
            popword = ''
    if popword != '': leftword = popword  
    #print "leftword=%s" % leftword

    #analyzing right part of the WORD
    for char in rightWORD:
        if char not in letters:
            break
        rightword += char
    #print "rightword=%s" % rightword
    
    if leftword != '':
        word = "%s.%s" % (leftword, rightword)
    else:
        word = rightword
    #print word

    return word

def CT_AutoComplete(index):
    """Return matched word in calltip window"""
    try:
        #get first field of index line of helpBuffer
        if helpBuffer[index-1].find('__builtin__') == 0:  #see if builtin function
            autoPart = helpBuffer[index-1].split()[0][len(CT_GetWordUnderCursor())+12:]  #11: len('__biultin__.')
        else:
            autoPart = helpBuffer[index-1].split()[0][len(CT_GetWordUnderCursor()):]
        vim.command('execute "normal a' + autoPart + '"')
    except:
        #print "auto complete ERROR"
        pass
    vim.command('startinsert!')   #from normal mode into insert mode
    #print autoPart

def CT_GetHelp(word):   
    """Return module or methods's  doc strings."""
    
    helpDoc = []
    spacing=16
    if len(word.split('.')) == 1:      #see if type "xxx."
        s = '__builtin__'
        m = word
    else:
        rindex= word.rfind('.')        #see if type "xxx.xx"
        s = word[:rindex]
        y, x = vim.current.window.cursor
        m = word[rindex+1:x+1]
    try:
        object = eval(s)
    except:
        return []
    joinLineFunc = lambda s: " ".join(s.split())

    methodList = [method for method in dir(object) \
                         if method.find('__') != 0 and \
                            method.find(m) == 0    and \
                            '__doc__' in dir(getattr(object, method))]
    helpDoc.extend(["%s.%s %s" %
                    (s,method.ljust(spacing),
                     joinLineFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])
    return helpDoc

def CT_ImportObject():
    """Import modules and function in the file"""            
    import tokenize
    import keyword
    import StringIO
    import pywin.debugger
    text='\n'.join(vim.current.buffer[:]) + '\n'
    f = StringIO.StringIO(text)
    g = tokenize.generate_tokens(f.readline)
    lineNo = 0
    for tokenType, t, start, end, line in g:
        if start[0] == lineNo: continue
        if tokenType == tokenize.INDENT or tokenType == tokenize.DEDENT \
                 or tokenize.tok_name[tokenType] == 'NL' \
                 or tokenType == tokenize.ERRORTOKEN \
                 or tokenType == tokenize.ENDMARKER \
                 or start[0] == lineNo: continue
        if t == 'import':
            module = ''
            while 1:
                tokenType, t, start, end, line = g.next()
                if t == ';' or tokenType == tokenize.NEWLINE:
                    try:
                        exec('import %s' % module) in globals()
                    except: 
                        print "Ignore: can't import %s\n" % module
                    break
                elif (tokenType == tokenize.OP and t == ','):
                    try:
                        exec('import %s' % module) in globals()
                    except: 
                        print "Ignore: can't import %s\n" % module
                    module = ''
                    continue
                module += t + ' '
        elif t == 'from':
            module = ''
            while 1:
                tokenType, t, start, end, line = g.next()
                if t == 'import': break
                module += t
            function = ''
            while 1:
                tokenType, t, start, end, line = g.next()
                if  t == ',':
                    try:
                        exec('from %s import %s' % (module, function)) in globals()
                    except:
                        print "Ignore: can't from %s import %s\n" % (module, function)
                    function = ''
                    continue
                elif tokenType == tokenize.NEWLINE:
                    try:
                        exec('from %s import %s' % (module, function)) in globals()
                    except: 
                        print "Ignore: can't from %s import %s\n" % (module, function)
                    break
                function += t + ' '
        
        elif t == 'class':
            i = 0
            l = 'class '
            classBlock = []
            while 1:
                tokenType, t, start, end, line = g.next()
                if tokenType == tokenize.INDENT:
                    i += 1
                    l = ' '*i*4
                elif tokenType == tokenize.DEDENT:
                    i -= 1
                    l = ' '*i*4
                    if i == 0: break
                elif tokenType == tokenize.NEWLINE or\
                         tokenType == tokenize.NL      or\
                         tokenType == tokenize.COMMENT:
                    classBlock.append(l)
                    l = ' '*i*4
                else:
                    l += t + ' '
            #print "class: %s" % classBlock[0]
            try:
                exec('\n'.join(classBlock)+'\n') in globals()
            except: pass
                #print "Ignore: can't import %s" % classBlock[0]
        elif keyword.iskeyword(t):
            lineNo = start[0]
        else:
            if t in globals():
                lineNo = start[0]
                continue
            varName = t
            tokenType, t, start, end, line = g.next()
            if t == '=':
                tokenType, t, start, end, line = g.next()
                if tokenType == tokenize.NEWLINE: break
                elif t == 'helpBuffer': pass
                #see if vars ='xxx' or "xxx" or '''xxx''' or """xxx""" or str(xxx)
                elif tokenType == tokenize.STRING or t == 'str':  
                    #print 'sting:%s' % varName
                    exec('%s = CT_STRINGTYPE' % varName)  in globals()
                #see if vars = [] or list()
                elif t == '[' or t == 'list':
                    #print 'list:%s' % varName
                    exec('%s= CT_LISTTYPE' % varName)  in globals()
                #see if vars = {} or dict()
                elif t == '{' or t == 'dict':
                    #print 'dict:%s' % varName
                    exec('%s = CT_DICTTYPE' % varName)  in globals()
                #see if vars = NUMBER
                elif tokenType == tokenize.NUMBER:
                    pass
                    #print 'number:%s' % varName
                #see if vars = Set([xxx])
                elif t == 'Set': 
                    #print 'set:%s' % varName
                    try:
                        exec('%s = CT_SETTYPE' % varName)  in globals()
                    except:
                        pass
                #see if vars = open(xxx) or file(xxx)
                elif t == 'open' or t == 'file':
                    #print 'file:%s' % varName
                    exec('%s = CT_FILETYPE' % varName)  in globals()
                else:
                    instance = t
                    #pywin.debugger.set_trace()
                    while 1:
                        tokenType, t, start, end, line = g.next()
                        if tokenType == tokenize.NAME and t == '.':
                            instance += t
                        elif tokenType == tokenize.NEWLINE: break
                        else:
                            #print 'instance: %s' % line
                            try:
                                exec('%s = %s' % (varName, instance))  in globals()
                            except: pass
                                #print 'ERROR: %s = %s' % (varName, instance)  
                            break
            lineNo = start[0]
    return 

def CT_CallTips():
    """Display help doc string in Python calltips buffer"""
    helpBuffer[:] = None
    docs = CT_GetHelp(CT_GetWordUnderCursor())

    #print docs
    for line in docs:
        helpBuffer.append(line)
    del helpBuffer[0]

    #from normal mode into insert mode
    y, x = vim.current.window.cursor
    if len(vim.current.line) > x + 1:
        vim.command('normal l')
        vim.command('startinsert')
    else:
        vim.command('startinsert!')
        
def CT_mapkeys():
    """mapping keys"""
    #mapping "a-zA-Z." keys
    for letter in letters+'.':    
        vim.command("inoremap  %s %s:python CT_CallTips()" \
                    % (letter, letter))
    #mapping "Back Space" keys
    vim.command("inoremap   :python CT_CallTips()")
    #mapping "F4" keys
    vim.command("inoremap   :python CT_ImportObject()")
    #mapping "Alt 1" key
    vim.command("inoremap   :python CT_AutoComplete(1)")
    #mapping "Alt 2" key
    vim.command("inoremap   :python CT_AutoComplete(2)")
    #mapping "Alt 3" key
    vim.command("inoremap   :python CT_AutoComplete(3)")
    #mapping "Alt 4" key
    vim.command("inoremap   :python CT_AutoComplete(4)")
    #mapping "Alt 5" key
    vim.command("inoremap   :python CT_AutoComplete(5)")

def CT_unmapkeys():
    """unmapping keys"""
    try:
        #Unmapping "a-zA-Z." keys
        for letter in letters+'.':    
            vim.command('iunmap  %s' % letter)
        #Unmapping "Back Space" keys
        vim.command('iunmap  ')
        #Unmapping "F4" keys
        vim.command('iunmap  ')
        #Unmapping "Alt 1" key
        vim.command('iunmap  ')
        #Unmapping "Alt 2" key
        vim.command('iunmap  ')
        #Unmapping "Alt 3" key
        vim.command('iunmap  ')
        #Unmapping "Alt 4" key
        vim.command('iunmap  ')
        #Unmapping "Alt 5" key
        vim.command('iunmap  ')
    except:
        pass

def CT_Init():
    """creat a new calltips windows"""

    global helpBuffer
    vim.command('silent botright new Python_CallTips')
    vim.command('set buftype=nofile')
    vim.command('set nonumber')
    vim.command('resize 5')
    vim.command('set noswapfile')
    helpBuffer = vim.current.buffer
    vim.command('wincmd p')   #switch back window
    vim.command('autocmd CursorHold *.py python CT_ImportObject()')
    vim.command('set updatetime=5000')
    from sys import path
    path.extend(['.','..'])  #add current path and parent path

def CT_Init2():
    """clear calltips buffer and unused python object"""

    ALL = ['CT_GetWordUnderCursor', 'CT_AutoComplete', 'CT_GetHelp',\
           'CT_ImportObject', 'CT_CallTips', 'CT_mapkeys', 'CT_unmapkeys',\
           'CT_Init', 'CT_Init2', '__builtin__', '__builtins__', '__doc__', \
           '__name__', 'vim', 'letters', 'helpBuffer', 'CT_STRINGTYPE', \
           'CT_LISTTYPE', 'CT_DICTTYPE', 'CT_FILETYPE', 'CT_SETTYPE', 'Set']
    helpBuffer[:] = None  #clear help buffer
    
    for object in globals().keys():
        if object not in ALL:
            try:
                exec('del %s' % object) in globals()
            except: pass
                #print 'Fail: del %s' % object
        

#VERBOSE = False
#VERBOSE = True           #inter method is displayed, such as '__str__','__repr__' etc.

CT_STRINGTYPE = str
CT_LISTTYPE   = list
CT_DICTTYPE   = dict
CT_FILETYPE   = file
CT_SETTYPE    = Set

if 'helpBuffer' in dir():
    CT_Init2()
    CT_ImportObject()
else:
    CT_Init()            
    CT_mapkeys()         
    CT_ImportObject()    

[导入自Mailman归档:http://www.zeuux.org/pipermail/zeuux-python]

如下红色区域有误,请重新填写。

    你的回复:

    请 登录 后回复。还没有在Zeuux哲思注册吗?现在 注册 !

    Zeuux © 2025

    京ICP备05028076号