diff options
author | Campbell Barton <ideasman42@gmail.com> | 2009-10-29 23:55:45 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2009-10-29 23:55:45 +0300 |
commit | 4b3fd4a8e05e49a9034e5ee789fcfef3961c19c5 (patch) | |
tree | 278aea66d1d901873a8e19f329f7a89c8a434d51 /release/scripts/modules/console | |
parent | 8c707b2a5f306f149bcd3057ab6fe91076c75dd8 (diff) |
replacement for my own autocomplete module by stani
--- from his patch
All the functionality is in the console
folder:
- intellisense.py: the central module which loads others on demand
- complete_namespace: more or less a replacement for the old autocomplete.py
- complete_import: module completion (I find this very handy, not just luxury)
These complete_* modules work very simple and should also work outside blender. You give some input and it returns a list with possible completions.
autocomplete.py is now deprecated.
Diffstat (limited to 'release/scripts/modules/console')
-rw-r--r-- | release/scripts/modules/console/__init__.py | 16 | ||||
-rw-r--r-- | release/scripts/modules/console/complete_import.py | 174 | ||||
-rw-r--r-- | release/scripts/modules/console/complete_namespace.py | 67 | ||||
-rw-r--r-- | release/scripts/modules/console/intellisense.py | 100 |
4 files changed, 357 insertions, 0 deletions
diff --git a/release/scripts/modules/console/__init__.py b/release/scripts/modules/console/__init__.py new file mode 100644 index 00000000000..eb32d78b1ef --- /dev/null +++ b/release/scripts/modules/console/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2009 www.stani.be (GPL license) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""Package for console specific modules.""" diff --git a/release/scripts/modules/console/complete_import.py b/release/scripts/modules/console/complete_import.py new file mode 100644 index 00000000000..02ded3eef6d --- /dev/null +++ b/release/scripts/modules/console/complete_import.py @@ -0,0 +1,174 @@ +# Copyright (c) 2009 Fernando Perez, www.stani.be (GPL license) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# Original copyright (see docstring): +#***************************************************************************** +# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu> +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#***************************************************************************** + +"""Completer for import statements + +Original code was from IPython/Extensions/ipy_completers.py. The following +changes have been made: +- ported to python3 +- pep8 polishing +- limit list of modules to prefix in case of "from w" +- sorted modules +- added sphinx documentation +""" + + +import os +import sys + +TIMEOUT_STORAGE = 3 # Time in secs after which the rootmodules will be stored +TIMEOUT_GIVEUP = 20 # Time in secs after which we give up + +ROOT_MODULES = None + + +def get_root_modules(): + """ + Returns a list containing the names of all the modules available in the + folders of the pythonpath. + + :returns: modules + :rtype: list + """ + global ROOT_MODULES + modules = [] + if not(ROOT_MODULES is None): + return ROOT_MODULES + from time import time + t = time() + store = False + for path in sys.path: + modules += module_list(path) + if time() - t >= TIMEOUT_STORAGE and not store: + # Caching the list of root modules, please wait! + store = True + if time() - t > TIMEOUT_GIVEUP: + # This is taking too long, we give up. + ROOT_MODULES = [] + return [] + + modules += sys.builtin_module_names + + modules = list(set(modules)) + if '__init__' in modules: + modules.remove('__init__') + modules = sorted(set(modules)) + if store: + ROOT_MODULES = modules + return modules + + +def module_list(path): + """ + Return the list containing the names of the modules available in + the given folder. + + :param path: folder path + :type path: str + :returns: modules + :rtype: list + """ + + if os.path.isdir(path): + folder_list = os.listdir(path) + elif path.endswith('.egg'): + from zipimport import zipimporter + try: + folder_list = [f for f in zipimporter(path)._files] + except: + folder_list = [] + else: + folder_list = [] + #folder_list = glob.glob(os.path.join(path,'*')) + folder_list = [p for p in folder_list \ + if os.path.exists(os.path.join(path, p, '__init__.py'))\ + or p[-3:] in ('.py', '.so')\ + or p[-4:] in ('.pyc', '.pyo', '.pyd')] + + folder_list = [os.path.basename(p).split('.')[0] for p in folder_list] + return folder_list + + +def complete(line): + """ + Returns a list containing the completion possibilities for an import line. + + :param line: + + incomplete line which contains an import statement:: + + import xml.d + from xml.dom import + + :type line: str + :returns: list of completion possibilities + :rtype: list + + >>> complete('import weak') + ['weakref'] + """ + import inspect + + def try_import(mod, only_modules=False): + + def is_importable(module, attr): + if only_modules: + return inspect.ismodule(getattr(module, attr)) + else: + return not(attr[:2] == '__' and attr[-2:] == '__') + + try: + m = __import__(mod) + except: + return [] + mods = mod.split('.') + for module in mods[1:]: + m = getattr(m, module) + if (not hasattr(m, '__file__')) or (not only_modules) or\ + (hasattr(m, '__file__') and '__init__' in m.__file__): + completion_list = [attr for attr in dir(m) + if is_importable(m, attr)] + 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__))) + completion_list = list(set(completion_list)) + if '__init__' in completion_list: + completion_list.remove('__init__') + return completion_list + + words = line.split(' ') + if len(words) == 3 and words[0] == 'from': + return ['import '] + if len(words) < 3 and (words[0] in ['import', 'from']): + if len(words) == 1: + 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)] + completion_list = try_import('.'.join(mod[:-1]), True) + completion_list = ['.'.join(mod[:-1] + [el]) for el in completion_list] + return completion_list + if len(words) >= 3 and words[0] == 'from': + mod = words[1] + return try_import(mod) diff --git a/release/scripts/modules/console/complete_namespace.py b/release/scripts/modules/console/complete_namespace.py new file mode 100644 index 00000000000..a2836a60b29 --- /dev/null +++ b/release/scripts/modules/console/complete_namespace.py @@ -0,0 +1,67 @@ +# Copyright (c) 2009 www.stani.be (GPL license) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""Autocomplete with the standard library""" + +import rlcompleter + +TEMP = '__tEmP__' # only \w characters are allowed! +TEMP_N = len(TEMP) + + +def complete(word, namespace, private=True): + """Complete word within a namespace with the standard rlcompleter + module. Also supports index or key access []. + + :param word: word to be completed + :type word: str + :param namespace: namespace + :type namespace: dict + :param private: whether private attribute/methods should be returned + :type private: bool + + >>> complete('fo', {'foo': 'bar'}) + ['foo'] + """ + completer = rlcompleter.Completer(namespace) + + # brackets are normally not allowed -> work around (only in this case) + if '[' in word: + 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 + else: + _word = word + + # find matches with stdlibrary (don't try to implement this yourself) + completer.complete(_word, 0) + matches = completer.matches + + # brackets are normally not allowed -> clean up + if '[' in word: + matches = [obj + match[TEMP_N:] for match in matches] + del namespace[TEMP] + + # separate public from private + public_matches = [match for match in matches if not('._' in match)] + if private: + private_matches = [match for match in matches if '._' in match] + return public_matches + private_matches + else: + return public_matches diff --git a/release/scripts/modules/console/intellisense.py b/release/scripts/modules/console/intellisense.py new file mode 100644 index 00000000000..2658f79a4cc --- /dev/null +++ b/release/scripts/modules/console/intellisense.py @@ -0,0 +1,100 @@ +# Copyright (c) 2009 www.stani.be (GPL license) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +"""This module provides intellisense features such as: + +* autocompletion +* calltips (not yet implemented) + +It unifies all completion plugins and only loads them on demand. +""" +# TODO: file complete if startswith quotes +import os +import re + +# regular expressions to find out which completer we need + +# 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) +RE_UNQUOTED_WORD = re.compile( + '''(?:^|[^"'])((?:\w+(?:\w|[.]|\[.+?\])*|))$''', re.UNICODE) + + +def complete(line, cursor, namespace, private=True): + """Returns a list of possible completions. + + :param line: incomplete text line + :type line: str + :param cursor: current character position + :type cursor: int + :param namespace: namespace + :type namespace: dict + :param private: whether private variables should be listed + :type private: bool + :returns: list of completions, word + :rtype: list, str + """ + re_unquoted_word = RE_UNQUOTED_WORD.search(line[:cursor]) + if re_unquoted_word: + # unquoted word -> module or attribute completion + word = re_unquoted_word.group(1) + if RE_MODULE.match(line): + import complete_import + matches = complete_import.complete(line) + else: + import complete_namespace + matches = complete_namespace.complete(word, namespace, private) + else: + # for now we don't have completers for strings + # TODO: add file auto completer for strings + word = '' + matches = [] + return matches, word + + +def expand(line, cursor, namespace, private=True): + """This method is invoked when the user asks autocompletion, + e.g. when Ctrl+Space is clicked. + + :param line: incomplete text line + :type line: str + :param cursor: current character position + :type cursor: int + :param namespace: namespace + :type namespace: dict + :param private: whether private variables should be listed + :type private: bool + :returns: + + current expanded line, updated cursor position and scrollback + + :rtype: str, int, str + """ + matches, word = complete(line, cursor, namespace, private) + prefix = os.path.commonprefix(matches)[len(word):] + if prefix: + line = line[:cursor] + prefix + line[cursor:] + cursor += len(prefix) + if len(matches) == 1: + scrollback = '' + else: + scrollback = ' '.join([m.split('.')[-1] for m in matches]) + return line, cursor, scrollback |