simplify repl completion support, only use the jedi one, because the builtin completion (rlcompleter) is not context sensitive and makes the whole thing really complicated.

This commit is contained in:
David Halter
2013-07-31 17:16:12 +02:00
parent ea2f9ebd4c
commit ae6dc782da

View File

@@ -2,92 +2,12 @@
Utilities for end-users.
"""
from rlcompleter import Completer
from __future__ import absolute_import
import __main__
from jedi import Interpreter
_NON_DELIMS = ' \t\n()'
"""
:class:`rcompleter.Completer` assumes these characters to be delimiter
(i.e., :meth:`rcompleter.Completer.complete` does not expect these
characters) but :class:`JediRLCompleter` can handle them.
"""
try:
import readline
except ImportError:
pass
else:
_READLINE_DEFAULT_DELIMS = readline.get_completer_delims()
_READLINE_JEDI_DELIMS = ''.join(
set(_READLINE_DEFAULT_DELIMS) - set(_NON_DELIMS))
class JediRLCompleter(Completer):
"""
:class:`rlcompleter.Completer` enhanced by Jedi.
This class tries matchers defined in :class:`.Completer` first.
If they fail, :class:`jedi.Interpreter` is used.
>>> import os
>>> completer = JediRLCompleter(locals())
>>> completer.complete('os.path.joi', 0) # completion w/o Jedi
'os.path.join('
>>> completer.complete('os.path.join().s', 0) # completion with Jedi
'os.path.join().split'
"""
def _jedi_matches(self, text):
completions = Interpreter(text, [self.namespace]).completions()
return [text + c.complete for c in completions]
@staticmethod
def _split_for_default_matcher(text, delims=_NON_DELIMS):
"""
Split `text` before passing it to :meth:`Completer.attr_matches` etc.
>>> JediRLCompleter._split_for_default_matcher('f(')
('f(', '')
>>> JediRLCompleter._split_for_default_matcher('f().g')
('f()', '.g')
"""
import re
m = re.match(r"(.*[{0}])([^{0}]*)".format(re.escape(delims)), text)
if not m:
return ('', text)
return m.groups()
def _find_matches(self, default_matcher, text):
"""
Common part for :meth:`attr_matches` and :meth:`global_matches`.
Try `default_matcher` first and return what it returns if
it is not empty. Otherwise, try :meth:`_jedi_matches`.
:arg default_matcher: :meth:`.Completer.attr_matches` or
:meth:`.Completer.global_matches`.
:arg str text: code to complete
"""
(pre, body) = self._split_for_default_matcher(text)
matches = default_matcher(self, body)
if matches:
return [pre + m for m in matches]
return self._jedi_matches(text)
def attr_matches(self, text):
# NOTE: Completer is old type class so `super` cannot be used here
return self._find_matches(Completer.attr_matches, text)
def global_matches(self, text):
# NOTE: Completer is old type class so `super` cannot be used here
return self._find_matches(Completer.global_matches, text)
def setup_readline():
"""
Install Jedi completer to :mod:`readline`.
@@ -105,6 +25,28 @@ def setup_readline():
except ImportError:
print("Module readline not available.")
else:
readline.set_completer(JediRLCompleter().complete)
class JediRL():
def complete(self, text, state):
"""
This complete stuff is pretty weird, a generator would make
a lot more sense, but probably due to backwards compatibility
this is still the way how it works.
The only important part is the ``Interpreter`` call, everything
else hsa been copied from the ``rlcompleter`` std. library
module.
"""
if state == 0:
namespace = __main__.__dict__
completions = Interpreter(text, [namespace]).completions()
self.matches = [text + c.complete for c in completions]
try:
return self.matches[state]
except IndexError:
return None
readline.set_completer(JediRL().get_item)
readline.parse_and_bind("tab: complete")
readline.set_completer_delims(_READLINE_JEDI_DELIMS)
# No delimiters, Jedi handles that.
readline.set_completer_delims('')