Show More
@@ -0,0 +1,4 b'' | |||
|
1 | Terminal IPython will now show the signature of the function while completing. | |
|
2 | Only the currently highlighted function will show its signature on the line | |
|
3 | below the completer by default. The functionality is recent so might be | |
|
4 | limited, we welcome bug report and enhancement request on it. |
@@ -1,4 +1,3 b'' | |||
|
1 | # encoding: utf-8 | |
|
2 | 1 |
|
|
3 | 2 | |
|
4 | 3 | This module started as fork of the rlcompleter module in the Python standard |
@@ -87,7 +86,7 b' We welcome any feedback on these new API, and we also encourage you to try this' | |||
|
87 | 86 | module in debug mode (start IPython with ``--Completer.debug=True``) in order |
|
88 | 87 | to have extra logging information is :any:`jedi` is crashing, or if current |
|
89 | 88 | IPython completer pending deprecations are returning results not yet handled |
|
90 |
by :any:`jedi` |
|
|
89 | by :any:`jedi` | |
|
91 | 90 | |
|
92 | 91 | Using Jedi for tab completion allow snippets like the following to work without |
|
93 | 92 | having to execute any code: |
@@ -103,8 +102,6 b' Be sure to update :any:`jedi` to the latest stable version or to try the' | |||
|
103 | 102 | current development version to get better completions. |
|
104 | 103 | """ |
|
105 | 104 | |
|
106 | # skip module docstests | |
|
107 | skip_doctest = True | |
|
108 | 105 | |
|
109 | 106 | # Copyright (c) IPython Development Team. |
|
110 | 107 | # Distributed under the terms of the Modified BSD License. |
@@ -142,9 +139,13 b' from IPython.utils.dir2 import dir2, get_real_method' | |||
|
142 | 139 | from IPython.utils.process import arg_split |
|
143 | 140 | from traitlets import Bool, Enum, observe, Int |
|
144 | 141 | |
|
142 | # skip module docstests | |
|
143 | skip_doctest = True | |
|
144 | ||
|
145 | 145 | try: |
|
146 | 146 | import jedi |
|
147 | 147 | import jedi.api.helpers |
|
148 | import jedi.api.classes | |
|
148 | 149 | JEDI_INSTALLED = True |
|
149 | 150 | except ImportError: |
|
150 | 151 | JEDI_INSTALLED = False |
@@ -237,7 +238,7 b' def protect_filename(s, protectables=PROTECTABLES):' | |||
|
237 | 238 | return s |
|
238 | 239 | |
|
239 | 240 | |
|
240 | def expand_user(path): | |
|
241 | def expand_user(path:str) -> Tuple[str, bool, str]: | |
|
241 | 242 | """Expand ``~``-style usernames in strings. |
|
242 | 243 | |
|
243 | 244 | This is similar to :func:`os.path.expanduser`, but it computes and returns |
@@ -277,7 +278,7 b' def expand_user(path):' | |||
|
277 | 278 | return newpath, tilde_expand, tilde_val |
|
278 | 279 | |
|
279 | 280 | |
|
280 | def compress_user(path, tilde_expand, tilde_val): | |
|
281 | def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str: | |
|
281 | 282 | """Does the opposite of expand_user, with its outputs. |
|
282 | 283 | """ |
|
283 | 284 | if tilde_expand: |
@@ -338,6 +339,8 b' class _FakeJediCompletion:' | |||
|
338 | 339 | self.complete = name |
|
339 | 340 | self.type = 'crashed' |
|
340 | 341 | self.name_with_symbols = name |
|
342 | self.signature = '' | |
|
343 | self._origin = 'fake' | |
|
341 | 344 | |
|
342 | 345 | def __repr__(self): |
|
343 | 346 | return '<Fake completion object jedi has crashed>' |
@@ -366,7 +369,9 b' class Completion:' | |||
|
366 | 369 | ``IPython.python_matches``, ``IPython.magics_matches``...). |
|
367 | 370 | """ |
|
368 | 371 | |
|
369 | def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='') -> None: | |
|
372 | __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin'] | |
|
373 | ||
|
374 | def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None: | |
|
370 | 375 | warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). " |
|
371 | 376 | "It may change without warnings. " |
|
372 | 377 | "Use in corresponding context manager.", |
@@ -376,10 +381,12 b' class Completion:' | |||
|
376 | 381 | self.end = end |
|
377 | 382 | self.text = text |
|
378 | 383 | self.type = type |
|
384 | self.signature = signature | |
|
379 | 385 | self._origin = _origin |
|
380 | 386 | |
|
381 | 387 | def __repr__(self): |
|
382 |
return '<Completion start=%s end=%s text=%r type=%r>' % |
|
|
388 | return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \ | |
|
389 | (self.start, self.end, self.text, self.type or '?', self.signature or '?') | |
|
383 | 390 | |
|
384 | 391 | def __eq__(self, other)->Bool: |
|
385 | 392 | """ |
@@ -417,6 +424,10 b' def _deduplicate_completions(text: str, completions: _IC)-> _IC:' | |||
|
417 | 424 | completions: Iterator[Completion] |
|
418 | 425 | iterator over the completions to deduplicate |
|
419 | 426 | |
|
427 | Yields | |
|
428 | ------ | |
|
429 | `Completions` objects | |
|
430 | ||
|
420 | 431 | |
|
421 | 432 | Completions coming from multiple sources, may be different but end up having |
|
422 | 433 | the same effect when applied to ``text``. If this is the case, this will |
@@ -489,7 +500,7 b' def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:' | |||
|
489 | 500 | seen_jedi.add(new_text) |
|
490 | 501 | elif c._origin == 'IPCompleter.python_matches': |
|
491 | 502 | seen_python_matches.add(new_text) |
|
492 | yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin) | |
|
503 | yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature) | |
|
493 | 504 | diff = seen_python_matches.difference(seen_jedi) |
|
494 | 505 | if diff and _debug: |
|
495 | 506 | print('IPython.python matches have extras:', diff) |
@@ -933,6 +944,52 b' def back_latex_name_matches(text:str):' | |||
|
933 | 944 | return u'', () |
|
934 | 945 | |
|
935 | 946 | |
|
947 | def _formatparamchildren(parameter) -> str: | |
|
948 | """ | |
|
949 | Get parameter name and value from Jedi Private API | |
|
950 | ||
|
951 | Jedi does not expose a simple way to get `param=value` from its API. | |
|
952 | ||
|
953 | Prameter | |
|
954 | ======== | |
|
955 | ||
|
956 | parameter: | |
|
957 | Jedi's function `Param` | |
|
958 | ||
|
959 | Returns | |
|
960 | ======= | |
|
961 | ||
|
962 | A string like 'a', 'b=1', '*args', '**kwargs' | |
|
963 | ||
|
964 | ||
|
965 | """ | |
|
966 | description = parameter.description | |
|
967 | if not description.startswith('param '): | |
|
968 | raise ValueError('Jedi function parameter description have change format.' | |
|
969 | 'Expected "param ...", found %r".' % description) | |
|
970 | return description[6:] | |
|
971 | ||
|
972 | def _make_signature(completion)-> str: | |
|
973 | """ | |
|
974 | Make the signature from a jedi completion | |
|
975 | ||
|
976 | Parameter | |
|
977 | ========= | |
|
978 | ||
|
979 | completion: jedi.Completion | |
|
980 | object does not complete a function type | |
|
981 | ||
|
982 | Returns | |
|
983 | ======= | |
|
984 | ||
|
985 | a string consisting of the function signature, with the parenthesis but | |
|
986 | without the function name. example: | |
|
987 | `(a, *args, b=1, **kwargs)` | |
|
988 | ||
|
989 | """ | |
|
990 | ||
|
991 | return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for p in completion.params) if f]) | |
|
992 | ||
|
936 | 993 | class IPCompleter(Completer): |
|
937 | 994 | """Extension of the completer class with IPython-specific features""" |
|
938 | 995 | |
@@ -1762,10 +1819,15 b' class IPCompleter(Completer):' | |||
|
1762 | 1819 | print("Error in Jedi getting type of ", jm) |
|
1763 | 1820 | type_ = None |
|
1764 | 1821 | delta = len(jm.name_with_symbols) - len(jm.complete) |
|
1822 | if type_ == 'function': | |
|
1823 | signature = _make_signature(jm) | |
|
1824 | else: | |
|
1825 | signature = '' | |
|
1765 | 1826 | yield Completion(start=offset - delta, |
|
1766 | 1827 | end=offset, |
|
1767 | 1828 | text=jm.name_with_symbols, |
|
1768 | 1829 | type=type_, |
|
1830 | signature=signature, | |
|
1769 | 1831 | _origin='jedi') |
|
1770 | 1832 | |
|
1771 | 1833 | if time.monotonic() > deadline: |
@@ -1777,7 +1839,8 b' class IPCompleter(Completer):' | |||
|
1777 | 1839 | end=offset, |
|
1778 | 1840 | text=jm.name_with_symbols, |
|
1779 | 1841 | type='<unknown>', # don't compute type for speed |
|
1780 |
_origin='jedi' |
|
|
1842 | _origin='jedi', | |
|
1843 | signature='') | |
|
1781 | 1844 | |
|
1782 | 1845 | |
|
1783 | 1846 | start_offset = before.rfind(matched_text) |
@@ -1785,13 +1848,14 b' class IPCompleter(Completer):' | |||
|
1785 | 1848 | # TODO: |
|
1786 | 1849 | # Supress this, right now just for debug. |
|
1787 | 1850 | if jedi_matches and matches and self.debug: |
|
1788 |
yield Completion(start=start_offset, end=offset, text='--jedi/ipython--', |
|
|
1851 | yield Completion(start=start_offset, end=offset, text='--jedi/ipython--', | |
|
1852 | _origin='debug', type='none', signature='') | |
|
1789 | 1853 | |
|
1790 | 1854 | # I'm unsure if this is always true, so let's assert and see if it |
|
1791 | 1855 | # crash |
|
1792 | 1856 | assert before.endswith(matched_text) |
|
1793 | 1857 | for m, t in zip(matches, matches_origin): |
|
1794 | yield Completion(start=start_offset, end=offset, text=m, _origin=t) | |
|
1858 | yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>') | |
|
1795 | 1859 | |
|
1796 | 1860 | |
|
1797 | 1861 | def complete(self, text=None, line_buffer=None, cursor_pos=None): |
@@ -37,6 +37,8 b' from IPython.utils._process_common import arg_split' | |||
|
37 | 37 | # FIXME: this should be pulled in with the right call via the component system |
|
38 | 38 | from IPython import get_ipython |
|
39 | 39 | |
|
40 | from typing import List | |
|
41 | ||
|
40 | 42 | #----------------------------------------------------------------------------- |
|
41 | 43 | # Globals and constants |
|
42 | 44 | #----------------------------------------------------------------------------- |
@@ -153,7 +155,7 b' def is_importable(module, attr, only_modules):' | |||
|
153 | 155 | return not(attr[:2] == '__' and attr[-2:] == '__') |
|
154 | 156 | |
|
155 | 157 | |
|
156 | def try_import(mod: str, only_modules=False): | |
|
158 | def try_import(mod: str, only_modules=False) -> List[str]: | |
|
157 | 159 | """ |
|
158 | 160 | Try to import given module and return list of potential completions. |
|
159 | 161 | """ |
@@ -173,9 +175,9 b' def try_import(mod: str, only_modules=False):' | |||
|
173 | 175 | completions.extend(getattr(m, '__all__', [])) |
|
174 | 176 | if m_is_init: |
|
175 | 177 | completions.extend(module_list(os.path.dirname(m.__file__))) |
|
176 | completions = {c for c in completions if isinstance(c, str)} | |
|
177 | completions.discard('__init__') | |
|
178 | return list(completions) | |
|
178 | completions_set = {c for c in completions if isinstance(c, str)} | |
|
179 | completions_set.discard('__init__') | |
|
180 | return list(completions_set) | |
|
179 | 181 | |
|
180 | 182 | |
|
181 | 183 | #----------------------------------------------------------------------------- |
@@ -335,6 +335,18 b' def test_jedi():' | |||
|
335 | 335 | |
|
336 | 336 | yield _test_not_complete, 'does not mix types', 'a=(1,"foo");a[0].', 'capitalize' |
|
337 | 337 | |
|
338 | def test_completion_have_signature(): | |
|
339 | """ | |
|
340 | Lets make sure jedi is capable of pulling out the signature of the function we are completing. | |
|
341 | """ | |
|
342 | ip = get_ipython() | |
|
343 | with provisionalcompleter(): | |
|
344 | completions = ip.Completer.completions('ope', 3) | |
|
345 | c = next(completions) # should be `open` | |
|
346 | assert 'file' in c.signature, "Signature of function was not found by completer" | |
|
347 | assert 'encoding' in c.signature, "Signature of function was not found by completer" | |
|
348 | ||
|
349 | ||
|
338 | 350 | def test_deduplicate_completions(): |
|
339 | 351 | """ |
|
340 | 352 | Test that completions are correctly deduplicated (even if ranges are not the same) |
@@ -120,8 +120,8 b' class IPythonPTCompleter(Completer):' | |||
|
120 | 120 | |
|
121 | 121 | adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset) |
|
122 | 122 | if c.type == 'function': |
|
123 | display_text = display_text + '()' | |
|
124 | ||
|
123 | yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()'), display_meta=c.type+c.signature) | |
|
124 | else: | |
|
125 | 125 | yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text), display_meta=c.type) |
|
126 | 126 | |
|
127 | 127 | class IPythonPTLexer(Lexer): |
@@ -100,6 +100,8 b' IPython and Jedi will be able to infer that ``data[0]`` is actually a string' | |||
|
100 | 100 | and should show relevant completions like ``upper()``, ``lower()`` and other |
|
101 | 101 | string methods. You can use the :kbd:`Tab` key to cycle through completions, |
|
102 | 102 | and while a completion is highlighted, its type will be shown as well. |
|
103 | When the type of the completion is a function, the completer will also show the | |
|
104 | signature of the function when highlighted. | |
|
103 | 105 | |
|
104 | 106 | Exploring your objects |
|
105 | 107 | ====================== |
General Comments 0
You need to be logged in to leave comments.
Login now