Improvements to the base BPyTextPlugin module:
- Added a centralized function for resolving targets (aaa.bbb.ccc) - Added documentation support for locally defined classes and methods - The time taken to parse now dictates how long to use the cache before parsing again - Other tweaks and comments and support for numeric var types The text plugin scripts have been updated to make use of these features.
This commit is contained in:
@@ -1,8 +1,34 @@
|
|||||||
|
"""The BPyTextPlugin Module
|
||||||
|
|
||||||
|
Use get_cached_descriptor(txt) to retrieve information about the script held in
|
||||||
|
the txt Text object.
|
||||||
|
|
||||||
|
Use print_cache_for(txt) to print the information to the console.
|
||||||
|
|
||||||
|
Use line, cursor = current_line(txt) to get the logical line and cursor position
|
||||||
|
|
||||||
|
Use get_targets(line, cursor) to find out what precedes the cursor:
|
||||||
|
aaa.bbb.cc|c.ddd -> ['aaa', 'bbb', 'cc']
|
||||||
|
|
||||||
|
Use resolve_targets(txt, targets) to turn a target list into a usable object if
|
||||||
|
one is found to match.
|
||||||
|
"""
|
||||||
|
|
||||||
import bpy, sys, os
|
import bpy, sys, os
|
||||||
import __builtin__, tokenize
|
import __builtin__, tokenize
|
||||||
from Blender.sys import time
|
from Blender.sys import time
|
||||||
from tokenize import generate_tokens, TokenError, \
|
from tokenize import generate_tokens, TokenError, \
|
||||||
COMMENT, DEDENT, INDENT, NAME, NEWLINE, NL, STRING
|
COMMENT, DEDENT, INDENT, NAME, NEWLINE, NL, STRING, NUMBER
|
||||||
|
|
||||||
|
class Definition():
|
||||||
|
"""Describes a definition or defined object through its name, line number
|
||||||
|
and docstring. This is the base class for definition based descriptors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, lineno, doc=''):
|
||||||
|
self.name = name
|
||||||
|
self.lineno = lineno
|
||||||
|
self.doc = doc
|
||||||
|
|
||||||
class ScriptDesc():
|
class ScriptDesc():
|
||||||
"""Describes a script through lists of further descriptor objects (classes,
|
"""Describes a script through lists of further descriptor objects (classes,
|
||||||
@@ -19,43 +45,40 @@ class ScriptDesc():
|
|||||||
self.defs = defs
|
self.defs = defs
|
||||||
self.vars = vars
|
self.vars = vars
|
||||||
self.incomplete = incomplete
|
self.incomplete = incomplete
|
||||||
self.time = 0
|
self.parse_due = 0
|
||||||
|
|
||||||
def set_time(self):
|
def set_delay(self, delay):
|
||||||
self.time = time()
|
self.parse_due = time() + delay
|
||||||
|
|
||||||
class ClassDesc():
|
class ClassDesc(Definition):
|
||||||
"""Describes a class through lists of further descriptor objects (defs and
|
"""Describes a class through lists of further descriptor objects (defs and
|
||||||
vars). The name of the class is held by the name field and the line on
|
vars). The name of the class is held by the name field and the line on
|
||||||
which it is defined is held in lineno.
|
which it is defined is held in lineno.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, defs, vars, lineno):
|
def __init__(self, name, defs, vars, lineno, doc=''):
|
||||||
self.name = name
|
Definition.__init__(self, name, lineno, doc)
|
||||||
self.defs = defs
|
self.defs = defs
|
||||||
self.vars = vars
|
self.vars = vars
|
||||||
self.lineno = lineno
|
|
||||||
|
|
||||||
class FunctionDesc():
|
class FunctionDesc(Definition):
|
||||||
"""Describes a function through its name and list of parameters (name,
|
"""Describes a function through its name and list of parameters (name,
|
||||||
params) and the line on which it is defined (lineno).
|
params) and the line on which it is defined (lineno).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, params, lineno):
|
def __init__(self, name, params, lineno, doc=''):
|
||||||
self.name = name
|
Definition.__init__(self, name, lineno, doc)
|
||||||
self.params = params
|
self.params = params
|
||||||
self.lineno = lineno
|
|
||||||
|
|
||||||
class VarDesc():
|
class VarDesc(Definition):
|
||||||
"""Describes a variable through its name and type (if ascertainable) and the
|
"""Describes a variable through its name and type (if ascertainable) and the
|
||||||
line on which it is defined (lineno). If no type can be determined, type
|
line on which it is defined (lineno). If no type can be determined, type
|
||||||
will equal None.
|
will equal None.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, name, type, lineno):
|
def __init__(self, name, type, lineno):
|
||||||
self.name = name
|
Definition.__init__(self, name, lineno)
|
||||||
self.type = type # None for unknown (supports: dict/list/str)
|
self.type = type # None for unknown (supports: dict/list/str)
|
||||||
self.lineno = lineno
|
|
||||||
|
|
||||||
# Context types
|
# Context types
|
||||||
CTX_UNSET = -1
|
CTX_UNSET = -1
|
||||||
@@ -64,9 +87,6 @@ CTX_SINGLE_QUOTE = 1
|
|||||||
CTX_DOUBLE_QUOTE = 2
|
CTX_DOUBLE_QUOTE = 2
|
||||||
CTX_COMMENT = 3
|
CTX_COMMENT = 3
|
||||||
|
|
||||||
# Special time period constants
|
|
||||||
TP_AUTO = -1
|
|
||||||
|
|
||||||
# Python keywords
|
# Python keywords
|
||||||
KEYWORDS = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global',
|
KEYWORDS = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global',
|
||||||
'or', 'with', 'assert', 'else', 'if', 'pass', 'yield',
|
'or', 'with', 'assert', 'else', 'if', 'pass', 'yield',
|
||||||
@@ -104,8 +124,76 @@ def _load_module_names():
|
|||||||
|
|
||||||
_load_module_names()
|
_load_module_names()
|
||||||
|
|
||||||
|
def _trim_doc(doc):
|
||||||
|
"""Trims the quotes from a quoted STRING token (eg. "'''text'''" -> "text")
|
||||||
|
"""
|
||||||
|
|
||||||
|
l = len(doc)
|
||||||
|
i = 0
|
||||||
|
while i < l/2 and (doc[i] == "'" or doc[i] == '"'):
|
||||||
|
i += 1
|
||||||
|
return doc[i:-i]
|
||||||
|
|
||||||
def get_cached_descriptor(txt, period=TP_AUTO):
|
def resolve_targets(txt, targets):
|
||||||
|
"""Attempts to return a useful object for the locally or externally defined
|
||||||
|
entity described by targets. If the object is local (defined in txt), a
|
||||||
|
Definition instance is returned. If the object is external (imported or
|
||||||
|
built in), the object itself is returned. If no object can be found, None is
|
||||||
|
returned.
|
||||||
|
"""
|
||||||
|
|
||||||
|
count = len(targets)
|
||||||
|
if count==0: return None
|
||||||
|
|
||||||
|
obj = None
|
||||||
|
local = None
|
||||||
|
i = 1
|
||||||
|
|
||||||
|
desc = get_cached_descriptor(txt)
|
||||||
|
if desc.classes.has_key(targets[0]):
|
||||||
|
local = desc.classes[targets[0]]
|
||||||
|
elif desc.defs.has_key(targets[0]):
|
||||||
|
local = desc.defs[targets[0]]
|
||||||
|
elif desc.vars.has_key(targets[0]):
|
||||||
|
obj = desc.vars[targets[0]].type
|
||||||
|
|
||||||
|
if local:
|
||||||
|
while i < count:
|
||||||
|
if hasattr(local, 'classes') and local.classes.has_key(targets[i]):
|
||||||
|
local = local.classes[targets[i]]
|
||||||
|
elif hasattr(local, 'defs') and local.defs.has_key(targets[i]):
|
||||||
|
local = local.defs[targets[i]]
|
||||||
|
elif hasattr(local, 'vars') and local.vars.has_key(targets[i]):
|
||||||
|
obj = local.vars[targets[i]].type
|
||||||
|
local = None
|
||||||
|
i += 1
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
local = None
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if local: return local
|
||||||
|
|
||||||
|
if not obj:
|
||||||
|
if desc.imports.has_key(targets[0]):
|
||||||
|
obj = desc.imports[targets[0]]
|
||||||
|
else:
|
||||||
|
builtins = get_builtins()
|
||||||
|
if builtins.has_key(targets[0]):
|
||||||
|
obj = builtins[targets[0]]
|
||||||
|
|
||||||
|
while obj and i < count:
|
||||||
|
if hasattr(obj, targets[i]):
|
||||||
|
obj = getattr(obj, targets[i])
|
||||||
|
else:
|
||||||
|
obj = None
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def get_cached_descriptor(txt, force_parse=0):
|
||||||
"""Returns the cached ScriptDesc for the specified Text object 'txt'. If the
|
"""Returns the cached ScriptDesc for the specified Text object 'txt'. If the
|
||||||
script has not been parsed in the last 'period' seconds it will be reparsed
|
script has not been parsed in the last 'period' seconds it will be reparsed
|
||||||
to obtain this descriptor.
|
to obtain this descriptor.
|
||||||
@@ -116,20 +204,11 @@ def get_cached_descriptor(txt, period=TP_AUTO):
|
|||||||
|
|
||||||
global _parse_cache
|
global _parse_cache
|
||||||
|
|
||||||
if period == TP_AUTO:
|
|
||||||
m = txt.nlines
|
|
||||||
r = 1
|
|
||||||
while True:
|
|
||||||
m = m >> 2
|
|
||||||
if not m: break
|
|
||||||
r = r << 1
|
|
||||||
period = r
|
|
||||||
|
|
||||||
parse = True
|
parse = True
|
||||||
key = hash(txt)
|
key = hash(txt)
|
||||||
if _parse_cache.has_key(key):
|
if not force_parse and _parse_cache.has_key(key):
|
||||||
desc = _parse_cache[key]
|
desc = _parse_cache[key]
|
||||||
if desc.time >= time() - period:
|
if desc.parse_due > time():
|
||||||
parse = desc.incomplete
|
parse = desc.incomplete
|
||||||
|
|
||||||
if parse:
|
if parse:
|
||||||
@@ -147,6 +226,7 @@ def parse_text(txt):
|
|||||||
flag set and information processed up to this point will still be accessible.
|
flag set and information processed up to this point will still be accessible.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
start_time = time()
|
||||||
txt.reset()
|
txt.reset()
|
||||||
tokens = generate_tokens(txt.readline) # Throws TokenError
|
tokens = generate_tokens(txt.readline) # Throws TokenError
|
||||||
|
|
||||||
@@ -171,12 +251,12 @@ def parse_text(txt):
|
|||||||
|
|
||||||
indent = 0
|
indent = 0
|
||||||
prev_type = -1
|
prev_type = -1
|
||||||
prev_string = ''
|
prev_text = ''
|
||||||
incomplete = False
|
incomplete = False
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
type, string, start, end, line = tokens.next()
|
type, text, start, end, line = tokens.next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
break
|
break
|
||||||
except TokenError, IndentationError:
|
except TokenError, IndentationError:
|
||||||
@@ -204,33 +284,33 @@ def parse_text(txt):
|
|||||||
|
|
||||||
# Default, look for 'from' or 'import' to start
|
# Default, look for 'from' or 'import' to start
|
||||||
if imp_step == 0:
|
if imp_step == 0:
|
||||||
if string == 'from':
|
if text == 'from':
|
||||||
imp_tmp = []
|
imp_tmp = []
|
||||||
imp_step = 1
|
imp_step = 1
|
||||||
elif string == 'import':
|
elif text == 'import':
|
||||||
imp_from = None
|
imp_from = None
|
||||||
imp_tmp = []
|
imp_tmp = []
|
||||||
imp_step = 2
|
imp_step = 2
|
||||||
|
|
||||||
# Found a 'from', create imp_from in form '???.???...'
|
# Found a 'from', create imp_from in form '???.???...'
|
||||||
elif imp_step == 1:
|
elif imp_step == 1:
|
||||||
if string == 'import':
|
if text == 'import':
|
||||||
imp_from = '.'.join(imp_tmp)
|
imp_from = '.'.join(imp_tmp)
|
||||||
imp_tmp = []
|
imp_tmp = []
|
||||||
imp_step = 2
|
imp_step = 2
|
||||||
elif type == NAME:
|
elif type == NAME:
|
||||||
imp_tmp.append(string)
|
imp_tmp.append(text)
|
||||||
elif string != '.':
|
elif text != '.':
|
||||||
imp_step = 0 # Invalid syntax
|
imp_step = 0 # Invalid syntax
|
||||||
|
|
||||||
# Found 'import', imp_from is populated or None, create imp_name
|
# Found 'import', imp_from is populated or None, create imp_name
|
||||||
elif imp_step == 2:
|
elif imp_step == 2:
|
||||||
if string == 'as':
|
if text == 'as':
|
||||||
imp_name = '.'.join(imp_tmp)
|
imp_name = '.'.join(imp_tmp)
|
||||||
imp_step = 3
|
imp_step = 3
|
||||||
elif type == NAME or string == '*':
|
elif type == NAME or text == '*':
|
||||||
imp_tmp.append(string)
|
imp_tmp.append(text)
|
||||||
elif string != '.':
|
elif text != '.':
|
||||||
imp_name = '.'.join(imp_tmp)
|
imp_name = '.'.join(imp_tmp)
|
||||||
imp_symb = imp_name
|
imp_symb = imp_name
|
||||||
imp_store = True
|
imp_store = True
|
||||||
@@ -238,7 +318,7 @@ def parse_text(txt):
|
|||||||
# Found 'as', change imp_symb to this value and go back to step 2
|
# Found 'as', change imp_symb to this value and go back to step 2
|
||||||
elif imp_step == 3:
|
elif imp_step == 3:
|
||||||
if type == NAME:
|
if type == NAME:
|
||||||
imp_symb = string
|
imp_symb = text
|
||||||
else:
|
else:
|
||||||
imp_store = True
|
imp_store = True
|
||||||
|
|
||||||
@@ -268,7 +348,7 @@ def parse_text(txt):
|
|||||||
imports[imp_symb] = module
|
imports[imp_symb] = module
|
||||||
|
|
||||||
# More to import from the same module?
|
# More to import from the same module?
|
||||||
if string == ',':
|
if text == ',':
|
||||||
imp_tmp = []
|
imp_tmp = []
|
||||||
imp_step = 2
|
imp_step = 2
|
||||||
else:
|
else:
|
||||||
@@ -283,7 +363,7 @@ def parse_text(txt):
|
|||||||
|
|
||||||
# Look for 'class'
|
# Look for 'class'
|
||||||
if cls_step == 0:
|
if cls_step == 0:
|
||||||
if string == 'class':
|
if text == 'class':
|
||||||
cls_name = None
|
cls_name = None
|
||||||
cls_lineno = start[0]
|
cls_lineno = start[0]
|
||||||
cls_indent = indent
|
cls_indent = indent
|
||||||
@@ -293,30 +373,32 @@ def parse_text(txt):
|
|||||||
elif cls_step == 1:
|
elif cls_step == 1:
|
||||||
if not cls_name:
|
if not cls_name:
|
||||||
if type == NAME:
|
if type == NAME:
|
||||||
cls_name = string
|
cls_name = text
|
||||||
cls_sline = False
|
cls_sline = False
|
||||||
cls_defs = dict()
|
cls_defs = dict()
|
||||||
cls_vars = dict()
|
cls_vars = dict()
|
||||||
elif string == ':':
|
elif text == ':':
|
||||||
cls_step = 2
|
cls_step = 2
|
||||||
|
|
||||||
# Found 'class' name ... ':', now check if it's a single line statement
|
# Found 'class' name ... ':', now check if it's a single line statement
|
||||||
elif cls_step == 2:
|
elif cls_step == 2:
|
||||||
if type == NEWLINE:
|
if type == NEWLINE:
|
||||||
cls_sline = False
|
cls_sline = False
|
||||||
cls_step = 3
|
|
||||||
else:
|
else:
|
||||||
cls_sline = True
|
cls_sline = True
|
||||||
cls_step = 3
|
cls_doc = ''
|
||||||
|
cls_step = 3
|
||||||
|
|
||||||
elif cls_step == 3:
|
elif cls_step == 3:
|
||||||
|
if not cls_doc and type == STRING:
|
||||||
|
cls_doc = _trim_doc(text)
|
||||||
if cls_sline:
|
if cls_sline:
|
||||||
if type == NEWLINE:
|
if type == NEWLINE:
|
||||||
classes[cls_name] = ClassDesc(cls_name, cls_defs, cls_vars, cls_lineno)
|
classes[cls_name] = ClassDesc(cls_name, cls_defs, cls_vars, cls_lineno, cls_doc)
|
||||||
cls_step = 0
|
cls_step = 0
|
||||||
else:
|
else:
|
||||||
if type == DEDENT and indent <= cls_indent:
|
if type == DEDENT and indent <= cls_indent:
|
||||||
classes[cls_name] = ClassDesc(cls_name, cls_defs, cls_vars, cls_lineno)
|
classes[cls_name] = ClassDesc(cls_name, cls_defs, cls_vars, cls_lineno, cls_doc)
|
||||||
cls_step = 0
|
cls_step = 0
|
||||||
|
|
||||||
#################
|
#################
|
||||||
@@ -325,7 +407,7 @@ def parse_text(txt):
|
|||||||
|
|
||||||
# Look for 'def'
|
# Look for 'def'
|
||||||
if def_step == 0:
|
if def_step == 0:
|
||||||
if string == 'def':
|
if text == 'def':
|
||||||
def_name = None
|
def_name = None
|
||||||
def_lineno = start[0]
|
def_lineno = start[0]
|
||||||
def_step = 1
|
def_step = 1
|
||||||
@@ -333,21 +415,43 @@ def parse_text(txt):
|
|||||||
# Found 'def', look for def_name followed by '('
|
# Found 'def', look for def_name followed by '('
|
||||||
elif def_step == 1:
|
elif def_step == 1:
|
||||||
if type == NAME:
|
if type == NAME:
|
||||||
def_name = string
|
def_name = text
|
||||||
def_params = []
|
def_params = []
|
||||||
elif def_name and string == '(':
|
elif def_name and text == '(':
|
||||||
def_step = 2
|
def_step = 2
|
||||||
|
|
||||||
# Found 'def' name '(', now identify the parameters upto ')'
|
# Found 'def' name '(', now identify the parameters upto ')'
|
||||||
# TODO: Handle ellipsis '...'
|
# TODO: Handle ellipsis '...'
|
||||||
elif def_step == 2:
|
elif def_step == 2:
|
||||||
if type == NAME:
|
if type == NAME:
|
||||||
def_params.append(string)
|
def_params.append(text)
|
||||||
elif string == ')':
|
elif text == ':':
|
||||||
|
def_step = 3
|
||||||
|
|
||||||
|
# Found 'def' ... ':', now check if it's a single line statement
|
||||||
|
elif def_step == 3:
|
||||||
|
if type == NEWLINE:
|
||||||
|
def_sline = False
|
||||||
|
else:
|
||||||
|
def_sline = True
|
||||||
|
def_doc = ''
|
||||||
|
def_step = 4
|
||||||
|
|
||||||
|
elif def_step == 4:
|
||||||
|
if type == STRING:
|
||||||
|
def_doc = _trim_doc(text)
|
||||||
|
newdef = None
|
||||||
|
if def_sline:
|
||||||
|
if type == NEWLINE:
|
||||||
|
newdef = FunctionDesc(def_name, def_params, def_lineno, def_doc)
|
||||||
|
else:
|
||||||
|
if type == NAME:
|
||||||
|
newdef = FunctionDesc(def_name, def_params, def_lineno, def_doc)
|
||||||
|
if newdef:
|
||||||
if cls_step > 0: # Parsing a class
|
if cls_step > 0: # Parsing a class
|
||||||
cls_defs[def_name] = FunctionDesc(def_name, def_params, def_lineno)
|
cls_defs[def_name] = newdef
|
||||||
else:
|
else:
|
||||||
defs[def_name] = FunctionDesc(def_name, def_params, def_lineno)
|
defs[def_name] = newdef
|
||||||
def_step = 0
|
def_step = 0
|
||||||
|
|
||||||
##########################
|
##########################
|
||||||
@@ -357,39 +461,42 @@ def parse_text(txt):
|
|||||||
if cls_step > 0: # Parsing a class
|
if cls_step > 0: # Parsing a class
|
||||||
# Look for 'self.???'
|
# Look for 'self.???'
|
||||||
if var1_step == 0:
|
if var1_step == 0:
|
||||||
if string == 'self':
|
if text == 'self':
|
||||||
var1_step = 1
|
var1_step = 1
|
||||||
elif var1_step == 1:
|
elif var1_step == 1:
|
||||||
if string == '.':
|
if text == '.':
|
||||||
var_name = None
|
var_name = None
|
||||||
var1_step = 2
|
var1_step = 2
|
||||||
else:
|
else:
|
||||||
var1_step = 0
|
var1_step = 0
|
||||||
elif var1_step == 2:
|
elif var1_step == 2:
|
||||||
if type == NAME:
|
if type == NAME:
|
||||||
var_name = string
|
var_name = text
|
||||||
if cls_vars.has_key(var_name):
|
if cls_vars.has_key(var_name):
|
||||||
var_step = 0
|
var_step = 0
|
||||||
else:
|
else:
|
||||||
var1_step = 3
|
var1_step = 3
|
||||||
elif var1_step == 3:
|
elif var1_step == 3:
|
||||||
if string == '=':
|
if text == '=':
|
||||||
var1_step = 4
|
var1_step = 4
|
||||||
elif var1_step == 4:
|
elif var1_step == 4:
|
||||||
var_type = None
|
var_type = None
|
||||||
if string == '[':
|
if type == NUMBER:
|
||||||
close = line.find(']', end[1])
|
if text.find('.') != -1: var_type = float
|
||||||
var_type = list
|
else: var_type = int
|
||||||
elif type == STRING:
|
elif type == STRING:
|
||||||
close = end[1]
|
close = end[1]
|
||||||
var_type = str
|
var_type = str
|
||||||
elif string == '(':
|
elif text == '[':
|
||||||
|
close = line.find(']', end[1])
|
||||||
|
var_type = list
|
||||||
|
elif text == '(':
|
||||||
close = line.find(')', end[1])
|
close = line.find(')', end[1])
|
||||||
var_type = tuple
|
var_type = tuple
|
||||||
elif string == '{':
|
elif text == '{':
|
||||||
close = line.find('}', end[1])
|
close = line.find('}', end[1])
|
||||||
var_type = dict
|
var_type = dict
|
||||||
elif string == 'dict':
|
elif text == 'dict':
|
||||||
close = line.find(')', end[1])
|
close = line.find(')', end[1])
|
||||||
var_type = dict
|
var_type = dict
|
||||||
if var_type and close+1 < len(line):
|
if var_type and close+1 < len(line):
|
||||||
@@ -401,27 +508,28 @@ def parse_text(txt):
|
|||||||
elif def_step > 0: # Parsing a def
|
elif def_step > 0: # Parsing a def
|
||||||
# Look for 'global ???[,???]'
|
# Look for 'global ???[,???]'
|
||||||
if var2_step == 0:
|
if var2_step == 0:
|
||||||
if string == 'global':
|
if text == 'global':
|
||||||
var2_step = 1
|
var2_step = 1
|
||||||
elif var2_step == 1:
|
elif var2_step == 1:
|
||||||
if type == NAME:
|
if type == NAME:
|
||||||
vars[string] = True
|
if not vars.has_key(text):
|
||||||
elif string != ',' and type != NL:
|
vars[text] = VarDesc(text, None, start[0])
|
||||||
|
elif text != ',' and type != NL:
|
||||||
var2_step == 0
|
var2_step == 0
|
||||||
|
|
||||||
else: # In global scope
|
else: # In global scope
|
||||||
if var3_step == 0:
|
if var3_step == 0:
|
||||||
# Look for names
|
# Look for names
|
||||||
if string == 'for':
|
if text == 'for':
|
||||||
var_accum = dict()
|
var_accum = dict()
|
||||||
var_forflag = True
|
var_forflag = True
|
||||||
elif string == '=' or (var_forflag and string == 'in'):
|
elif text == '=' or (var_forflag and text == 'in'):
|
||||||
var_forflag = False
|
var_forflag = False
|
||||||
var3_step = 1
|
var3_step = 1
|
||||||
elif type == NAME:
|
elif type == NAME:
|
||||||
if prev_string != '.' and not vars.has_key(string):
|
if prev_text != '.' and not vars.has_key(text):
|
||||||
var_accum[string] = VarDesc(string, None, start[0])
|
var_accum[text] = VarDesc(text, None, start[0])
|
||||||
elif not string in [',', '(', ')', '[', ']']:
|
elif not text in [',', '(', ')', '[', ']']:
|
||||||
var_accum = dict()
|
var_accum = dict()
|
||||||
var_forflag = False
|
var_forflag = False
|
||||||
elif var3_step == 1:
|
elif var3_step == 1:
|
||||||
@@ -431,10 +539,13 @@ def parse_text(txt):
|
|||||||
else:
|
else:
|
||||||
var_name = var_accum.keys()[0]
|
var_name = var_accum.keys()[0]
|
||||||
var_type = None
|
var_type = None
|
||||||
if string == '[': var_type = list
|
if type == NUMBER:
|
||||||
|
if text.find('.') != -1: var_type = float
|
||||||
|
else: var_type = int
|
||||||
elif type == STRING: var_type = str
|
elif type == STRING: var_type = str
|
||||||
elif string == '(': var_type = tuple
|
elif text == '[': var_type = list
|
||||||
elif string == '{': var_type = dict
|
elif text == '(': var_type = tuple
|
||||||
|
elif text == '{': var_type = dict
|
||||||
vars[var_name] = VarDesc(var_name, var_type, start[0])
|
vars[var_name] = VarDesc(var_name, var_type, start[0])
|
||||||
var3_step = 0
|
var3_step = 0
|
||||||
|
|
||||||
@@ -443,10 +554,10 @@ def parse_text(txt):
|
|||||||
#######################
|
#######################
|
||||||
|
|
||||||
prev_type = type
|
prev_type = type
|
||||||
prev_string = string
|
prev_text = text
|
||||||
|
|
||||||
desc = ScriptDesc(txt.name, imports, classes, defs, vars, incomplete)
|
desc = ScriptDesc(txt.name, imports, classes, defs, vars, incomplete)
|
||||||
desc.set_time()
|
desc.set_delay(10 * (time()-start_time) + 0.05)
|
||||||
|
|
||||||
global _parse_cache
|
global _parse_cache
|
||||||
_parse_cache[hash(txt)] = desc
|
_parse_cache[hash(txt)] = desc
|
||||||
@@ -587,13 +698,11 @@ def get_targets(line, cursor):
|
|||||||
returns them as a list in the same order.
|
returns them as a list in the same order.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
targets = []
|
|
||||||
i = cursor - 1
|
i = cursor - 1
|
||||||
while i >= 0 and (line[i].isalnum() or line[i] == '_' or line[i] == '.'):
|
while i >= 0 and (line[i].isalnum() or line[i] == '_' or line[i] == '.'):
|
||||||
i -= 1
|
i -= 1
|
||||||
|
|
||||||
pre = line[i+1:cursor]
|
return line[i+1:cursor].split('.')
|
||||||
return pre.split('.')
|
|
||||||
|
|
||||||
def get_defs(txt):
|
def get_defs(txt):
|
||||||
"""Returns a dictionary which maps definition names in the source code to
|
"""Returns a dictionary which maps definition names in the source code to
|
||||||
@@ -650,6 +759,7 @@ def print_cache_for(txt, period=sys.maxint):
|
|||||||
print 'Defs:'
|
print 'Defs:'
|
||||||
for name, ddesc in desc.defs.items():
|
for name, ddesc in desc.defs.items():
|
||||||
print ' ', name, ddesc.params, ddesc.lineno
|
print ' ', name, ddesc.params, ddesc.lineno
|
||||||
|
print ' ', ddesc.doc
|
||||||
print '------------------------------------------------'
|
print '------------------------------------------------'
|
||||||
print 'Vars:'
|
print 'Vars:'
|
||||||
for name, vdesc in desc.vars.items():
|
for name, vdesc in desc.vars.items():
|
||||||
@@ -663,10 +773,12 @@ def print_cache_for(txt, period=sys.maxint):
|
|||||||
for clsnme, clsdsc in desc.classes.items():
|
for clsnme, clsdsc in desc.classes.items():
|
||||||
print ' *********************************'
|
print ' *********************************'
|
||||||
print ' Name:', clsnme
|
print ' Name:', clsnme
|
||||||
|
print ' ', clsdsc.doc
|
||||||
print ' ---------------------------------'
|
print ' ---------------------------------'
|
||||||
print ' Defs:'
|
print ' Defs:'
|
||||||
for name, ddesc in clsdsc.defs.items():
|
for name, ddesc in clsdsc.defs.items():
|
||||||
print ' ', name, ddesc.params, ddesc.lineno
|
print ' ', name, ddesc.params, ddesc.lineno
|
||||||
|
print ' ', ddesc.doc
|
||||||
print ' ---------------------------------'
|
print ' ---------------------------------'
|
||||||
print ' Vars:'
|
print ' Vars:'
|
||||||
for name, vdesc in clsdsc.vars.items():
|
for name, vdesc in clsdsc.vars.items():
|
||||||
|
|||||||
@@ -27,49 +27,36 @@ def main():
|
|||||||
if get_context(txt) != CTX_NORMAL:
|
if get_context(txt) != CTX_NORMAL:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Look backwards for first '(' without ')'
|
# Identify the name under the cursor
|
||||||
b = 0
|
llen = len(line)
|
||||||
found = False
|
while c<llen and (line[c].isalnum() or line[c]=='_'):
|
||||||
for i in range(c-1, -1, -1):
|
c += 1
|
||||||
if line[i] == ')': b += 1
|
|
||||||
elif line[i] == '(':
|
|
||||||
b -= 1
|
|
||||||
if b < 0:
|
|
||||||
found = True
|
|
||||||
c = i
|
|
||||||
break
|
|
||||||
|
|
||||||
# Otherwise identify the name under the cursor
|
targets = get_targets(line, c)
|
||||||
if not found:
|
|
||||||
llen = len(line)
|
|
||||||
while c<llen and (line[c].isalnum() or line[c]=='_'):
|
|
||||||
c += 1
|
|
||||||
|
|
||||||
pre = get_targets(line, c)
|
# If no name under cursor, look backward to see if we're in function parens
|
||||||
|
if len(targets) == 0 or targets[0] == '':
|
||||||
|
# Look backwards for first '(' without ')'
|
||||||
|
b = 0
|
||||||
|
found = False
|
||||||
|
for i in range(c-1, -1, -1):
|
||||||
|
if line[i] == ')': b += 1
|
||||||
|
elif line[i] == '(':
|
||||||
|
b -= 1
|
||||||
|
if b < 0:
|
||||||
|
found = True
|
||||||
|
c = i
|
||||||
|
break
|
||||||
|
if found: targets = get_targets(line, c)
|
||||||
|
if len(targets) == 0 or targets[0] == '':
|
||||||
|
return
|
||||||
|
|
||||||
if len(pre) == 0:
|
obj = resolve_targets(txt, targets)
|
||||||
return
|
if not obj: return
|
||||||
|
|
||||||
imports = get_imports(txt)
|
if isinstance(obj, Definition): # Local definition
|
||||||
builtins = get_builtins()
|
txt.showDocs(obj.doc)
|
||||||
|
elif hasattr(obj, '__doc__') and obj.__doc__:
|
||||||
# Identify the root (root.sub.sub.)
|
|
||||||
if imports.has_key(pre[0]):
|
|
||||||
obj = imports[pre[0]]
|
|
||||||
elif builtins.has_key(pre[0]):
|
|
||||||
obj = builtins[pre[0]]
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Step through sub-attributes
|
|
||||||
try:
|
|
||||||
for name in pre[1:]:
|
|
||||||
obj = getattr(obj, name)
|
|
||||||
except AttributeError:
|
|
||||||
print "Attribute not found '%s' in '%s'" % (name, '.'.join(pre))
|
|
||||||
return
|
|
||||||
|
|
||||||
if hasattr(obj, '__doc__') and obj.__doc__:
|
|
||||||
txt.showDocs(obj.__doc__)
|
txt.showDocs(obj.__doc__)
|
||||||
|
|
||||||
# Check we are running as a script and not imported as a module
|
# Check we are running as a script and not imported as a module
|
||||||
|
|||||||
@@ -27,23 +27,15 @@ def main():
|
|||||||
if get_context(txt) != CTX_NORMAL:
|
if get_context(txt) != CTX_NORMAL:
|
||||||
return
|
return
|
||||||
|
|
||||||
pre = get_targets(line, c)
|
targets = get_targets(line, c)
|
||||||
|
|
||||||
if len(pre) <= 1:
|
if targets[0] == '': # Check if we are looking at a constant [] {} '' etc.
|
||||||
return
|
i = c - len('.'.join(targets)) - 1
|
||||||
|
|
||||||
imports = get_imports(txt)
|
|
||||||
builtins = get_builtins()
|
|
||||||
|
|
||||||
# Identify the root (root.sub.sub.)
|
|
||||||
obj = None
|
|
||||||
if pre[0] == '':
|
|
||||||
i = c - len('.'.join(pre)) - 1
|
|
||||||
if i >= 0:
|
if i >= 0:
|
||||||
if line[i] == '"' or line[i] == "'":
|
if line[i] == '"' or line[i] == "'":
|
||||||
obj = str
|
targets[0] = 'str'
|
||||||
elif line[i] == '}':
|
elif line[i] == '}':
|
||||||
obj = dict
|
targets[0] = 'dict'
|
||||||
elif line[i] == ']': # Could be array elem x[y] or list [y]
|
elif line[i] == ']': # Could be array elem x[y] or list [y]
|
||||||
i = line.rfind('[', 0, i) - 1
|
i = line.rfind('[', 0, i) - 1
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
@@ -54,47 +46,44 @@ def main():
|
|||||||
break
|
break
|
||||||
i -= 1
|
i -= 1
|
||||||
if i < 0:
|
if i < 0:
|
||||||
obj = list
|
targets[0] = 'list'
|
||||||
elif imports.has_key(pre[0]):
|
|
||||||
obj = imports[pre[0]]
|
|
||||||
elif builtins.has_key(pre[0]):
|
|
||||||
obj = builtins[pre[0]]
|
|
||||||
else:
|
|
||||||
desc = get_cached_descriptor(txt)
|
|
||||||
if desc.vars.has_key(pre[0]):
|
|
||||||
obj = desc.vars[pre[0]].type
|
|
||||||
|
|
||||||
|
obj = resolve_targets(txt, targets[:-1])
|
||||||
if not obj:
|
if not obj:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Step through sub-attributes
|
|
||||||
try:
|
|
||||||
for name in pre[1:-1]:
|
|
||||||
obj = getattr(obj, name)
|
|
||||||
except AttributeError:
|
|
||||||
print "Attribute not found '%s' in '%s'" % (name, '.'.join(pre))
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
attr = obj.__dict__.keys()
|
|
||||||
except AttributeError:
|
|
||||||
attr = dir(obj)
|
|
||||||
else:
|
|
||||||
if not attr:
|
|
||||||
attr = dir(obj)
|
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
for k in attr:
|
|
||||||
|
if isinstance(obj, VarDesc):
|
||||||
|
obj = obj.type
|
||||||
|
|
||||||
|
if isinstance(obj, Definition): # Locally defined
|
||||||
|
if hasattr(obj, 'classes'):
|
||||||
|
items.extend([(s, 'f') for s in obj.classes.keys()])
|
||||||
|
if hasattr(obj, 'defs'):
|
||||||
|
items.extend([(s, 'f') for s in obj.defs.keys()])
|
||||||
|
if hasattr(obj, 'vars'):
|
||||||
|
items.extend([(s, 'v') for s in obj.vars.keys()])
|
||||||
|
|
||||||
|
else: # Otherwise we have an imported or builtin object
|
||||||
try:
|
try:
|
||||||
v = getattr(obj, k)
|
attr = obj.__dict__.keys()
|
||||||
except (AttributeError, TypeError): # Some attributes are not readable
|
except AttributeError:
|
||||||
pass
|
attr = dir(obj)
|
||||||
else:
|
else:
|
||||||
items.append((k, type_char(v)))
|
if not attr: attr = dir(obj)
|
||||||
|
|
||||||
|
for k in attr:
|
||||||
|
try:
|
||||||
|
v = getattr(obj, k)
|
||||||
|
except (AttributeError, TypeError): # Some attributes are not readable
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
items.append((k, type_char(v)))
|
||||||
|
|
||||||
if items != []:
|
if items != []:
|
||||||
items.sort(cmp = suggest_cmp)
|
items.sort(cmp = suggest_cmp)
|
||||||
txt.suggest(items, pre[-1])
|
txt.suggest(items, targets[-1])
|
||||||
|
|
||||||
# Check we are running as a script and not imported as a module
|
# Check we are running as a script and not imported as a module
|
||||||
if __name__ == "__main__" and OK:
|
if __name__ == "__main__" and OK:
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ def main():
|
|||||||
if not txt:
|
if not txt:
|
||||||
return
|
return
|
||||||
|
|
||||||
(line, c) = current_line(txt)
|
line, c = current_line(txt)
|
||||||
|
|
||||||
# Check we are in a normal context
|
# Check we are in a normal context
|
||||||
if get_context(txt) != CTX_NORMAL:
|
if get_context(txt) != CTX_NORMAL:
|
||||||
@@ -65,7 +65,7 @@ def main():
|
|||||||
|
|
||||||
# Otherwise we suggest globals, keywords, etc.
|
# Otherwise we suggest globals, keywords, etc.
|
||||||
list = []
|
list = []
|
||||||
pre = get_targets(line, c)
|
targets = get_targets(line, c)
|
||||||
desc = get_cached_descriptor(txt)
|
desc = get_cached_descriptor(txt)
|
||||||
|
|
||||||
for k in KEYWORDS:
|
for k in KEYWORDS:
|
||||||
@@ -87,7 +87,7 @@ def main():
|
|||||||
list.append((k, 'v'))
|
list.append((k, 'v'))
|
||||||
|
|
||||||
list.sort(cmp = suggest_cmp)
|
list.sort(cmp = suggest_cmp)
|
||||||
txt.suggest(list, pre[-1])
|
txt.suggest(list, targets[-1])
|
||||||
|
|
||||||
# Check we are running as a script and not imported as a module
|
# Check we are running as a script and not imported as a module
|
||||||
if __name__ == "__main__" and OK:
|
if __name__ == "__main__" and OK:
|
||||||
|
|||||||
Reference in New Issue
Block a user