1- # encoding: utf-8
21"""Completion for IPython.
32
43This module started as fork of the rlcompleter module in the Python standard
8786module in debug mode (start IPython with ``--Completer.debug=True``) in order
8887to have extra logging information is :any:`jedi` is crashing, or if current
8988IPython completer pending deprecations are returning results not yet handled
90- by :any:`jedi`.
89+ by :any:`jedi`
9190
9291Using Jedi for tab completion allow snippets like the following to work without
9392having to execute any code:
103102current development version to get better completions.
104103"""
105104
106- # skip module docstests
107- skip_doctest = True
108105
109106# Copyright (c) IPython Development Team.
110107# Distributed under the terms of the Modified BSD License.
142139from IPython .utils .process import arg_split
143140from traitlets import Bool , Enum , observe , Int
144141
142+ # skip module docstests
143+ skip_doctest = True
144+
145145try :
146146 import jedi
147147 import jedi .api .helpers
148+ import jedi .api .classes
148149 JEDI_INSTALLED = True
149150except ImportError :
150151 JEDI_INSTALLED = False
@@ -237,7 +238,7 @@ def protect_filename(s, protectables=PROTECTABLES):
237238 return s
238239
239240
240- def expand_user (path ) :
241+ def expand_user (path : str ) -> Tuple [ str , bool , str ] :
241242 """Expand ``~``-style usernames in strings.
242243
243244 This is similar to :func:`os.path.expanduser`, but it computes and returns
@@ -277,7 +278,7 @@ def expand_user(path):
277278 return newpath , tilde_expand , tilde_val
278279
279280
280- def compress_user (path , tilde_expand , tilde_val ) :
281+ def compress_user (path : str , tilde_expand : bool , tilde_val : str ) -> str :
281282 """Does the opposite of expand_user, with its outputs.
282283 """
283284 if tilde_expand :
@@ -338,6 +339,8 @@ def __init__(self, name):
338339 self .complete = name
339340 self .type = 'crashed'
340341 self .name_with_symbols = name
342+ self .signature = ''
343+ self ._origin = 'fake'
341344
342345 def __repr__ (self ):
343346 return '<Fake completion object jedi has crashed>'
@@ -366,7 +369,9 @@ class Completion:
366369 ``IPython.python_matches``, ``IPython.magics_matches``...).
367370 """
368371
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 :
370375 warnings .warn ("``Completion`` is a provisional API (as of IPython 6.0). "
371376 "It may change without warnings. "
372377 "Use in corresponding context manager." ,
@@ -376,10 +381,12 @@ def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='
376381 self .end = end
377382 self .text = text
378383 self .type = type
384+ self .signature = signature
379385 self ._origin = _origin
380386
381387 def __repr__ (self ):
382- return '<Completion start=%s end=%s text=%r type=%r>' % (self .start , self .end , self .text , self .type or '?' )
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 '?' )
383390
384391 def __eq__ (self , other )-> Bool :
385392 """
@@ -417,6 +424,10 @@ def _deduplicate_completions(text: str, completions: _IC)-> _IC:
417424 completions: Iterator[Completion]
418425 iterator over the completions to deduplicate
419426
427+ Yields
428+ ------
429+ `Completions` objects
430+
420431
421432 Completions coming from multiple sources, may be different but end up having
422433 the same effect when applied to ``text``. If this is the case, this will
@@ -489,7 +500,7 @@ def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:
489500 seen_jedi .add (new_text )
490501 elif c ._origin == 'IPCompleter.python_matches' :
491502 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 )
493504 diff = seen_python_matches .difference (seen_jedi )
494505 if diff and _debug :
495506 print ('IPython.python matches have extras:' , diff )
@@ -933,6 +944,52 @@ def back_latex_name_matches(text:str):
933944 return u'' , ()
934945
935946
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+
936993class IPCompleter (Completer ):
937994 """Extension of the completer class with IPython-specific features"""
938995
@@ -1762,10 +1819,15 @@ def _completions(self, full_text: str, offset: int, *, _timeout)->Iterator[Compl
17621819 print ("Error in Jedi getting type of " , jm )
17631820 type_ = None
17641821 delta = len (jm .name_with_symbols ) - len (jm .complete )
1822+ if type_ == 'function' :
1823+ signature = _make_signature (jm )
1824+ else :
1825+ signature = ''
17651826 yield Completion (start = offset - delta ,
17661827 end = offset ,
17671828 text = jm .name_with_symbols ,
17681829 type = type_ ,
1830+ signature = signature ,
17691831 _origin = 'jedi' )
17701832
17711833 if time .monotonic () > deadline :
@@ -1777,21 +1839,23 @@ def _completions(self, full_text: str, offset: int, *, _timeout)->Iterator[Compl
17771839 end = offset ,
17781840 text = jm .name_with_symbols ,
17791841 type = '<unknown>' , # don't compute type for speed
1780- _origin = 'jedi' )
1842+ _origin = 'jedi' ,
1843+ signature = '' )
17811844
17821845
17831846 start_offset = before .rfind (matched_text )
17841847
17851848 # TODO:
17861849 # Supress this, right now just for debug.
17871850 if jedi_matches and matches and self .debug :
1788- yield Completion (start = start_offset , end = offset , text = '--jedi/ipython--' , _origin = 'debug' )
1851+ yield Completion (start = start_offset , end = offset , text = '--jedi/ipython--' ,
1852+ _origin = 'debug' , type = 'none' , signature = '' )
17891853
17901854 # I'm unsure if this is always true, so let's assert and see if it
17911855 # crash
17921856 assert before .endswith (matched_text )
17931857 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>' )
17951859
17961860
17971861 def complete (self , text = None , line_buffer = None , cursor_pos = None ):
0 commit comments