completer.py
1172 lines
| 41.7 KiB
| text/x-python
|
PythonLexer
Brian E. Granger
|
r17700 | # encoding: utf-8 | ||
ville
|
r988 | """Word completion for IPython. | ||
This module is a fork of the rlcompleter module in the Python standard | ||||
library. The original enhancements made to rlcompleter have been sent | ||||
upstream and were accepted as of Python 2.3, but we need a lot more | ||||
functionality specific to IPython, so this module will continue to live as an | ||||
IPython-specific utility. | ||||
Original rlcompleter documentation: | ||||
This requires the latest extension to the readline module (the | ||||
completes keywords, built-ins and globals in __main__; when completing | ||||
NAME.NAME..., it evaluates (!) the expression up to the last dot and | ||||
completes its attributes. | ||||
It's very cool to do "import string" type "string.", hit the | ||||
completion key (twice), and see the list of names defined by the | ||||
string module! | ||||
Tip: to use the tab key as the completion key, call | ||||
readline.parse_and_bind("tab: complete") | ||||
Notes: | ||||
- Exceptions raised by the completer function are *ignored* (and | ||||
Thomas Kluyver
|
r12553 | generally cause the completion to fail). This is a feature -- since | ||
readline sets the tty device in raw (or cbreak) mode, printing a | ||||
traceback wouldn't work well without some complicated hoopla to save, | ||||
reset and restore the tty state. | ||||
ville
|
r988 | |||
- The evaluation of the NAME.NAME... form may cause arbitrary | ||||
Thomas Kluyver
|
r12553 | application defined code to be executed if an object with a | ||
``__getattr__`` hook is found. Since it is the responsibility of the | ||||
application (or the user) to enable this feature, I consider this an | ||||
acceptable risk. More complicated expressions (e.g. function calls or | ||||
indexing operations) are *not* evaluated. | ||||
ville
|
r988 | |||
- GNU readline is also used by the built-in functions input() and | ||||
Thomas Kluyver
|
r12553 | raw_input(), and thus these also benefit/suffer from the completer | ||
features. Clearly an interactive application can benefit by | ||||
specifying its own completer function and using raw_input() for all | ||||
its input. | ||||
ville
|
r988 | |||
- When the original stdin is not a tty device, GNU readline is never | ||||
Thomas Kluyver
|
r12553 | used, and this module (and the readline module) are silently inactive. | ||
ville
|
r988 | """ | ||
Paul Ivanov
|
r17735 | # Copyright (c) IPython Development Team. | ||
# Distributed under the terms of the Modified BSD License. | ||||
Paul Ivanov
|
r17756 | # | ||
# Some of this code originated from rlcompleter in the Python standard library | ||||
# Copyright (C) 2001 Python Software Foundation, www.python.org | ||||
Fernando Perez
|
r2365 | |||
ville
|
r988 | import __main__ | ||
import glob | ||||
Brian Granger
|
r2498 | import inspect | ||
Brian Granger
|
r2248 | import itertools | ||
ville
|
r988 | import keyword | ||
import os | ||||
import re | ||||
import sys | ||||
Brian Granger
|
r2205 | |||
MinRK
|
r4825 | from IPython.config.configurable import Configurable | ||
Brian Granger
|
r2205 | from IPython.core.error import TryNext | ||
MinRK
|
r7437 | from IPython.core.inputsplitter import ESC_MAGIC | ||
Brian E. Granger
|
r17738 | from IPython.core.latex_symbols import latex_symbols | ||
Fernando Perez
|
r2959 | from IPython.utils import generics | ||
MinRK
|
r16564 | from IPython.utils import io | ||
Thomas Kluyver
|
r17137 | from IPython.utils.decorators import undoc | ||
Brian Granger
|
r2498 | from IPython.utils.dir2 import dir2 | ||
Fernando Perez
|
r3074 | from IPython.utils.process import arg_split | ||
Brian E. Granger
|
r17738 | from IPython.utils.py3compat import builtin_mod, string_types, PY3 | ||
MinRK
|
r5231 | from IPython.utils.traitlets import CBool, Enum | ||
ville
|
r988 | |||
Fernando Perez
|
r2365 | #----------------------------------------------------------------------------- | ||
# Globals | ||||
#----------------------------------------------------------------------------- | ||||
# Public API | ||||
ville
|
r988 | __all__ = ['Completer','IPCompleter'] | ||
Fernando Perez
|
r2365 | if sys.platform == 'win32': | ||
PROTECTABLES = ' ' | ||||
else: | ||||
Fernando Perez
|
r3176 | PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&' | ||
Fernando Perez
|
r2365 | |||
Joel Nothman
|
r16466 | |||
Fernando Perez
|
r2365 | #----------------------------------------------------------------------------- | ||
# Main functions and classes | ||||
#----------------------------------------------------------------------------- | ||||
Fernando Perez
|
r3184 | def has_open_quotes(s): | ||
"""Return whether a string has open quotes. | ||||
This simply counts whether the number of quote characters of either type in | ||||
the string is odd. | ||||
Returns | ||||
------- | ||||
If there is an open quote, the quote character is returned. Else, return | ||||
False. | ||||
""" | ||||
# We check " first, then ', so complex cases with nested quotes will get | ||||
# the " to take precedence. | ||||
if s.count('"') % 2: | ||||
return '"' | ||||
elif s.count("'") % 2: | ||||
return "'" | ||||
else: | ||||
return False | ||||
Fernando Perez
|
r2365 | def protect_filename(s): | ||
"""Escape a string to protect certain characters.""" | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2365 | return "".join([(ch in PROTECTABLES and '\\' + ch or ch) | ||
for ch in s]) | ||||
Fernando Perez
|
r2965 | def expand_user(path): | ||
"""Expand '~'-style usernames in strings. | ||||
This is similar to :func:`os.path.expanduser`, but it computes and returns | ||||
extra information that will be useful if the input was being used in | ||||
computing completions, and you wish to return the completions with the | ||||
original '~' instead of its expanded value. | ||||
Parameters | ||||
---------- | ||||
path : str | ||||
String to be expanded. If no ~ is present, the output is the same as the | ||||
input. | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2965 | Returns | ||
------- | ||||
newpath : str | ||||
Result of ~ expansion in the input path. | ||||
tilde_expand : bool | ||||
Whether any expansion was performed or not. | ||||
tilde_val : str | ||||
The value that ~ was replaced with. | ||||
""" | ||||
# Default values | ||||
tilde_expand = False | ||||
tilde_val = '' | ||||
newpath = path | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2965 | if path.startswith('~'): | ||
tilde_expand = True | ||||
MinRK
|
r5201 | rest = len(path)-1 | ||
Fernando Perez
|
r2965 | newpath = os.path.expanduser(path) | ||
MinRK
|
r5201 | if rest: | ||
tilde_val = newpath[:-rest] | ||||
else: | ||||
tilde_val = newpath | ||||
Fernando Perez
|
r2965 | |||
return newpath, tilde_expand, tilde_val | ||||
def compress_user(path, tilde_expand, tilde_val): | ||||
"""Does the opposite of expand_user, with its outputs. | ||||
""" | ||||
if tilde_expand: | ||||
return path.replace(tilde_val, '~') | ||||
else: | ||||
return path | ||||
Fernando Perez
|
r6945 | |||
David P. Sanders
|
r13343 | |||
def penalize_magics_key(word): | ||||
"""key for sorting that penalizes magic commands in the ordering | ||||
Normal words are left alone. | ||||
Magic commands have the initial % moved to the end, e.g. | ||||
%matplotlib is transformed as follows: | ||||
%matplotlib -> matplotlib% | ||||
[The choice of the final % is arbitrary.] | ||||
Since "matplotlib" < "matplotlib%" as strings, | ||||
"timeit" will appear before the magic "%timeit" in the ordering | ||||
For consistency, move "%%" to the end, so cell magics appear *after* | ||||
line magics with the same name. | ||||
A check is performed that there are no other "%" in the string; | ||||
if there are, then the string is not a magic command and is left unchanged. | ||||
""" | ||||
# Move any % signs from start to end of the key | ||||
# provided there are no others elsewhere in the string | ||||
if word[:2] == "%%": | ||||
if not "%" in word[2:]: | ||||
return word[2:] + "%%" | ||||
if word[:1] == "%": | ||||
if not "%" in word[1:]: | ||||
return word[1:] + "%" | ||||
return word | ||||
Thomas Kluyver
|
r17137 | @undoc | ||
Fernando Perez
|
r2855 | class Bunch(object): pass | ||
Fernando Perez
|
r6945 | |||
MinRK
|
r4825 | DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?' | ||
klonuo
|
r9207 | GREEDY_DELIMS = ' =\r\n' | ||
Fernando Perez
|
r2855 | |||
Fernando Perez
|
r6945 | |||
Fernando Perez
|
r2855 | class CompletionSplitter(object): | ||
"""An object to split an input line in a manner similar to readline. | ||||
By having our own implementation, we can expose readline-like completion in | ||||
a uniform manner to all frontends. This object only needs to be given the | ||||
line of text to be split and the cursor position on said line, and it | ||||
returns the 'word' to be completed on at the cursor after splitting the | ||||
entire line. | ||||
What characters are used as splitting delimiters can be controlled by | ||||
setting the `delims` attribute (this is a property that internally | ||||
Fernando Perez
|
r6945 | automatically builds the necessary regular expression)""" | ||
Fernando Perez
|
r2855 | |||
# Private interface | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2855 | # A string of delimiter characters. The default value makes sense for | ||
# IPython's most typical usage patterns. | ||||
MinRK
|
r4825 | _delims = DELIMS | ||
Fernando Perez
|
r2855 | |||
# The expression (a normal string) to be compiled into a regular expression | ||||
# for actual splitting. We store it as an attribute mostly for ease of | ||||
# debugging, since this type of code can be so tricky to debug. | ||||
_delim_expr = None | ||||
# The regular expression that does the actual splitting | ||||
_delim_re = None | ||||
def __init__(self, delims=None): | ||||
delims = CompletionSplitter._delims if delims is None else delims | ||||
Fernando Perez
|
r6945 | self.delims = delims | ||
Fernando Perez
|
r2855 | |||
Fernando Perez
|
r6945 | @property | ||
def delims(self): | ||||
"""Return the string of delimiter characters.""" | ||||
return self._delims | ||||
@delims.setter | ||||
def delims(self, delims): | ||||
Fernando Perez
|
r2855 | """Set the delimiters for line splitting.""" | ||
expr = '[' + ''.join('\\'+ c for c in delims) + ']' | ||||
self._delim_re = re.compile(expr) | ||||
self._delims = delims | ||||
self._delim_expr = expr | ||||
def split_line(self, line, cursor_pos=None): | ||||
"""Split a line of text with a cursor at the given position. | ||||
""" | ||||
l = line if cursor_pos is None else line[:cursor_pos] | ||||
return self._delim_re.split(l)[-1] | ||||
MinRK
|
r4825 | class Completer(Configurable): | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4825 | greedy = CBool(False, config=True, | ||
help="""Activate greedy completion | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4825 | This will enable completion on elements of lists, results of function calls, etc., | ||
but can be unsafe because the code is actually evaluated on TAB. | ||||
""" | ||||
) | ||||
MinRK
|
r5231 | |||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r11064 | def __init__(self, namespace=None, global_namespace=None, **kwargs): | ||
ville
|
r988 | """Create a new completer for the command line. | ||
MinRK
|
r4825 | Completer(namespace=ns,global_namespace=ns2) -> completer instance. | ||
ville
|
r988 | |||
If unspecified, the default namespace where completions are performed | ||||
is __main__ (technically, __main__.__dict__). Namespaces should be | ||||
given as dictionaries. | ||||
An optional second namespace can be given. This allows the completer | ||||
to handle cases where both the local and global scopes need to be | ||||
distinguished. | ||||
Completer instances should be used as the completion mechanism of | ||||
readline via the set_completer() call: | ||||
readline.set_completer(Completer(my_namespace).complete) | ||||
""" | ||||
# Don't bind to namespace quite yet, but flag whether the user wants a | ||||
# specific namespace or to use __main__.__dict__. This will allow us | ||||
# to bind to __main__.__dict__ at completion time, not now. | ||||
if namespace is None: | ||||
self.use_main_ns = 1 | ||||
else: | ||||
self.use_main_ns = 0 | ||||
self.namespace = namespace | ||||
# The global namespace, if given, can be bound directly | ||||
if global_namespace is None: | ||||
self.global_namespace = {} | ||||
else: | ||||
self.global_namespace = global_namespace | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r11064 | super(Completer, self).__init__(**kwargs) | ||
ville
|
r988 | |||
def complete(self, text, state): | ||||
"""Return the next possible completion for 'text'. | ||||
This is called successively with state == 0, 1, 2, ... until it | ||||
returns None. The completion should begin with 'text'. | ||||
""" | ||||
if self.use_main_ns: | ||||
self.namespace = __main__.__dict__ | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | if state == 0: | ||
if "." in text: | ||||
self.matches = self.attr_matches(text) | ||||
else: | ||||
self.matches = self.global_matches(text) | ||||
try: | ||||
return self.matches[state] | ||||
except IndexError: | ||||
return None | ||||
def global_matches(self, text): | ||||
"""Compute matches when text is a simple name. | ||||
Return a list of all keywords, built-in functions and names currently | ||||
defined in self.namespace or self.global_namespace that match. | ||||
""" | ||||
Fernando Perez
|
r2365 | #print 'Completer->global_matches, txt=%r' % text # dbg | ||
ville
|
r988 | matches = [] | ||
match_append = matches.append | ||||
n = len(text) | ||||
for lst in [keyword.kwlist, | ||||
Thomas Kluyver
|
r13351 | builtin_mod.__dict__.keys(), | ||
ville
|
r988 | self.namespace.keys(), | ||
self.global_namespace.keys()]: | ||||
for word in lst: | ||||
if word[:n] == text and word != "__builtins__": | ||||
match_append(word) | ||||
return matches | ||||
def attr_matches(self, text): | ||||
"""Compute matches when text contains a dot. | ||||
Assuming the text is of the form NAME.NAME....[NAME], and is | ||||
evaluatable in self.namespace or self.global_namespace, it will be | ||||
evaluated and its attributes (as revealed by dir()) are used as | ||||
possible completions. (For class instances, class members are are | ||||
also considered.) | ||||
WARNING: this can still invoke arbitrary C code, if an object | ||||
with a __getattr__ hook is evaluated. | ||||
""" | ||||
macgyver
|
r4477 | #io.rprint('Completer->attr_matches, txt=%r' % text) # dbg | ||
ville
|
r988 | # Another option, seems to work great. Catches things like ''.<tab> | ||
m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text) | ||||
Tim Couper
|
r6309 | |||
macgyver
|
r4477 | if m: | ||
Bernardo B. Marques
|
r4872 | expr, attr = m.group(1, 3) | ||
MinRK
|
r4825 | elif self.greedy: | ||
macgyver
|
r4477 | m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer) | ||
if not m2: | ||||
return [] | ||||
expr, attr = m2.group(1,2) | ||||
MinRK
|
r4825 | else: | ||
return [] | ||||
Tim Couper
|
r6309 | |||
ville
|
r988 | try: | ||
obj = eval(expr, self.namespace) | ||||
except: | ||||
try: | ||||
obj = eval(expr, self.global_namespace) | ||||
except: | ||||
return [] | ||||
Tim Couper
|
r6308 | if self.limit_to__all__ and hasattr(obj, '__all__'): | ||
words = get__all__entries(obj) | ||||
else: | ||||
words = dir2(obj) | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | try: | ||
words = generics.complete_object(obj, words) | ||||
Brian Granger
|
r2205 | except TryNext: | ||
ville
|
r988 | pass | ||
Thomas Kluyver
|
r5155 | except Exception: | ||
# Silence errors from completion function | ||||
#raise # dbg | ||||
pass | ||||
ville
|
r988 | # Build match list to return | ||
n = len(attr) | ||||
res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ] | ||||
return res | ||||
Fernando Perez
|
r2365 | |||
Tim Couper
|
r6308 | def get__all__entries(obj): | ||
"""returns the strings in the __all__ attribute""" | ||||
try: | ||||
Fernando Perez
|
r6945 | words = getattr(obj, '__all__') | ||
Tim Couper
|
r6308 | except: | ||
return [] | ||||
Thomas Kluyver
|
r13353 | return [w for w in words if isinstance(w, string_types)] | ||
Tim Couper
|
r6308 | |||
Joel Nothman
|
r15766 | def match_dict_keys(keys, prefix): | ||
"""Used by dict_key_matches, matching the prefix to a list of keys""" | ||||
if not prefix: | ||||
MinRK
|
r16564 | return None, 0, [repr(k) for k in keys | ||
Joel Nothman
|
r15766 | if isinstance(k, (string_types, bytes))] | ||
quote_match = re.search('["\']', prefix) | ||||
quote = quote_match.group() | ||||
try: | ||||
prefix_str = eval(prefix + quote, {}) | ||||
except Exception: | ||||
MinRK
|
r16564 | return None, 0, [] | ||
token_match = re.search(r'\w*$', prefix, re.UNICODE) | ||||
token_start = token_match.start() | ||||
token_prefix = token_match.group() | ||||
Joel Nothman
|
r15766 | |||
# TODO: support bytes in Py3k | ||||
matched = [] | ||||
for key in keys: | ||||
try: | ||||
if not key.startswith(prefix_str): | ||||
continue | ||||
Joel Nothman
|
r16456 | except (AttributeError, TypeError, UnicodeError): | ||
Joel Nothman
|
r15766 | # Python 3+ TypeError on b'a'.startswith('a') or vice-versa | ||
continue | ||||
# reformat remainder of key to begin with prefix | ||||
rem = key[len(prefix_str):] | ||||
Joel Nothman
|
r16456 | # force repr wrapped in ' | ||
Joel Nothman
|
r15766 | rem_repr = repr(rem + '"') | ||
if rem_repr.startswith('u') and prefix[0] not in 'uU': | ||||
Joel Nothman
|
r16456 | # Found key is unicode, but prefix is Py2 string. | ||
# Therefore attempt to interpret key as string. | ||||
Joel Nothman
|
r15766 | try: | ||
rem_repr = repr(rem.encode('ascii') + '"') | ||||
except UnicodeEncodeError: | ||||
continue | ||||
rem_repr = rem_repr[1 + rem_repr.index("'"):-2] | ||||
if quote == '"': | ||||
Joel Nothman
|
r16456 | # The entered prefix is quoted with ", | ||
# but the match is quoted with '. | ||||
# A contained " hence needs escaping for comparison: | ||||
Joel Nothman
|
r15766 | rem_repr = rem_repr.replace('"', '\\"') | ||
# then reinsert prefix from start of token | ||||
matched.append('%s%s' % (token_prefix, rem_repr)) | ||||
MinRK
|
r16564 | return quote, token_start, matched | ||
Joel Nothman
|
r15766 | |||
Joel Nothman
|
r16468 | def _safe_isinstance(obj, module, class_name): | ||
"""Checks if obj is an instance of module.class_name if loaded | ||||
""" | ||||
return (module in sys.modules and | ||||
isinstance(obj, getattr(__import__(module), class_name))) | ||||
Joel Nothman
|
r15766 | |||
ville
|
r988 | class IPCompleter(Completer): | ||
"""Extension of the completer class with IPython-specific features""" | ||||
MinRK
|
r4825 | def _greedy_changed(self, name, old, new): | ||
"""update the splitter and readline delims when greedy is changed""" | ||||
if new: | ||||
Fernando Perez
|
r6945 | self.splitter.delims = GREEDY_DELIMS | ||
MinRK
|
r4825 | else: | ||
Fernando Perez
|
r6945 | self.splitter.delims = DELIMS | ||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4825 | if self.readline: | ||
Fernando Perez
|
r6945 | self.readline.set_completer_delims(self.splitter.delims) | ||
MinRK
|
r5231 | |||
merge_completions = CBool(True, config=True, | ||||
help="""Whether to merge completion results into a single list | ||||
If False, only the completion results from the first non-empty | ||||
completer will be returned. | ||||
""" | ||||
) | ||||
omit__names = Enum((0,1,2), default_value=2, config=True, | ||||
help="""Instruct the completer to omit private method names | ||||
Specifically, when completing on ``object.<tab>``. | ||||
When 2 [default]: all names that start with '_' will be excluded. | ||||
When 1: all 'magic' names (``__foo__``) will be excluded. | ||||
When 0: nothing will be excluded. | ||||
""" | ||||
) | ||||
Tim Couper
|
r6311 | limit_to__all__ = CBool(default_value=False, config=True, | ||
Tim Couper
|
r6308 | help="""Instruct the completer to use __all__ for the completion | ||
Specifically, when completing on ``object.<tab>``. | ||||
Tim Couper
|
r6311 | When True: only those names in obj.__all__ will be included. | ||
Tim Couper
|
r6308 | |||
Tim Couper
|
r6311 | When False [default]: the __all__ attribute is ignored | ||
Tim Couper
|
r6308 | """ | ||
) | ||||
Bernardo B. Marques
|
r4872 | |||
MinRK
|
r4825 | def __init__(self, shell=None, namespace=None, global_namespace=None, | ||
Thomas Kluyver
|
r12605 | use_readline=True, config=None, **kwargs): | ||
ville
|
r988 | """IPCompleter() -> completer | ||
Return a completer object suitable for use by the readline library | ||||
via readline.set_completer(). | ||||
Inputs: | ||||
- shell: a pointer to the ipython shell itself. This is needed | ||||
Thomas Kluyver
|
r12553 | because this completer knows about magic functions, and those can | ||
only be accessed via the ipython instance. | ||||
ville
|
r988 | |||
- namespace: an optional dict where completions are performed. | ||||
- global_namespace: secondary optional dict for completions, to | ||||
Thomas Kluyver
|
r12553 | handle cases (such as IPython embedded inside functions) where | ||
both Python scopes are visible. | ||||
ville
|
r988 | |||
Fernando Perez
|
r2839 | use_readline : bool, optional | ||
If true, use the readline library. This completer can still function | ||||
without readline, though in that case callers must provide some extra | ||||
information on each call about the current line.""" | ||||
ville
|
r988 | |||
Brian Granger
|
r2244 | self.magic_escape = ESC_MAGIC | ||
Fernando Perez
|
r2857 | self.splitter = CompletionSplitter() | ||
Fernando Perez
|
r2956 | # Readline configuration, only used by the rlcompleter method. | ||
Fernando Perez
|
r2839 | if use_readline: | ||
Bernardo B. Marques
|
r4872 | # We store the right version of readline so that later code | ||
Fernando Perez
|
r2839 | import IPython.utils.rlineimpl as readline | ||
self.readline = readline | ||||
Fernando Perez
|
r2956 | else: | ||
self.readline = None | ||||
Fernando Perez
|
r2839 | |||
MinRK
|
r4825 | # _greedy_changed() depends on splitter and readline being defined: | ||
Completer.__init__(self, namespace=namespace, global_namespace=global_namespace, | ||||
MinRK
|
r5231 | config=config, **kwargs) | ||
MinRK
|
r4825 | |||
Fernando Perez
|
r2839 | # List where completion matches will be stored | ||
self.matches = [] | ||||
Fernando Perez
|
r6906 | self.shell = shell | ||
ville
|
r988 | # Regexp to split filenames with spaces in them | ||
self.space_name_re = re.compile(r'([^\\] )') | ||||
# Hold a local ref. to glob.glob for speed | ||||
self.glob = glob.glob | ||||
# Determine if we are running on 'dumb' terminals, like (X)Emacs | ||||
# buffers, to avoid completion problems. | ||||
term = os.environ.get('TERM','xterm') | ||||
self.dumb_terminal = term in ['dumb','emacs'] | ||||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # Special handling of backslashes needed in win32 platforms | ||
if sys.platform == "win32": | ||||
self.clean_glob = self._clean_glob_win32 | ||||
else: | ||||
self.clean_glob = self._clean_glob | ||||
Fernando Perez
|
r2365 | |||
Piti Ongmongkolkul
|
r9169 | #regexp to parse docstring for function signature | ||
self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') | ||||
self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') | ||||
#use this if positional argument name is also needed | ||||
#= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)') | ||||
Fernando Perez
|
r2365 | # All active matcher routines for completion | ||
ville
|
r988 | self.matchers = [self.python_matches, | ||
self.file_matches, | ||||
Fernando Perez
|
r2365 | self.magic_matches, | ||
Fernando Perez
|
r2839 | self.python_func_kw_matches, | ||
Joel Nothman
|
r15766 | self.dict_key_matches, | ||
Fernando Perez
|
r2839 | ] | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | def all_completions(self, text): | ||
andy wilson
|
r3430 | """ | ||
Wrapper around the complete method for the benefit of emacs | ||||
and pydb. | ||||
""" | ||||
return self.complete(text)[1] | ||||
ville
|
r988 | |||
def _clean_glob(self,text): | ||||
return self.glob("%s*" % text) | ||||
def _clean_glob_win32(self,text): | ||||
return [f.replace("\\","/") | ||||
Bernardo B. Marques
|
r4872 | for f in self.glob("%s*" % text)] | ||
ville
|
r988 | |||
def file_matches(self, text): | ||||
"""Match filenames, expanding ~USER type strings. | ||||
Most of the seemingly convoluted logic in this completer is an | ||||
attempt to handle filenames with spaces in them. And yet it's not | ||||
quite perfect, because Python's readline doesn't expose all of the | ||||
GNU readline details needed for this to be done correctly. | ||||
For a filename with a space in it, the printed completions will be | ||||
only the parts after what's already been typed (instead of the | ||||
full completions, as is normally done). I don't think with the | ||||
current (as of Python 2.3) Python readline it's possible to do | ||||
better.""" | ||||
Fernando Perez
|
r3074 | #io.rprint('Completer->file_matches: <%r>' % text) # dbg | ||
ville
|
r988 | |||
# chars that require escaping with backslash - i.e. chars | ||||
# that readline treats incorrectly as delimiters, but we | ||||
# don't want to treat as delimiters in filename matching | ||||
# when escaped with backslash | ||||
if text.startswith('!'): | ||||
text = text[1:] | ||||
text_prefix = '!' | ||||
else: | ||||
text_prefix = '' | ||||
Fernando Perez
|
r3184 | |||
Fernando Perez
|
r2956 | text_until_cursor = self.text_until_cursor | ||
Fernando Perez
|
r3184 | # track strings with open quotes | ||
open_quotes = has_open_quotes(text_until_cursor) | ||||
if '(' in text_until_cursor or '[' in text_until_cursor: | ||||
lsplit = text | ||||
else: | ||||
try: | ||||
# arg_split ~ shlex.split, but with unicode bugs fixed by us | ||||
lsplit = arg_split(text_until_cursor)[-1] | ||||
except ValueError: | ||||
# typically an unmatched ", or backslash without escaped char. | ||||
if open_quotes: | ||||
lsplit = text_until_cursor.split(open_quotes)[-1] | ||||
else: | ||||
return [] | ||||
except IndexError: | ||||
# tab pressed on empty line | ||||
lsplit = "" | ||||
ville
|
r988 | |||
Fernando Perez
|
r2903 | if not open_quotes and lsplit != protect_filename(lsplit): | ||
Fernando Perez
|
r3184 | # if protectables are found, do matching on the whole escaped name | ||
has_protectables = True | ||||
ville
|
r988 | text0,text = text,lsplit | ||
else: | ||||
Fernando Perez
|
r3184 | has_protectables = False | ||
ville
|
r988 | text = os.path.expanduser(text) | ||
if text == "": | ||||
return [text_prefix + protect_filename(f) for f in self.glob("*")] | ||||
Fernando Perez
|
r3184 | # Compute the matches from the filesystem | ||
ville
|
r988 | m0 = self.clean_glob(text.replace('\\','')) | ||
Fernando Perez
|
r3184 | |||
ville
|
r988 | if has_protectables: | ||
# If we had protectables, we need to revert our changes to the | ||||
# beginning of filename so that we don't double-write the part | ||||
# of the filename we have so far | ||||
len_lsplit = len(lsplit) | ||||
Bernardo B. Marques
|
r4872 | matches = [text_prefix + text0 + | ||
ville
|
r988 | protect_filename(f[len_lsplit:]) for f in m0] | ||
else: | ||||
if open_quotes: | ||||
# if we have a string with an open quote, we don't need to | ||||
# protect the names at all (and we _shouldn't_, as it | ||||
# would cause bugs when the filesystem call is made). | ||||
matches = m0 | ||||
else: | ||||
Bernardo B. Marques
|
r4872 | matches = [text_prefix + | ||
ville
|
r988 | protect_filename(f) for f in m0] | ||
Fernando Perez
|
r2903 | #io.rprint('mm', matches) # dbg | ||
Bradley M. Froehle
|
r5766 | |||
# Mark directories in input list by appending '/' to their names. | ||||
matches = [x+'/' if os.path.isdir(x) else x for x in matches] | ||||
return matches | ||||
ville
|
r988 | |||
Fernando Perez
|
r2365 | def magic_matches(self, text): | ||
"""Match magics""" | ||||
Fernando Perez
|
r2956 | #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg | ||
Fernando Perez
|
r2365 | # Get all shell magics now rather than statically, so magics loaded at | ||
Fernando Perez
|
r6991 | # runtime show up too. | ||
lsm = self.shell.magics_manager.lsmagic() | ||||
line_magics = lsm['line'] | ||||
cell_magics = lsm['cell'] | ||||
Fernando Perez
|
r2365 | pre = self.magic_escape | ||
Fernando Perez
|
r6991 | pre2 = pre+pre | ||
# Completion logic: | ||||
# - user gives %%: only do cell magics | ||||
# - user gives %: do both line and cell magics | ||||
# - no prefix: do both | ||||
# In other words, line magics are skipped if the user gives %% explicitly | ||||
bare_text = text.lstrip(pre) | ||||
comp = [ pre2+m for m in cell_magics if m.startswith(bare_text)] | ||||
if not text.startswith(pre2): | ||||
comp += [ pre+m for m in line_magics if m.startswith(bare_text)] | ||||
return comp | ||||
Fernando Perez
|
r2365 | |||
ville
|
r988 | def python_matches(self,text): | ||
"""Match attributes or global python names""" | ||||
Tim Couper
|
r6308 | |||
macgyver
|
r4477 | #io.rprint('Completer->python_matches, txt=%r' % text) # dbg | ||
ville
|
r988 | if "." in text: | ||
try: | ||||
matches = self.attr_matches(text) | ||||
if text.endswith('.') and self.omit__names: | ||||
if self.omit__names == 1: | ||||
# true if txt is _not_ a __ name, false otherwise: | ||||
no__name = (lambda txt: | ||||
re.match(r'.*\.__.*?__',txt) is None) | ||||
else: | ||||
# true if txt is _not_ a _ name, false otherwise: | ||||
no__name = (lambda txt: | ||||
Paul Ivanov
|
r17755 | re.match(r'\._.*?',txt[txt.rindex('.'):]) is None) | ||
ville
|
r988 | matches = filter(no__name, matches) | ||
except NameError: | ||||
# catches <undefined attributes>.<tab> | ||||
matches = [] | ||||
else: | ||||
matches = self.global_matches(text) | ||||
Fernando Perez
|
r2365 | |||
ville
|
r988 | return matches | ||
Piti Ongmongkolkul
|
r9171 | def _default_arguments_from_docstring(self, doc): | ||
Piti Ongmongkolkul
|
r9173 | """Parse the first line of docstring for call signature. | ||
Piti Ongmongkolkul
|
r9171 | |||
Docstring should be of the form 'min(iterable[, key=func])\n'. | ||||
Piti Ongmongkolkul
|
r9172 | It can also parse cython docstring of the form | ||
'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'. | ||||
Piti Ongmongkolkul
|
r9166 | """ | ||
Piti Ongmongkolkul
|
r9169 | if doc is None: | ||
return [] | ||||
Piti Ongmongkolkul
|
r9173 | |||
Piti Ongmongkolkul
|
r9165 | #care only the firstline | ||
Piti Ongmongkolkul
|
r9173 | line = doc.lstrip().splitlines()[0] | ||
Piti Ongmongkolkul
|
r9169 | #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') | ||
Piti Ongmongkolkul
|
r9165 | #'min(iterable[, key=func])\n' -> 'iterable[, key=func]' | ||
Piti Ongmongkolkul
|
r9169 | sig = self.docstring_sig_re.search(line) | ||
if sig is None: | ||||
return [] | ||||
Piti Ongmongkolkul
|
r9165 | # iterable[, key=func]' -> ['iterable[' ,' key=func]'] | ||
sig = sig.groups()[0].split(',') | ||||
ret = [] | ||||
for s in sig: | ||||
Piti Ongmongkolkul
|
r9169 | #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') | ||
ret += self.docstring_kwd_re.findall(s) | ||||
Piti Ongmongkolkul
|
r9165 | return ret | ||
ville
|
r988 | def _default_arguments(self, obj): | ||
"""Return the list of default arguments of obj if it is callable, | ||||
or empty list otherwise.""" | ||||
Piti Ongmongkolkul
|
r9165 | call_obj = obj | ||
ret = [] | ||||
if inspect.isbuiltin(obj): | ||||
Piti Ongmongkolkul
|
r9167 | pass | ||
Piti Ongmongkolkul
|
r9165 | elif not (inspect.isfunction(obj) or inspect.ismethod(obj)): | ||
ville
|
r988 | if inspect.isclass(obj): | ||
Piti Ongmongkolkul
|
r9168 | #for cython embededsignature=True the constructor docstring | ||
#belongs to the object itself not __init__ | ||||
ret += self._default_arguments_from_docstring( | ||||
Piti Ongmongkolkul
|
r9169 | getattr(obj, '__doc__', '')) | ||
Piti Ongmongkolkul
|
r9167 | # for classes, check for __init__,__new__ | ||
Piti Ongmongkolkul
|
r9169 | call_obj = (getattr(obj, '__init__', None) or | ||
getattr(obj, '__new__', None)) | ||||
ville
|
r988 | # for all others, check if they are __call__able | ||
elif hasattr(obj, '__call__'): | ||||
Piti Ongmongkolkul
|
r9165 | call_obj = obj.__call__ | ||
Piti Ongmongkolkul
|
r9167 | ret += self._default_arguments_from_docstring( | ||
Piti Ongmongkolkul
|
r9169 | getattr(call_obj, '__doc__', '')) | ||
Piti Ongmongkolkul
|
r9167 | |||
ville
|
r988 | try: | ||
Piti Ongmongkolkul
|
r9165 | args,_,_1,defaults = inspect.getargspec(call_obj) | ||
ville
|
r988 | if defaults: | ||
Piti Ongmongkolkul
|
r9165 | ret+=args[-len(defaults):] | ||
except TypeError: | ||||
pass | ||||
return list(set(ret)) | ||||
ville
|
r988 | |||
def python_func_kw_matches(self,text): | ||||
"""Match named parameters (kwargs) of the last open function""" | ||||
Piti Ongmongkolkul
|
r9165 | |||
ville
|
r988 | if "." in text: # a parameter cannot be dotted | ||
return [] | ||||
try: regexp = self.__funcParamsRegex | ||||
except AttributeError: | ||||
regexp = self.__funcParamsRegex = re.compile(r''' | ||||
Jez Ng
|
r7507 | '.*?(?<!\\)' | # single quoted strings or | ||
".*?(?<!\\)" | # double quoted strings or | ||||
\w+ | # identifier | ||||
\S # other characters | ||||
ville
|
r988 | ''', re.VERBOSE | re.DOTALL) | ||
# 1. find the nearest identifier that comes before an unclosed | ||||
piti118
|
r6510 | # parenthesis before the cursor | ||
# e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo" | ||||
tokens = regexp.findall(self.text_until_cursor) | ||||
ville
|
r988 | tokens.reverse() | ||
iterTokens = iter(tokens); openPar = 0 | ||||
Piti Ongmongkolkul
|
r9165 | |||
ville
|
r988 | for token in iterTokens: | ||
if token == ')': | ||||
openPar -= 1 | ||||
elif token == '(': | ||||
openPar += 1 | ||||
if openPar > 0: | ||||
# found the last unclosed parenthesis | ||||
break | ||||
else: | ||||
return [] | ||||
# 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) | ||||
ids = [] | ||||
isId = re.compile(r'\w+$').match | ||||
Piti Ongmongkolkul
|
r9165 | |||
ville
|
r988 | while True: | ||
try: | ||||
Bradley M. Froehle
|
r7847 | ids.append(next(iterTokens)) | ||
ville
|
r988 | if not isId(ids[-1]): | ||
ids.pop(); break | ||||
Bradley M. Froehle
|
r7847 | if not next(iterTokens) == '.': | ||
ville
|
r988 | break | ||
except StopIteration: | ||||
break | ||||
# lookup the candidate callable matches either using global_matches | ||||
# or attr_matches for dotted names | ||||
if len(ids) == 1: | ||||
callableMatches = self.global_matches(ids[0]) | ||||
else: | ||||
callableMatches = self.attr_matches('.'.join(ids[::-1])) | ||||
argMatches = [] | ||||
for callableMatch in callableMatches: | ||||
Fernando Perez
|
r2365 | try: | ||
namedArgs = self._default_arguments(eval(callableMatch, | ||||
Piti Ongmongkolkul
|
r9166 | self.namespace)) | ||
Fernando Perez
|
r2365 | except: | ||
continue | ||||
Piti Ongmongkolkul
|
r9165 | |||
ville
|
r988 | for namedArg in namedArgs: | ||
if namedArg.startswith(text): | ||||
argMatches.append("%s=" %namedArg) | ||||
return argMatches | ||||
Joel Nothman
|
r15766 | def dict_key_matches(self, text): | ||
Thomas Kluyver
|
r17138 | "Match string keys in a dictionary, after e.g. 'foo[' " | ||
Joel Nothman
|
r15766 | def get_keys(obj): | ||
Joel Nothman
|
r16466 | # Only allow completion for known in-memory dict-like types | ||
Joel Nothman
|
r16468 | if isinstance(obj, dict) or\ | ||
_safe_isinstance(obj, 'pandas', 'DataFrame'): | ||||
Joel Nothman
|
r15766 | try: | ||
return list(obj.keys()) | ||||
except Exception: | ||||
return [] | ||||
mbyt
|
r19790 | elif _safe_isinstance(obj, 'numpy', 'ndarray') or\ | ||
_safe_isinstance(obj, 'numpy', 'void'): | ||||
Joel Nothman
|
r16466 | return obj.dtype.names or [] | ||
return [] | ||||
Joel Nothman
|
r15766 | |||
try: | ||||
regexps = self.__dict_key_regexps | ||||
except AttributeError: | ||||
dict_key_re_fmt = r'''(?x) | ||||
( # match dict-referring expression wrt greedy setting | ||||
%s | ||||
) | ||||
\[ # open bracket | ||||
\s* # and optional whitespace | ||||
([uUbB]? # string prefix (r not handled) | ||||
(?: # unclosed string | ||||
'(?:[^']|(?<!\\)\\')* | ||||
| | ||||
"(?:[^"]|(?<!\\)\\")* | ||||
) | ||||
)? | ||||
$ | ||||
''' | ||||
regexps = self.__dict_key_regexps = { | ||||
False: re.compile(dict_key_re_fmt % ''' | ||||
# identifiers separated by . | ||||
(?!\d)\w+ | ||||
(?:\.(?!\d)\w+)* | ||||
'''), | ||||
True: re.compile(dict_key_re_fmt % ''' | ||||
.+ | ||||
''') | ||||
} | ||||
match = regexps[self.greedy].search(self.text_until_cursor) | ||||
if match is None: | ||||
return [] | ||||
expr, prefix = match.groups() | ||||
try: | ||||
obj = eval(expr, self.namespace) | ||||
except Exception: | ||||
try: | ||||
obj = eval(expr, self.global_namespace) | ||||
except Exception: | ||||
return [] | ||||
keys = get_keys(obj) | ||||
if not keys: | ||||
return keys | ||||
MinRK
|
r16564 | closing_quote, token_offset, matches = match_dict_keys(keys, prefix) | ||
if not matches: | ||||
return matches | ||||
# get the cursor position of | ||||
# - the text being completed | ||||
# - the start of the key text | ||||
# - the start of the completion | ||||
text_start = len(self.text_until_cursor) - len(text) | ||||
if prefix: | ||||
key_start = match.start(2) | ||||
completion_start = key_start + token_offset | ||||
else: | ||||
key_start = completion_start = match.end() | ||||
# grab the leading prefix, to make sure all completions start with `text` | ||||
if text_start > key_start: | ||||
leading = '' | ||||
else: | ||||
leading = text[text_start:completion_start] | ||||
# the index of the `[` character | ||||
bracket_idx = match.end(1) | ||||
Joel Nothman
|
r15766 | |||
# append closing quote and bracket as appropriate | ||||
MinRK
|
r16564 | # this is *not* appropriate if the opening quote or bracket is outside | ||
# the text given to this method | ||||
suf = '' | ||||
Joel Nothman
|
r15766 | continuation = self.line_buffer[len(self.text_until_cursor):] | ||
MinRK
|
r16564 | if key_start > text_start and closing_quote: | ||
# quotes were opened inside text, maybe close them | ||||
if continuation.startswith(closing_quote): | ||||
continuation = continuation[len(closing_quote):] | ||||
else: | ||||
suf += closing_quote | ||||
if bracket_idx > text_start: | ||||
# brackets were opened inside text, maybe close them | ||||
if not continuation.startswith(']'): | ||||
suf += ']' | ||||
return [leading + k + suf for k in matches] | ||||
Joel Nothman
|
r15766 | |||
Brian E. Granger
|
r17700 | def latex_matches(self, text): | ||
Thomas Kluyver
|
r17810 | u"""Match Latex syntax for unicode characters. | ||
This does both \\alp -> \\alpha and \\alpha -> α | ||||
Used on Python 3 only. | ||||
""" | ||||
Brian E. Granger
|
r17738 | slashpos = text.rfind('\\') | ||
if slashpos > -1: | ||||
s = text[slashpos:] | ||||
if s in latex_symbols: | ||||
Brian E. Granger
|
r17740 | # Try to complete a full latex symbol to unicode | ||
# \\alpha -> α | ||||
Brian E. Granger
|
r17738 | return s, [latex_symbols[s]] | ||
Brian E. Granger
|
r17740 | else: | ||
# If a user has partially typed a latex symbol, give them | ||||
# a full list of options \al -> [\aleph, \alpha] | ||||
matches = [k for k in latex_symbols if k.startswith(s)] | ||||
return s, matches | ||||
Brian E. Granger
|
r17738 | return u'', [] | ||
Brian E. Granger
|
r17700 | |||
Fernando Perez
|
r2959 | def dispatch_custom_completer(self, text): | ||
macgyver
|
r4477 | #io.rprint("Custom! '%s' %s" % (text, self.custom_completers)) # dbg | ||
Bernardo B. Marques
|
r4872 | line = self.line_buffer | ||
ville
|
r988 | if not line.strip(): | ||
return None | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2959 | # Create a little structure to pass all the relevant information about | ||
# the current completion to any custom completer. | ||||
Fernando Perez
|
r2365 | event = Bunch() | ||
ville
|
r988 | event.line = line | ||
event.symbol = text | ||||
cmd = line.split(None,1)[0] | ||||
event.command = cmd | ||||
Fernando Perez
|
r2959 | event.text_until_cursor = self.text_until_cursor | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | #print "\ncustom:{%s]\n" % event # dbg | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | # for foo etc, try also to find completer for %foo | ||
if not cmd.startswith(self.magic_escape): | ||||
try_magic = self.custom_completers.s_matches( | ||||
Bernardo B. Marques
|
r4872 | self.magic_escape + cmd) | ||
ville
|
r988 | else: | ||
Bernardo B. Marques
|
r4872 | try_magic = [] | ||
Fernando Perez
|
r2365 | for c in itertools.chain(self.custom_completers.s_matches(cmd), | ||
Fernando Perez
|
r2956 | try_magic, | ||
self.custom_completers.flat_matches(self.text_until_cursor)): | ||||
ville
|
r988 | #print "try",c # dbg | ||
try: | ||||
res = c(event) | ||||
Fernando Perez
|
r3064 | if res: | ||
# first, try case sensitive match | ||||
withcase = [r for r in res if r.startswith(text)] | ||||
if withcase: | ||||
return withcase | ||||
# if none, then case insensitive ones are ok too | ||||
text_low = text.lower() | ||||
return [r for r in res if r.lower().startswith(text_low)] | ||||
Brian Granger
|
r2205 | except TryNext: | ||
ville
|
r988 | pass | ||
Bernardo B. Marques
|
r4872 | |||
ville
|
r988 | return None | ||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2857 | def complete(self, text=None, line_buffer=None, cursor_pos=None): | ||
Fernando Perez
|
r3184 | """Find completions for the given text and line context. | ||
ville
|
r988 | |||
Fernando Perez
|
r2857 | Note that both the text and the line_buffer are optional, but at least | ||
one of them must be given. | ||||
Fernando Perez
|
r2839 | Parameters | ||
---------- | ||||
Fernando Perez
|
r2857 | text : string, optional | ||
Text to perform the completion on. If not given, the line buffer | ||||
is split using the instance's CompletionSplitter object. | ||||
Fernando Perez
|
r2839 | |||
line_buffer : string, optional | ||||
If not given, the completer attempts to obtain the current line | ||||
buffer via readline. This keyword allows clients which are | ||||
requesting for text completions in non-readline contexts to inform | ||||
the completer of the entire text. | ||||
cursor_pos : int, optional | ||||
Index of the cursor in the full line buffer. Should be provided by | ||||
remote frontends where kernel has no access to frontend state. | ||||
Fernando Perez
|
r3184 | |||
Returns | ||||
------- | ||||
text : str | ||||
Text that was actually used in the completion. | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r3184 | matches : list | ||
A list of completion matches. | ||||
ville
|
r988 | """ | ||
Brian E. Granger
|
r17738 | # io.rprint('\nCOMP1 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg | ||
Fernando Perez
|
r2857 | |||
# if the cursor position isn't given, the only sane assumption we can | ||||
# make is that it's at the end of the line (the common case) | ||||
if cursor_pos is None: | ||||
cursor_pos = len(line_buffer) if text is None else len(text) | ||||
Brian E. Granger
|
r17739 | if PY3: | ||
latex_text = text if not line_buffer else line_buffer[:cursor_pos] | ||||
latex_text, latex_matches = self.latex_matches(latex_text) | ||||
if latex_matches: | ||||
return latex_text, latex_matches | ||||
Brian E. Granger
|
r17738 | |||
Fernando Perez
|
r2857 | # if text is either None or an empty string, rely on the line buffer | ||
if not text: | ||||
text = self.splitter.split_line(line_buffer, cursor_pos) | ||||
# If no line buffer is given, assume the input text is all there was | ||||
if line_buffer is None: | ||||
line_buffer = text | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2956 | self.line_buffer = line_buffer | ||
self.text_until_cursor = self.line_buffer[:cursor_pos] | ||||
Brian E. Granger
|
r17738 | # io.rprint('COMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg | ||
Fernando Perez
|
r2857 | |||
Fernando Perez
|
r2839 | # Start with a clean slate of completions | ||
self.matches[:] = [] | ||||
custom_res = self.dispatch_custom_completer(text) | ||||
if custom_res is not None: | ||||
# did custom completers produce something? | ||||
self.matches = custom_res | ||||
ville
|
r988 | else: | ||
Fernando Perez
|
r2839 | # Extend the list of completions with the results of each | ||
# matcher, so we return results to the user from all | ||||
# namespaces. | ||||
if self.merge_completions: | ||||
self.matches = [] | ||||
for matcher in self.matchers: | ||||
Fernando Perez
|
r3073 | try: | ||
self.matches.extend(matcher(text)) | ||||
except: | ||||
# Show the ugly traceback if the matcher causes an | ||||
# exception, but do NOT crash the kernel! | ||||
Fernando Perez
|
r3184 | sys.excepthook(*sys.exc_info()) | ||
Fernando Perez
|
r2839 | else: | ||
for matcher in self.matchers: | ||||
self.matches = matcher(text) | ||||
if self.matches: | ||||
break | ||||
# FIXME: we should extend our api to return a dict with completions for | ||||
# different types of objects. The rlcomplete() method could then | ||||
# simply collapse the dict into a list for readline, but we'd have | ||||
# richer completion semantics in other evironments. | ||||
David P. Sanders
|
r13343 | |||
# use penalize_magics_key to put magics after variables with same name | ||||
self.matches = sorted(set(self.matches), key=penalize_magics_key) | ||||
Fernando Perez
|
r2857 | #io.rprint('COMP TEXT, MATCHES: %r, %r' % (text, self.matches)) # dbg | ||
return text, self.matches | ||||
Fernando Perez
|
r2839 | |||
def rlcomplete(self, text, state): | ||||
"""Return the state-th possible completion for 'text'. | ||||
ville
|
r988 | |||
Fernando Perez
|
r2839 | This is called successively with state == 0, 1, 2, ... until it | ||
returns None. The completion should begin with 'text'. | ||||
ville
|
r988 | |||
Fernando Perez
|
r2839 | Parameters | ||
---------- | ||||
text : string | ||||
Text to perform the completion on. | ||||
state : int | ||||
Counter used by readline. | ||||
""" | ||||
if state==0: | ||||
Fernando Perez
|
r2855 | |||
Fernando Perez
|
r2956 | self.line_buffer = line_buffer = self.readline.get_line_buffer() | ||
cursor_pos = self.readline.get_endidx() | ||||
Fernando Perez
|
r2839 | |||
Fernando Perez
|
r2855 | #io.rprint("\nRLCOMPLETE: %r %r %r" % | ||
# (text, line_buffer, cursor_pos) ) # dbg | ||||
Fernando Perez
|
r2839 | # if there is only a tab on a line with only whitespace, instead of | ||
# the mostly useless 'do you want to see all million completions' | ||||
# message, just do the right thing and give the user his tab! | ||||
# Incidentally, this enables pasting of tabbed text from an editor | ||||
# (as long as autoindent is off). | ||||
# It should be noted that at least pyreadline still shows file | ||||
# completions - is there a way around it? | ||||
# don't apply this on 'dumb' terminals, such as emacs buffers, so | ||||
# we don't interfere with their own tab-completion mechanism. | ||||
Fernando Perez
|
r2855 | if not (self.dumb_terminal or line_buffer.strip()): | ||
Fernando Perez
|
r2839 | self.readline.insert_text('\t') | ||
sys.stdout.flush() | ||||
ville
|
r988 | return None | ||
Fernando Perez
|
r2839 | |||
Fernando Perez
|
r2959 | # Note: debugging exceptions that may occur in completion is very | ||
# tricky, because readline unconditionally silences them. So if | ||||
# during development you suspect a bug in the completion code, turn | ||||
# this flag on temporarily by uncommenting the second form (don't | ||||
# flip the value in the first line, as the '# dbg' marker can be | ||||
# automatically detected and is used elsewhere). | ||||
DEBUG = False | ||||
#DEBUG = True # dbg | ||||
if DEBUG: | ||||
try: | ||||
self.complete(text, line_buffer, cursor_pos) | ||||
except: | ||||
import traceback; traceback.print_exc() | ||||
else: | ||||
# The normal production version is here | ||||
Bernardo B. Marques
|
r4872 | |||
Fernando Perez
|
r2959 | # This method computes the self.matches array | ||
self.complete(text, line_buffer, cursor_pos) | ||||
Fernando Perez
|
r2839 | |||
try: | ||||
return self.matches[state] | ||||
except IndexError: | ||||
ville
|
r988 | return None | ||