diff --git a/docs/conf.py b/docs/conf.py index d7a2815b..3c46b089 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,7 +28,7 @@ sys.path.append(os.path.abspath('_themes')) # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx.ext.todo', - 'sphinx.ext.inheritance_diagram'] + 'sphinx.ext.intersphinx', 'sphinx.ext.inheritance_diagram'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -270,3 +270,10 @@ autoclass_content = 'both' autodoc_member_order = 'bysource' autodoc_default_flags = [] #autodoc_default_flags = ['members', 'undoc-members'] + + +# -- Options for intersphinx module -------------------------------------------- + +intersphinx_mapping = { + 'http://docs.python.org/': None, +} diff --git a/docs/docs/repl.rst b/docs/docs/repl.rst new file mode 100644 index 00000000..e911e46e --- /dev/null +++ b/docs/docs/repl.rst @@ -0,0 +1,8 @@ +.. include:: ../global.rst + +How to use Jedi from Python interpreter +======================================= + +.. automodule:: jedi.replstartup + +.. autofunction:: jedi.utils.setup_readline diff --git a/docs/index.rst b/docs/index.rst index c8e518ea..9f53f667 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,6 +22,7 @@ Docs docs/installation docs/features + docs/repl docs/recipes docs/plugin-api docs/history diff --git a/jedi/utils.py b/jedi/utils.py index eb617fe7..37aa2225 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -2,40 +2,109 @@ Utilities for end-users. """ -import sys +from rlcompleter import Completer from jedi import Interpreter -def readline_complete(text, state): - """ - Function to be passed to :func:`readline.set_completer`. +_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. +""" - Usage:: +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)) - import readline - readline.set_completer(readline_complete) + +class JediRLCompleter(Completer): """ - ns = vars(sys.modules['__main__']) - completions = Interpreter(text, [ns]).completions() - try: - return text + completions[state].complete - except IndexError: - return None + :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`. + + This function setups :mod:`readline` to use Jedi in Python interactive + shell. If you want to use custom ``PYTHONSTARTUP`` file, you can call + this function like this: + + >>> from jedi.utils import setup_readline + >>> setup_readline() + """ try: import readline except ImportError: print("Module readline not available.") else: - readline.set_completer(readline_complete) + readline.set_completer(JediRLCompleter().complete) readline.parse_and_bind("tab: complete") - - # Default delimiters minus "()": - readline.set_completer_delims(' \t\n`~!@#$%^&*-=+[{]}\\|;:\'",<>/?') + readline.set_completer_delims(_READLINE_JEDI_DELIMS)