Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2009-10-30 12:34:57 +0300
committerCampbell Barton <ideasman42@gmail.com>2009-10-30 12:34:57 +0300
commitae9eae222c0a92bea637f1023a46fe44e4a2a534 (patch)
treea3845fa59eeb9bec53c675792fe1f6176be4445c /release/scripts/modules/console
parentae3cf92491be73f353b278f1632a68990d0e44d9 (diff)
Patch from Stani for autocomplete
adds ability to complete in these situations bpy -> bpy. bpy.data.objects -> bpy.data.objects["Mesh"] my autocomplete could only do bpy -> bpy.
Diffstat (limited to 'release/scripts/modules/console')
-rw-r--r--release/scripts/modules/console/complete_import.py14
-rw-r--r--release/scripts/modules/console/complete_namespace.py147
-rw-r--r--release/scripts/modules/console/intellisense.py26
3 files changed, 162 insertions, 25 deletions
diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py
index 02ded3eef6d..9166dee2bb2 100644
--- a/release/scripts/modules/console/complete_import.py
+++ b/release/scripts/modules/console/complete_import.py
@@ -126,6 +126,8 @@ def complete(line):
>>> complete('import weak')
['weakref']
+ >>> complete('from weakref import C')
+ ['CallableProxyType']
"""
import inspect
@@ -148,6 +150,8 @@ def complete(line):
(hasattr(m, '__file__') and '__init__' in m.__file__):
completion_list = [attr for attr in dir(m)
if is_importable(m, attr)]
+ else:
+ completion_list = []
completion_list.extend(getattr(m, '__all__', []))
if hasattr(m, '__file__') and '__init__' in m.__file__:
completion_list.extend(module_list(os.path.dirname(m.__file__)))
@@ -156,6 +160,9 @@ def complete(line):
completion_list.remove('__init__')
return completion_list
+ def filter_prefix(names, prefix):
+ return [name for name in names if name.startswith(prefix)]
+
words = line.split(' ')
if len(words) == 3 and words[0] == 'from':
return ['import ']
@@ -164,11 +171,10 @@ def complete(line):
return get_root_modules()
mod = words[1].split('.')
if len(mod) < 2:
- mod0 = mod[0]
- return [m for m in get_root_modules() if m.startswith(mod0)]
+ return filter_prefix(get_root_modules(), words[-1])
completion_list = try_import('.'.join(mod[:-1]), True)
completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list]
- return completion_list
+ return filter_prefix(completion_list, words[-1])
if len(words) >= 3 and words[0] == 'from':
mod = words[1]
- return try_import(mod)
+ return filter_prefix(try_import(mod), words[-1])
diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py
index a2836a60b29..4aa0de558f2 100644
--- a/release/scripts/modules/console/complete_namespace.py
+++ b/release/scripts/modules/console/complete_namespace.py
@@ -15,12 +15,86 @@
"""Autocomplete with the standard library"""
+import re
import rlcompleter
+
+RE_INCOMPLETE_INDEX = re.compile('(.*?)\[[^\]]+$')
+
TEMP = '__tEmP__' # only \w characters are allowed!
TEMP_N = len(TEMP)
+def is_dict(obj):
+ """Returns whether obj is a dictionary"""
+ return hasattr(obj, 'keys') and hasattr(getattr(obj, 'keys'), '__call__')
+
+
+def complete_names(word, namespace):
+ """Complete variable names or attributes
+
+ :param word: word to be completed
+ :type word: str
+ :param namespace: namespace
+ :type namespace: dict
+ :returns: completion matches
+ :rtype: list of str
+
+ >>> complete_names('fo', {'foo': 'bar'})
+ ['foo', 'for', 'format(']
+ """
+ # start completer
+ completer = rlcompleter.Completer(namespace)
+ # find matches with std library (don't try to implement this yourself)
+ completer.complete(word, 0)
+ return sorted(set(completer.matches))
+
+
+def complete_indices(word, namespace, obj=None, base=None):
+ """Complete a list or dictionary with its indices:
+
+ * integer numbers for list
+ * any keys for dictionary
+
+ :param word: word to be completed
+ :type word: str
+ :param namespace: namespace
+ :type namespace: dict
+ :param obj: object evaluated from base
+ :param base: substring which can be evaluated into an object
+ :type base: str
+ :returns: completion matches
+ :rtype: list of str
+
+ >>> complete_indices('foo', {'foo': range(5)})
+ ['foo[0]', 'foo[1]', 'foo[2]', 'foo[3]', 'foo[4]']
+ >>> complete_indices('foo', {'foo': {'bar':0, 1:2}})
+ ['foo[1]', "foo['bar']"]
+ >>> complete_indices("foo['b", {'foo': {'bar':0, 1:2}}, base='foo')
+ ["foo['bar']"]
+ """
+ #FIXME: 'foo["b'
+ if base is None:
+ base = word
+ if obj is None:
+ try:
+ obj = eval(base, namespace)
+ except Exception:
+ return []
+ if not hasattr(obj, '__getitem__'):
+ # obj is not a list or dictionary
+ return []
+ if is_dict(obj):
+ # dictionary type
+ matches = ['%s[%r]' % (base, key) for key in sorted(obj.keys())]
+ else:
+ # list type
+ matches = ['%s[%d]' % (base, idx) for idx in range(len(obj))]
+ if word != base:
+ matches = [match for match in matches if match.startswith(word)]
+ return matches
+
+
def complete(word, namespace, private=True):
"""Complete word within a namespace with the standard rlcompleter
module. Also supports index or key access [].
@@ -31,32 +105,77 @@ def complete(word, namespace, private=True):
:type namespace: dict
:param private: whether private attribute/methods should be returned
:type private: bool
+ :returns: completion matches
+ :rtype: list of str
- >>> complete('fo', {'foo': 'bar'})
- ['foo']
+ >>> complete('foo[1', {'foo': range(14)})
+ ['foo[1]', 'foo[10]', 'foo[11]', 'foo[12]', 'foo[13]']
+ >>> complete('foo[0]', {'foo': [range(5)]})
+ ['foo[0][0]', 'foo[0][1]', 'foo[0][2]', 'foo[0][3]', 'foo[0][4]']
+ >>> complete('foo[0].i', {'foo': [range(5)]})
+ ['foo[0].index(', 'foo[0].insert(']
+ >>> complete('rlcompleter', {'rlcompleter': rlcompleter})
+ ['rlcompleter.']
"""
- completer = rlcompleter.Completer(namespace)
+ #
+ # if word is empty -> nothing to complete
+ if not word:
+ return []
+
+ re_incomplete_index = RE_INCOMPLETE_INDEX.search(word)
+ if re_incomplete_index:
+ # ignore incomplete index at the end, e.g 'a[1' -> 'a'
+ matches = complete_indices(word, namespace,
+ base=re_incomplete_index.group(1))
+
+ elif not('[' in word):
+ matches = complete_names(word, namespace)
+
+ elif word[-1] == ']':
+ matches = [word]
+
+ elif '.' in word:
+ # brackets are normally not allowed -> work around
- # brackets are normally not allowed -> work around (only in this case)
- if '[' in word:
+ # remove brackets by using a temp var without brackets
obj, attr = word.rsplit('.', 1)
try:
# do not run the obj expression in the console
namespace[TEMP] = eval(obj, namespace)
except Exception:
return []
- _word = TEMP + '.' + attr
+ matches = complete_names(TEMP + '.' + attr, namespace)
+ matches = [obj + match[TEMP_N:] for match in matches]
+ del namespace[TEMP]
+
else:
- _word = word
+ # safety net, but when would this occur?
+ return []
- # find matches with stdlibrary (don't try to implement this yourself)
- completer.complete(_word, 0)
- matches = completer.matches
+ if not matches:
+ return []
- # brackets are normally not allowed -> clean up
- if '[' in word:
- matches = [obj + match[TEMP_N:] for match in matches]
- del namespace[TEMP]
+ # add '.', '(' or '[' if no match has been found
+ elif len(matches) == 1 and matches[0] == word:
+
+ # try to retrieve the object
+ try:
+ obj = eval(word, namespace)
+ except Exception:
+ return []
+ # ignore basic types
+ if type(obj) in (bool, float, int, str):
+ return []
+ # an extra char '[', '(' or '.' will be added
+ if hasattr(obj, '__getitem__'):
+ # list or dictionary
+ matches = complete_indices(word, namespace, obj)
+ elif hasattr(obj, '__call__'):
+ # callables
+ matches = [word + '(']
+ else:
+ # any other type
+ matches = [word + '.']
# separate public from private
public_matches = [match for match in matches if not('._' in match)]
diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py
index 2658f79a4cc..eda34c9ff6b 100644
--- a/release/scripts/modules/console/intellisense.py
+++ b/release/scripts/modules/console/intellisense.py
@@ -29,17 +29,29 @@ import re
# line which starts with an import statement
RE_MODULE = re.compile('^import|from.+')
-# The following regular expression means a word which:
-# - doesn't start with a quote (quoted words are not py objects)
-# - starts with a [a-zA-Z0-9_]
-# - afterwards dots are allowed as well
-# - square bracket pairs [] are allowed (should be closed)
+# The following regular expression means an 'unquoted' word
RE_UNQUOTED_WORD = re.compile(
- '''(?:^|[^"'])((?:\w+(?:\w|[.]|\[.+?\])*|))$''', re.UNICODE)
+ # don't start with a quote
+ '''(?:^|[^"'a-zA-Z0-9_])'''
+ # start with a \w = [a-zA-Z0-9_]
+ '''((?:\w+'''
+ # allow also dots and closed bracket pairs []
+ '''(?:\w|[.]|\[.+?\])*'''
+ # allow empty string
+ '''|)'''
+ # allow an unfinished index at the end (including quotes)
+ '''(?:\[[^\]]*$)?)$''',
+ # allow unicode as theoretically this is possible
+ re.UNICODE)
def complete(line, cursor, namespace, private=True):
- """Returns a list of possible completions.
+ """Returns a list of possible completions:
+
+ * name completion
+ * attribute completion (obj.attr)
+ * index completion for lists and dictionaries
+ * module completion (from/import)
:param line: incomplete text line
:type line: str