From 7a8ea56b058a7fbb3ea2590b24e9dd679e34bc99 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 18 May 2013 23:01:21 +0200 Subject: [PATCH 1/7] Document jedi.replstartup usage --- docs/docs/repl.rst | 8 ++++++++ docs/index.rst | 1 + 2 files changed, 9 insertions(+) create mode 100644 docs/docs/repl.rst 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 From 134dab174d0891ee0f4b5d71507dc78bc4e1153e Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 18 May 2013 23:05:15 +0200 Subject: [PATCH 2/7] Setup intersphinx module for linking Python doc For example, :mod:`readline` links to the corresponding page. --- docs/conf.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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, +} From 27f8b342e1a4bea9c807b005d16f932880bb7136 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 18 May 2013 23:41:15 +0200 Subject: [PATCH 3/7] Document utils.setup_readline --- jedi/utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/jedi/utils.py b/jedi/utils.py index eb617fe7..24e1e5cb 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -28,6 +28,14 @@ def readline_complete(text, state): 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 From 0c85642e6b22e3d2aabb6bda596f5df8f881898a Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sat, 18 May 2013 23:49:48 +0200 Subject: [PATCH 4/7] Add utils.JediRLCompleter --- jedi/utils.py | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/jedi/utils.py b/jedi/utils.py index 24e1e5cb..2da56cf8 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -2,27 +2,20 @@ 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`. +class JediRLCompleter(Completer): - Usage:: - - import readline - readline.set_completer(readline_complete) - - """ - ns = vars(sys.modules['__main__']) - completions = Interpreter(text, [ns]).completions() - try: - return text + completions[state].complete - except IndexError: - return None + def attr_matches(self, text): + if '(' in text or ')' in text: + completions = Interpreter(text, [self.namespace]).completions() + return [text + c.complete for c in completions] + else: + # NOTE: Completer is old type class + return Completer.attr_matches(self, text) def setup_readline(): @@ -42,7 +35,7 @@ def setup_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 "()": From 6a8a06fd0b514e0cc191f54aa0dffbc4924e851c Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 19 May 2013 00:24:49 +0200 Subject: [PATCH 5/7] Improve JediRLCompleter for minor cases --- jedi/utils.py | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/jedi/utils.py b/jedi/utils.py index 2da56cf8..41bc2da5 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -9,13 +9,51 @@ from jedi import Interpreter class JediRLCompleter(Completer): + 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='()'): + """ + 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): - if '(' in text or ')' in text: - completions = Interpreter(text, [self.namespace]).completions() - return [text + c.complete for c in completions] - else: - # NOTE: Completer is old type class - return Completer.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(): From 3b3310ee3ebcb41056870d5fc861619330a50894 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 19 May 2013 00:41:33 +0200 Subject: [PATCH 6/7] Determine readline delims programmatically --- jedi/utils.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/jedi/utils.py b/jedi/utils.py index 41bc2da5..e957dbd3 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -7,6 +7,23 @@ from rlcompleter import Completer 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): def _jedi_matches(self, text): @@ -14,7 +31,7 @@ class JediRLCompleter(Completer): return [text + c.complete for c in completions] @staticmethod - def _split_for_default_matcher(text, delims='()'): + def _split_for_default_matcher(text, delims=_NON_DELIMS): """ Split `text` before passing it to :meth:`Completer.attr_matches` etc. @@ -75,6 +92,4 @@ def setup_readline(): else: 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) From 4bd03d0c74f0bb2afb5345ae3e7e792480526c26 Mon Sep 17 00:00:00 2001 From: Takafumi Arakaki Date: Sun, 19 May 2013 00:52:41 +0200 Subject: [PATCH 7/7] Document JediRLCompleter --- jedi/utils.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/jedi/utils.py b/jedi/utils.py index e957dbd3..37aa2225 100644 --- a/jedi/utils.py +++ b/jedi/utils.py @@ -26,6 +26,21 @@ else: 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]