Skip to content

Commit 107aba8

Browse files
Cleanup of completion code.
1 parent 1b528cf commit 107aba8

File tree

2 files changed

+125
-81
lines changed

2 files changed

+125
-81
lines changed

ptpython/completer.py

Lines changed: 124 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -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

244282
class 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

ptpython/style.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def generate_style(python_style: BaseStyle, ui_style: BaseStyle) -> BaseStyle:
7575
"out.number": "#ff0000",
7676
# Completions.
7777
"completion.builtin": "",
78+
"completion.param": "#006666 italic",
7879
"completion.keyword": "fg:#008800",
7980
"completion.keyword fuzzymatch.inside": "fg:#008800",
8081
"completion.keyword fuzzymatch.outside": "fg:#44aa44",

0 commit comments

Comments
 (0)