@@ -51,6 +51,7 @@ def __init__(
5151 self .get_enable_dictionary_completion = get_enable_dictionary_completion
5252
5353 self ._system_completer = SystemCompleter ()
54+ self ._jedi_completer = JediCompleter (get_globals , get_locals )
5455 self ._dictionary_completer = DictionaryCompleter (get_globals , get_locals )
5556
5657 self ._path_completer_cache : Optional [GrammarCompleter ] = None
@@ -129,10 +130,14 @@ def _complete_path_while_typing(self, document: Document) -> bool:
129130 )
130131
131132 def _complete_python_while_typing (self , document : Document ) -> bool :
132- char_before_cursor = document .char_before_cursor
133+ """
134+ When `complete_while_typing` is set, only return completions when this
135+ returns `True`.
136+ """
137+ text = document .text_before_cursor .rstrip ()
138+ char_before_cursor = text [- 1 :]
133139 return bool (
134- document .text
135- and (char_before_cursor .isalnum () or char_before_cursor in "_." )
140+ text and (char_before_cursor .isalnum () or char_before_cursor in "_.(," )
136141 )
137142
138143 def get_completions (
@@ -151,94 +156,127 @@ def get_completions(
151156 )
152157 return
153158
154- # Do dictionary key completions.
155- if self .get_enable_dictionary_completion ():
156- has_dict_completions = False
157- for c in self ._dictionary_completer .get_completions (
158- document , complete_event
159- ):
160- if c .text not in "[." :
161- # If we get the [ or . completion, still include the other
162- # completions.
163- has_dict_completions = True
164- yield c
165- if has_dict_completions :
166- return
167-
168159 # Do Path completions (if there were no dictionary completions).
169160 if complete_event .completion_requested or self ._complete_path_while_typing (
170161 document
171162 ):
172163 yield from self ._path_completer .get_completions (document , complete_event )
173164
174- # If we are inside a string, Don't do Jedi completion.
175- if self ._path_completer_grammar .match (document .text_before_cursor ):
176- return
177-
178- # Do Jedi Python completions.
179165 if complete_event .completion_requested or self ._complete_python_while_typing (
180166 document
181167 ):
182- script = get_jedi_script_from_document (
183- document , self .get_locals (), self . get_globals ()
184- )
168+ # If we are inside a string, Don't do Python completion.
169+ if self ._path_completer_grammar . match ( document . text_before_cursor ):
170+ return
185171
186- if script :
187- try :
188- jedi_completions = script .complete (
189- column = document .cursor_position_col ,
190- line = document .cursor_position_row + 1 ,
172+ # Do dictionary key completions.
173+ if self .get_enable_dictionary_completion ():
174+ has_dict_completions = False
175+ for c in self ._dictionary_completer .get_completions (
176+ document , complete_event
177+ ):
178+ if c .text not in "[." :
179+ # If we get the [ or . completion, still include the other
180+ # completions.
181+ has_dict_completions = True
182+ yield c
183+ if has_dict_completions :
184+ return
185+
186+ # Do Jedi Python completions.
187+ yield from self ._jedi_completer .get_completions (document , complete_event )
188+
189+
190+ class JediCompleter (Completer ):
191+ """
192+ Autocompleter that uses the Jedi library.
193+ """
194+
195+ def __init__ (self , get_globals , get_locals ) -> None :
196+ super ().__init__ ()
197+
198+ self .get_globals = get_globals
199+ self .get_locals = get_locals
200+
201+ def get_completions (
202+ self , document : Document , complete_event : CompleteEvent
203+ ) -> Iterable [Completion ]:
204+ script = get_jedi_script_from_document (
205+ document , self .get_locals (), self .get_globals ()
206+ )
207+
208+ if script :
209+ try :
210+ jedi_completions = script .complete (
211+ column = document .cursor_position_col ,
212+ line = document .cursor_position_row + 1 ,
213+ )
214+ except TypeError :
215+ # Issue #9: bad syntax causes completions() to fail in jedi.
216+ # https://github.com/jonathanslenders/python-prompt-toolkit/issues/9
217+ pass
218+ except UnicodeDecodeError :
219+ # Issue #43: UnicodeDecodeError on OpenBSD
220+ # https://github.com/jonathanslenders/python-prompt-toolkit/issues/43
221+ pass
222+ except AttributeError :
223+ # Jedi issue #513: https://github.com/davidhalter/jedi/issues/513
224+ pass
225+ except ValueError :
226+ # Jedi issue: "ValueError: invalid \x escape"
227+ pass
228+ except KeyError :
229+ # Jedi issue: "KeyError: u'a_lambda'."
230+ # https://github.com/jonathanslenders/ptpython/issues/89
231+ pass
232+ except IOError :
233+ # Jedi issue: "IOError: No such file or directory."
234+ # https://github.com/jonathanslenders/ptpython/issues/71
235+ pass
236+ except AssertionError :
237+ # In jedi.parser.__init__.py: 227, in remove_last_newline,
238+ # the assertion "newline.value.endswith('\n')" can fail.
239+ pass
240+ except SystemError :
241+ # In jedi.api.helpers.py: 144, in get_stack_at_position
242+ # raise SystemError("This really shouldn't happen. There's a bug in Jedi.")
243+ pass
244+ except NotImplementedError :
245+ # See: https://github.com/jonathanslenders/ptpython/issues/223
246+ pass
247+ except Exception :
248+ # Supress all other Jedi exceptions.
249+ pass
250+ else :
251+ # Move function parameters to the top.
252+ jedi_completions = sorted (
253+ jedi_completions ,
254+ key = lambda jc : (
255+ # Params first.
256+ jc .type != "param" ,
257+ # Private at the end.
258+ jc .name .startswith ("_" ),
259+ # Then sort by name.
260+ jc .name_with_symbols .lower (),
261+ ),
262+ )
263+
264+ for jc in jedi_completions :
265+ if jc .type == "function" :
266+ suffix = "()"
267+ else :
268+ suffix = ""
269+
270+ if jc .type == "param" :
271+ suffix = "..."
272+
273+ yield Completion (
274+ jc .name_with_symbols ,
275+ len (jc .complete ) - len (jc .name_with_symbols ),
276+ display = jc .name_with_symbols + suffix ,
277+ display_meta = jc .type ,
278+ style = _get_style_for_jedi_completion (jc ),
191279 )
192- except TypeError :
193- # Issue #9: bad syntax causes completions() to fail in jedi.
194- # https://github.com/jonathanslenders/python-prompt-toolkit/issues/9
195- pass
196- except UnicodeDecodeError :
197- # Issue #43: UnicodeDecodeError on OpenBSD
198- # https://github.com/jonathanslenders/python-prompt-toolkit/issues/43
199- pass
200- except AttributeError :
201- # Jedi issue #513: https://github.com/davidhalter/jedi/issues/513
202- pass
203- except ValueError :
204- # Jedi issue: "ValueError: invalid \x escape"
205- pass
206- except KeyError :
207- # Jedi issue: "KeyError: u'a_lambda'."
208- # https://github.com/jonathanslenders/ptpython/issues/89
209- pass
210- except IOError :
211- # Jedi issue: "IOError: No such file or directory."
212- # https://github.com/jonathanslenders/ptpython/issues/71
213- pass
214- except AssertionError :
215- # In jedi.parser.__init__.py: 227, in remove_last_newline,
216- # the assertion "newline.value.endswith('\n')" can fail.
217- pass
218- except SystemError :
219- # In jedi.api.helpers.py: 144, in get_stack_at_position
220- # raise SystemError("This really shouldn't happen. There's a bug in Jedi.")
221- pass
222- except NotImplementedError :
223- # See: https://github.com/jonathanslenders/ptpython/issues/223
224- pass
225- except Exception :
226- # Supress all other Jedi exceptions.
227- pass
228- else :
229- for jc in jedi_completions :
230- if jc .type == "function" :
231- suffix = "()"
232- else :
233- suffix = ""
234-
235- yield Completion (
236- jc .name_with_symbols ,
237- len (jc .complete ) - len (jc .name_with_symbols ),
238- display = jc .name_with_symbols + suffix ,
239- display_meta = jc .type ,
240- style = _get_style_for_name (jc .name_with_symbols ),
241- )
242280
243281
244282class DictionaryCompleter (Completer ):
@@ -575,10 +613,15 @@ class ReprFailedError(Exception):
575613 _builtin_names = []
576614
577615
578- def _get_style_for_name ( name : str ) -> str :
616+ def _get_style_for_jedi_completion ( jedi_completion ) -> str :
579617 """
580618 Return completion style to use for this name.
581619 """
620+ name = jedi_completion .name_with_symbols
621+
622+ if jedi_completion .type == "param" :
623+ return "class:completion.param"
624+
582625 if name in _builtin_names :
583626 return "class:completion.builtin"
584627
0 commit comments