diff options
author | Campbell Barton <ideasman42@gmail.com> | 2009-11-07 17:17:49 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2009-11-07 17:17:49 +0300 |
commit | f25bc9568855b1ad0e095f95e3b288fd007ef079 (patch) | |
tree | e9000eaea6261b53c88d6a8d8c14188f2db36830 /release | |
parent | ddeb9f8e24da6076ed76be1573cc152def5751ae (diff) |
missed committing this file (from Stani's patch)
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/console/complete_calltip.py | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/release/scripts/modules/console/complete_calltip.py b/release/scripts/modules/console/complete_calltip.py new file mode 100644 index 00000000000..486f9469d60 --- /dev/null +++ b/release/scripts/modules/console/complete_calltip.py @@ -0,0 +1,190 @@ +# 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/>. + +import inspect +import re + + +# regular expression constants +DEF_DOC = '%s\s*(\(.*?\))' +DEF_SOURCE = 'def\s+%s\s*(\(.*?\)):' +RE_EMPTY_LINE = re.compile('^\s*\n') +RE_FLAG = re.MULTILINE | re.DOTALL +RE_NEWLINE = re.compile('\n+') +RE_SPACE = re.compile('\s+') +RE_DEF_COMPLETE = re.compile( + # 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 opening bracket(s) + '''(?:\(|\s)*)$''') + + +def reduce_newlines(text): + """Reduces multiple newlines to a single newline. + + :param text: text with multiple newlines + :type text: str + :returns: text with single newlines + :rtype: str + + >>> reduce_newlines('hello\\n\\nworld') + 'hello\\nworld' + """ + return RE_NEWLINE.sub('\n', text) + + +def reduce_spaces(text): + """Reduces multiple whitespaces to a single space. + + :param text: text with multiple spaces + :type text: str + :returns: text with single spaces + :rtype: str + + >>> reduce_spaces('hello \\nworld') + 'hello world' + """ + return RE_SPACE.sub(' ', text) + + +def get_doc(object): + """Get the doc string or comments for an object. + + :param object: object + :returns: doc string + :rtype: str + + >>> get_doc(abs) + 'abs(number) -> number\\n\\nReturn the absolute value of the argument.' + """ + result = inspect.getdoc(object) or inspect.getcomments(object) + return result and RE_EMPTY_LINE.sub('', result.rstrip()) or '' + + +def get_argspec(func, strip_self=True, doc=None, source=None): + """Get argument specifications. + + :param strip_self: strip `self` from argspec + :type strip_self: bool + :param doc: doc string of func (optional) + :type doc: str + :param source: source code of func (optional) + :type source: str + :returns: argument specification + :rtype: str + + >>> get_argspec(inspect.getclasstree) + '(classes, unique=0)' + >>> get_argspec(abs) + '(number)' + """ + # get the function object of the class + try: + func = func.__func__ + except AttributeError: + try: + # py 2.X + func = func.im_func + except AttributeError: + pass + # is callable? + if not hasattr(func, '__call__'): + return '' + # func should have a name + try: + func_name = func.__name__ + except AttributeError: + return '' + # from docstring + if doc is None: + doc = get_doc(func) + match = re.search(DEF_DOC % func_name, doc, RE_FLAG) + # from source code + if not match: + if source is None: + try: + source = inspect.getsource(func) + except TypeError: + source = '' + if source: + match = re.search(DEF_SOURCE % func_name, source, RE_FLAG) + if match: + argspec = reduce_spaces(match.group(1)) + else: + # try with the inspect.getarg* functions + try: + argspec = inspect.formatargspec(*inspect.getfullargspec(func)) + except: + try: + # py 2.X + argspec = inspect.formatargspec(*inspect.getargspec(func)) + except: + try: + argspec = inspect.formatargvalues( + *inspect.getargvalues(func)) + except: + argspec = '' + if strip_self: + argspec = argspec.replace('self, ', '') + return argspec + + +def complete(line, cursor, namespace): + """Complete callable with calltip. + + :param line: incomplete text line + :type line: str + :param cursor: current character position + :type cursor: int + :param namespace: namespace + :type namespace: dict + :returns: (matches, world, scrollback) + :rtype: (list of str, str, str) + + >>> import os + >>> complete('os.path.isdir(', 14, {'os': os})[-1] + 'isdir(s)\\nReturn true if the pathname refers to an existing directory.' + >>> complete('abs(', 4, {})[-1] + 'abs(number) -> number\\nReturn the absolute value of the argument.' + """ + matches = [] + match = RE_DEF_COMPLETE.search(line[:cursor]) + if match: + word = match.group(1) + func_word = match.group(2) + try: + func = eval(func_word, namespace) + except Exception: + func = None + scrollback = '' + if func: + doc = get_doc(func) + argspec = get_argspec(func, doc=doc) + scrollback = func_word.split('.')[-1] + (argspec or '()') + if doc.startswith(scrollback): + scrollback = doc + elif doc: + scrollback += '\n' + doc + scrollback = reduce_newlines(scrollback) + else: + word = '' + scrollback = '' + return matches, word, scrollback |