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.
This commit is contained in:
16
release/scripts/modules/console/__init__.py
Normal file
16
release/scripts/modules/console/__init__.py
Normal file
@@ -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."""
|
||||
174
release/scripts/modules/console/complete_import.py
Normal file
174
release/scripts/modules/console/complete_import.py
Normal file
@@ -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)
|
||||
67
release/scripts/modules/console/complete_namespace.py
Normal file
67
release/scripts/modules/console/complete_namespace.py
Normal file
@@ -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
|
||||
100
release/scripts/modules/console/intellisense.py
Normal file
100
release/scripts/modules/console/intellisense.py
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user