diff options
author | Ian Thompson <quornian@googlemail.com> | 2008-07-15 11:34:46 +0400 |
---|---|---|
committer | Ian Thompson <quornian@googlemail.com> | 2008-07-15 11:34:46 +0400 |
commit | aeb4d0c631537d93de084301cac6a5dc981b6655 (patch) | |
tree | c82ff43f2732cac35447b657b8adc9306261b74f /release/scripts/textplugin_suggest.py | |
parent | dbb61988fdaa6085912dee6ab8f5569e63ef88fb (diff) |
Created a BPy module BPyTextPlugin to centralize functions used across the text plugin scripts. Also created two more scripts to handle imports and member suggestions.
Diffstat (limited to 'release/scripts/textplugin_suggest.py')
-rw-r--r-- | release/scripts/textplugin_suggest.py | 327 |
1 files changed, 44 insertions, 283 deletions
diff --git a/release/scripts/textplugin_suggest.py b/release/scripts/textplugin_suggest.py index c4fce9ad7a9..8e14dffca9c 100644 --- a/release/scripts/textplugin_suggest.py +++ b/release/scripts/textplugin_suggest.py @@ -1,301 +1,62 @@ #!BPY """ -Name: 'Suggest' -Blender: 243 +Name: 'Suggest All' +Blender: 246 Group: 'TextPlugin' Shortcut: 'Ctrl+Space' -Tooltip: 'Suggests completions for the word at the cursor in a python script' +Tooltip: 'Performs suggestions based on the context of the cursor' """ -import bpy, __builtin__, token -from Blender import Text -from StringIO import StringIO -from inspect import * -from tokenize import generate_tokens +# Only run if we have the required modules +try: + import bpy + from BPyTextPlugin import * + OK = True +except: + OK = False -TK_TYPE = 0 -TK_TOKEN = 1 -TK_START = 2 #(srow, scol) -TK_END = 3 #(erow, ecol) -TK_LINE = 4 -TK_ROW = 0 -TK_COL = 1 - -execs = [] # Used to establish the same import context across defs - -keywords = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global', - 'or', 'with', 'assert', 'else', 'if', 'pass', 'yield', - 'break', 'except', 'import', 'print', 'class', 'exec', 'in', - 'raise', 'continue', 'finally', 'is', 'return', 'def', 'for', - 'lambda', 'try' ] - - -def getBuiltins(): - builtins = [] - bi = dir(__builtin__) - for k in bi: - v = getattr(__builtin__, k) - if ismodule(v): t='m' - elif callable(v): t='f' - else: t='v' - builtins.append((k, t)) - return builtins - - -def getKeywords(): - global keywords - return [(k, 'k') for k in keywords] - - -def getTokens(txt): - txt.reset() - g = generate_tokens(txt.readline) - tokens = [] - for t in g: tokens.append(t) - return tokens - - -def isNameChar(s): - return (s.isalnum() or s == '_') - - -# Returns words preceding the cursor that are separated by periods as a list in -# the same order -def getCompletionSymbols(txt): - (l, c)= txt.getCursorPos() - lines = txt.asLines() - line = lines[l] - a=0 - for a in range(1, c+1): - if not isNameChar(line[c-a]) and line[c-a]!='.': - a -= 1 - break - return line[c-a:c].split('.') - - -def getImports(txt): - imports = [] - - # Unfortunately, tokenize may fail if the script leaves brackets or strings - # open. For now we return an empty list until I have a better idea. Maybe - # parse manually. - try: - tokens = getTokens(txt) - except: - return [] - - for i in range(1, len(tokens)): - - # Handle all import statements - if tokens[i-1][TK_TOKEN] == 'import': - - # Find 'from' if it exists - fr = -1 - for a in range(1, i): - if tokens[i-a][TK_TYPE] == token.NEWLINE: break - if tokens[i-a][TK_TOKEN] == 'from': - fr = i-a - break - - # Handle: import ___[.___][,___[.___]] - if fr<0: - parent = '' - - # Handle: from ___[.___] import ___[,___] - else: # fr>=0: - parent = ''.join([t[TK_TOKEN] for t in tokens[fr+1:i-1]]) - - module = '' - while i < len(tokens)-1: - if tokens[i][TK_TYPE] == token.NAME: - - # Get the module name - module = module + tokens[i][TK_TOKEN] - - if tokens[i+1][TK_TOKEN] == '.': - module += '.' - i += 1 - else: - # Add the module name and parent to the dict - imports.append((module, parent)) - module = '' - - elif tokens[i][TK_TOKEN]!=',': - break - - i += 1 - - # Process imports for: from ___ import * - for imp,frm in imports: - print imp, frm - if frm == '': - try: __import__(imp) - except: print '^ERR^' - else: - try: __import__(frm, globals(), locals(), [imp]) - except: print '^ERR^' - - - -# Returns a list of tuples of symbol names and their types (name, type) where -# type is one of: -# m (module/class) Has its own members (includes classes) -# v (variable) Has a type which may have its own members -# f (function) Callable and may have a return type (with its own members) -# It also updates the global import context (via execs) -def getGlobals(txt): - global execs - - # Unfortunately, tokenize may fail if the script leaves brackets or strings - # open. For now we return an empty list, leaving builtins and keywords as - # the only globals. (on the TODO list) - try: - tokens = getTokens(txt) - except: - return [] - - globals = dict() - for i in range(len(tokens)): - - # Handle all import statements - if i>=1 and tokens[i-1][TK_TOKEN]=='import': - - # Find 'from' if it exists - fr= -1 - for a in range(1, i): - if tokens[i-a][TK_TYPE]==token.NEWLINE: break - if tokens[i-a][TK_TOKEN]=='from': - fr=i-a - break - - # Handle: import ___[,___] - if fr<0: - - while True: - if tokens[i][TK_TYPE]==token.NAME: - # Add the import to the execs list - x = tokens[i][TK_LINE].strip() - k = tokens[i][TK_TOKEN] - execs.append(x) - exec 'try: '+x+'\nexcept: pass' - - # Add the symbol name to the return list - globals[k] = 'm' - elif tokens[i][TK_TOKEN]!=',': - break - i += 1 - - # Handle statement: from ___[.___] import ___[,___] - else: # fr>=0: - - # Add the import to the execs list - x = tokens[i][TK_LINE].strip() - execs.append(x) - exec 'try: '+x+'\nexcept: pass' - - # Import parent module so we can process it for sub modules - parent = ''.join([t[TK_TOKEN] for t in tokens[fr+1:i-1]]) - exec 'try: import '+parent+'\nexcept: pass' - - # All submodules, functions, etc. - if tokens[i][TK_TOKEN]=='*': - - # Add each symbol name to the return list - d = eval(parent).__dict__.items() - for k,v in d: - if not globals.has_key(k) or not globals[k]: - t='v' - if ismodule(v): t='m' - elif callable(v): t='f' - globals[k] = t - - # Specific function, submodule, etc. - else: - while True: - if tokens[i][TK_TYPE]==token.NAME: - k = tokens[i][TK_TOKEN] - if not globals.has_key(k) or not globals[k]: - t='v' - try: - v = eval(parent+'.'+k) - if ismodule(v): t='m' - elif callable(v): t='f' - except: pass - globals[k] = t - elif tokens[i][TK_TOKEN]!=',': - break - i += 1 - - elif tokens[i][TK_TYPE]==token.NAME and tokens[i][TK_TOKEN] not in keywords and (i==0 or tokens[i-1][TK_TOKEN]!='.'): - k = tokens[i][TK_TOKEN] - if not globals.has_key(k) or not globals[k]: - t=None - if (i>0 and tokens[i-1][TK_TOKEN]=='def'): - t='f' - else: - t='v' - globals[k] = t - - return globals.items() - - -def globalSuggest(txt, cs): - globals = getGlobals(txt) - return globals - - -# Only works for 'static' members (eg. Text.Get) -def memberSuggest(txt, cs): - global execs +def main(): + txt = bpy.data.texts.active + (line, c) = current_line(txt) - # Populate the execs for imports - getGlobals(txt) + # Check we are in a normal context + if get_context(line, c) != NORMAL: + return - # Sometimes we have conditional includes which will fail if the module - # cannot be found. So we protect outselves in a try block - for x in execs: - exec 'try: '+x+'\nexcept: pass' + # Check that which precedes the cursor and perform the following: + # Period(.) - Run textplugin_membersuggest.py + # 'import' or 'from' - Run textplugin_imports.py + # Other - Continue this script (global suggest) + pre = get_targets(line, c) - suggestions = dict() - (row, col) = txt.getCursorPos() + count = len(pre) - sub = cs[len(cs)-1] + if count > 1: # Period found + import textplugin_membersuggest + textplugin_membersuggest.main() + return + # Look for 'import' or 'from' + elif line.rfind('import ', 0, c) == c-7 or line.rfind('from ', 0, c) == c-5: + import textplugin_imports + textplugin_imports.main() + return - m=None - pre='.'.join(cs[:-1]) - try: - m = eval(pre) - except: - print pre+ ' not found or not imported.' + list = [] - if m!=None: - for k,v in m.__dict__.items(): - if ismodule(v): t='m' - elif callable(v): t='f' - else: t='v' - suggestions[k] = t + for k in KEYWORDS: + list.append((k, 'k')) - return suggestions.items() - - -def cmp0(x, y): - return cmp(x[0], y[0]) - - -def main(): - txt = bpy.data.texts.active - if txt==None: return + for k, v in get_builtins().items(): + list.append((k, type_char(v))) - cs = getCompletionSymbols(txt) + for k, v in get_imports(txt).items(): + list.append((k, type_char(v))) - if len(cs)<=1: - l = globalSuggest(txt, cs) - l.extend(getBuiltins()) - l.extend(getKeywords()) - else: - l = memberSuggest(txt, cs) + for k, v in get_defs(txt).items(): + list.append((k, 'f')) - l.sort(cmp=cmp0) - txt.suggest(l, cs[len(cs)-1]) + list.sort(cmp = suggest_cmp) + txt.suggest(list, pre[-1]) -main() +if OK: + main() |