##// END OF EJS Templates
Move dict keys completions higher....
Move dict keys completions higher. If we are in a dict this is likely way more accurate.

File last commit:

r25873:ba11b22e
r27063:d23958a1
Show More
ptutils.py
197 lines | 7.2 KiB | text/x-python | PythonLexer
Min RK
Make IPythonPTCompleter(shell) backward-compatible...
r22768 """prompt-toolkit utilities
Everything in this module is a private API,
not to be used outside IPython.
"""
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
Thomas Kluyver
Integrate completion into debugger
r22388 import unicodedata
from wcwidth import wcwidth
Matthias Bussonnier
Deduplicate completions between IPython and Jedi....
r23358 from IPython.core.completer import (
Matthias Bussonnier
Minor improvements to completion...
r23467 provisionalcompleter, cursor_to_position,
Matthias Bussonnier
Deduplicate completions between IPython and Jedi....
r23358 _deduplicate_completions)
Thomas Kluyver
Integrate completion into debugger
r22388 from prompt_toolkit.completion import Completer, Completion
Jonathan Slenders
Upgrade to prompt_toolkit 2.0
r24376 from prompt_toolkit.lexers import Lexer
from prompt_toolkit.lexers import PygmentsLexer
from prompt_toolkit.patch_stdout import patch_stdout
Thomas Kluyver
Integrate completion into debugger
r22388
Jonathan Slenders
Use correct Pygments lexers for different %%magics.
r22512 import pygments.lexers as pygments_lexers
Elliott Morgan Jobson
Fixed variable names and path delimiter for #11441
r24827 import os
Matthias Bussonnier
forgotten sys
r25872 import sys
Matthias Bussonnier
forgotten traceback
r25873 import traceback
Jonathan Slenders
Use correct Pygments lexers for different %%magics.
r22512
Matthias Bussonnier
First step in reintegrating Jedi...
r23284 _completion_sentinel = object()
Matthias Bussonnier
Fix a couple of edge cases, and update tests...
r25690 def _elide_point(string:str, *, min_elide=30)->str:
Matthias Bussonnier
Minor improvements to completion...
r23467 """
Elliott Morgan Jobson
Updated path completion for long path names to display the completed portion rather than the prefix. Will now elide for long file names as well as long object names....
r24816 If a string is long enough, and has at least 3 dots,
replace the middle part with ellipses.
If a string naming a file is long enough, and has at least 3 slashes,
Matthias Bussonnier
Minor improvements to completion...
r23467 replace the middle part with ellipses.
Matthias Bussonnier
Fix Crash: Eliding string with consecutive dots....
r23591 If three consecutive dots, or two consecutive dots are encountered these are
replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode
equivalents
Matthias Bussonnier
Minor improvements to completion...
r23467 """
Matthias Bussonnier
Fix Crash: Eliding string with consecutive dots....
r23591 string = string.replace('...','\N{HORIZONTAL ELLIPSIS}')
string = string.replace('..','\N{TWO DOT LEADER}')
Matthias Bussonnier
Minor improvements to completion...
r23467 if len(string) < min_elide:
return string
Elliott Morgan Jobson
Updated path completion for long path names to display the completed portion rather than the prefix. Will now elide for long file names as well as long object names....
r24816 object_parts = string.split('.')
Elliott Morgan Jobson
Fixed variable names and path delimiter for #11441
r24827 file_parts = string.split(os.sep)
Matthias Bussonnier
Be more explicit and don't use side effect of expression.
r25486 if file_parts[-1] == '':
file_parts.pop()
Matthias Bussonnier
Minor improvements to completion...
r23467
Elliott Morgan Jobson
Updated path completion for long path names to display the completed portion rather than the prefix. Will now elide for long file names as well as long object names....
r24816 if len(object_parts) > 3:
Elliott Morgan Jobson
Fixed variable names and path delimiter for #11441
r24827 return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1])
Elliott Morgan Jobson
Updated path completion for long path names to display the completed portion rather than the prefix. Will now elide for long file names as well as long object names....
r24816
elif len(file_parts) > 3:
Elliott Morgan Jobson
Updated to use the operating system's path delimiter in the displayed path suggestion #11441
r24828 return ('{}' + os.sep + '{}\N{HORIZONTAL ELLIPSIS}{}' + os.sep + '{}').format(file_parts[0], file_parts[1][0], file_parts[-2][-1], file_parts[-1])
Matthias Bussonnier
Minor improvements to completion...
r23467
Elliott Morgan Jobson
Updated path completion for long path names to display the completed portion rather than the prefix. Will now elide for long file names as well as long object names....
r24816 return string
Matthias Bussonnier
Minor improvements to completion...
r23467
Matthias Bussonnier
Fix a couple of edge cases, and update tests...
r25690 def _elide_typed(string:str, typed:str, *, min_elide:int=30)->str:
Matthias Bussonnier
Try to elide long completion based on user input....
r25689 """
Elide the middle of a long string if the beginning has already been typed.
"""
if len(string) < min_elide:
return string
cut_how_much = len(typed)-3
Matthias Bussonnier
Fix a couple of edge cases, and update tests...
r25690 if cut_how_much < 7:
return string
Matthias Bussonnier
Try to elide long completion based on user input....
r25689 if string.startswith(typed) and len(string)> len(typed):
return f"{string[:3]}\N{HORIZONTAL ELLIPSIS}{string[cut_how_much:]}"
return string
Matthias Bussonnier
Fix a couple of edge cases, and update tests...
r25690 def _elide(string:str, typed:str, min_elide=30)->str:
Matthias Bussonnier
Try to elide long completion based on user input....
r25689 return _elide_typed(
_elide_point(string, min_elide=min_elide),
typed, min_elide=min_elide)
Matthias Bussonnier
First step in reintegrating Jedi...
r23284
Steve Bartz
Do not duplicate '=' when tab completing a kwarg with the cursor on '='
r23622 def _adjust_completion_text_based_on_context(text, body, offset):
Matthias Bussonnier
Attemt to fix ast transformer now that Num and Str atr the same Constant ast node...
r24924 if text.endswith('=') and len(body) > offset and body[offset] == '=':
Steve Bartz
Do not duplicate '=' when tab completing a kwarg with the cursor on '='
r23622 return text[:-1]
else:
return text
Matthias Bussonnier
First step in reintegrating Jedi...
r23284
Thomas Kluyver
Integrate completion into debugger
r22388
class IPythonPTCompleter(Completer):
"""Adaptor to provide IPython completions to prompt_toolkit"""
Jonathan Slenders
Upgrade to prompt_toolkit 2.0
r24376 def __init__(self, ipy_completer=None, shell=None):
Min RK
Make IPythonPTCompleter(shell) backward-compatible...
r22768 if shell is None and ipy_completer is None:
raise TypeError("Please pass shell=an InteractiveShell instance.")
self._ipy_completer = ipy_completer
Min RK
give PTCompleter InteractiveShell, not Completer...
r22765 self.shell = shell
Min RK
explicit TypeError when IPCompleter passed to PTCompleter
r22767
Min RK
give PTCompleter InteractiveShell, not Completer...
r22765 @property
def ipy_completer(self):
Min RK
Make IPythonPTCompleter(shell) backward-compatible...
r22768 if self._ipy_completer:
return self._ipy_completer
else:
return self.shell.Completer
Thomas Kluyver
Integrate completion into debugger
r22388
def get_completions(self, document, complete_event):
if not document.current_line.strip():
return
Thomas Kluyver
Use prompt_toolkit's patch_stdout_context when finding completions...
r23171 # Some bits of our completion system may print stuff (e.g. if a module
# is imported). This context manager ensures that doesn't interfere with
# the prompt.
Thomas Kluyver
Protect against completion machinery returning an empty string...
r22445
Jonathan Slenders
Upgrade to prompt_toolkit 2.0
r24376 with patch_stdout(), provisionalcompleter():
Matthias Bussonnier
First step in reintegrating Jedi...
r23284 body = document.text
cursor_row = document.cursor_position_row
cursor_col = document.cursor_position_col
cursor_position = document.cursor_position
offset = cursor_to_position(body, cursor_row, cursor_col)
Matthias Bussonnier
Try to elide long completion based on user input....
r25689 try:
yield from self._get_completions(body, offset, cursor_position, self.ipy_completer)
except Exception as e:
Matthias Bussonnier
catch unrecoverable error...
r25870 try:
exc_type, exc_value, exc_tb = sys.exc_info()
traceback.print_exception(exc_type, exc_value, exc_tb)
except AttributeError:
print('Unrecoverable Error in completions')
Thomas Kluyver
Integrate completion into debugger
r22388
Matthias Bussonnier
First step in reintegrating Jedi...
r23284 @staticmethod
def _get_completions(body, offset, cursor_position, ipyc):
"""
Private equivalent of get_completions() use only for unit_testing.
"""
debug = getattr(ipyc, 'debug', False)
Matthias Bussonnier
Deduplicate completions between IPython and Jedi....
r23358 completions = _deduplicate_completions(
body, ipyc.completions(body, offset))
Matthias Bussonnier
First step in reintegrating Jedi...
r23284 for c in completions:
if not c.text:
# Guard against completion machinery giving us an empty string.
continue
text = unicodedata.normalize('NFC', c.text)
Thomas Kluyver
Integrate completion into debugger
r22388 # When the first character of the completion has a zero length,
# then it's probably a decomposed unicode character. E.g. caused by
# the "\dot" completion. Try to compose again with the previous
# character.
Matthias Bussonnier
First step in reintegrating Jedi...
r23284 if wcwidth(text[0]) == 0:
if cursor_position + c.start > 0:
char_before = body[c.start - 1]
fixed_text = unicodedata.normalize(
'NFC', char_before + text)
Thomas Kluyver
Integrate completion into debugger
r22388
# Yield the modified completion instead, if this worked.
Matthias Bussonnier
First step in reintegrating Jedi...
r23284 if wcwidth(text[0:1]) == 1:
yield Completion(fixed_text, start_position=c.start - offset - 1)
Thomas Kluyver
Integrate completion into debugger
r22388 continue
# TODO: Use Jedi to determine meta_text
# (Jedi currently has a bug that results in incorrect information.)
# meta_text = ''
# yield Completion(m, start_position=start_pos,
# display_meta=meta_text)
Matthias Bussonnier
Minor improvements to completion...
r23467 display_text = c.text
Steve Bartz
Do not duplicate '=' when tab completing a kwarg with the cursor on '='
r23622 adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset)
Matthias Bussonnier
Minor improvements to completion...
r23467 if c.type == 'function':
Matthias Bussonnier
Try to elide long completion based on user input....
r25689 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()', body[c.start:c.end]), display_meta=c.type+c.signature)
Matthias Bussonnier
Show signature with Jedi....
r23753 else:
Matthias Bussonnier
Try to elide long completion based on user input....
r25689 yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text, body[c.start:c.end]), display_meta=c.type)
Thomas Kluyver
Integrate completion into debugger
r22388
class IPythonPTLexer(Lexer):
"""
Wrapper around PythonLexer and BashLexer.
"""
def __init__(self):
Jonathan Slenders
Use correct Pygments lexers for different %%magics.
r22512 l = pygments_lexers
Srinivas Reddy Thatiparthy
Remove PY3 variable
r23108 self.python_lexer = PygmentsLexer(l.Python3Lexer)
Jonathan Slenders
Use correct Pygments lexers for different %%magics.
r22512 self.shell_lexer = PygmentsLexer(l.BashLexer)
self.magic_lexers = {
'HTML': PygmentsLexer(l.HtmlLexer),
'html': PygmentsLexer(l.HtmlLexer),
'javascript': PygmentsLexer(l.JavascriptLexer),
'js': PygmentsLexer(l.JavascriptLexer),
'perl': PygmentsLexer(l.PerlLexer),
'ruby': PygmentsLexer(l.RubyLexer),
'latex': PygmentsLexer(l.TexLexer),
}
Thomas Kluyver
Integrate completion into debugger
r22388
Jonathan Slenders
Upgrade to prompt_toolkit 2.0
r24376 def lex_document(self, document):
Jonathan Slenders
Use correct Pygments lexers for different %%magics.
r22512 text = document.text.lstrip()
lexer = self.python_lexer
if text.startswith('!') or text.startswith('%%bash'):
lexer = self.shell_lexer
elif text.startswith('%%'):
for magic, l in self.magic_lexers.items():
if text.startswith('%%' + magic):
lexer = l
break
Jonathan Slenders
Upgrade to prompt_toolkit 2.0
r24376 return lexer.lex_document(document)