diff options
author | shadchin <shadchin@yandex-team.ru> | 2022-02-10 16:44:39 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:39 +0300 |
commit | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (patch) | |
tree | 64175d5cadab313b3e7039ebaa06c5bc3295e274 /contrib/python/ipython/py3/IPython/terminal/ptutils.py | |
parent | 2598ef1d0aee359b4b6d5fdd1758916d5907d04f (diff) | |
download | ydb-e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0.tar.gz |
Restoring authorship annotation for <shadchin@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/ipython/py3/IPython/terminal/ptutils.py')
-rw-r--r-- | contrib/python/ipython/py3/IPython/terminal/ptutils.py | 394 |
1 files changed, 197 insertions, 197 deletions
diff --git a/contrib/python/ipython/py3/IPython/terminal/ptutils.py b/contrib/python/ipython/py3/IPython/terminal/ptutils.py index 380087dc29..3e5d3c5c77 100644 --- a/contrib/python/ipython/py3/IPython/terminal/ptutils.py +++ b/contrib/python/ipython/py3/IPython/terminal/ptutils.py @@ -1,197 +1,197 @@ -"""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. - -import unicodedata -from wcwidth import wcwidth -import sys -import traceback - -from IPython.core.completer import ( - provisionalcompleter, cursor_to_position, - _deduplicate_completions) -from prompt_toolkit.completion import Completer, Completion -from prompt_toolkit.lexers import Lexer -from prompt_toolkit.lexers import PygmentsLexer -from prompt_toolkit.patch_stdout import patch_stdout - -import pygments.lexers as pygments_lexers -import os - -_completion_sentinel = object() - -def _elide_point(string:str, *, min_elide=30)->str: - """ - 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, - replace the middle part with ellipses. - - If three consecutive dots, or two consecutive dots are encountered these are - replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode - equivalents - """ - string = string.replace('...','\N{HORIZONTAL ELLIPSIS}') - string = string.replace('..','\N{TWO DOT LEADER}') - if len(string) < min_elide: - return string - - object_parts = string.split('.') - file_parts = string.split(os.sep) - if file_parts[-1] == '': - file_parts.pop() - - if len(object_parts) > 3: - return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1]) - - elif len(file_parts) > 3: - return ('{}' + os.sep + '{}\N{HORIZONTAL ELLIPSIS}{}' + os.sep + '{}').format(file_parts[0], file_parts[1][0], file_parts[-2][-1], file_parts[-1]) - - return string - -def _elide_typed(string:str, typed:str, *, min_elide:int=30)->str: - """ - 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 - if cut_how_much < 7: - return string - if string.startswith(typed) and len(string)> len(typed): - return f"{string[:3]}\N{HORIZONTAL ELLIPSIS}{string[cut_how_much:]}" - return string - -def _elide(string:str, typed:str, min_elide=30)->str: - return _elide_typed( - _elide_point(string, min_elide=min_elide), - typed, min_elide=min_elide) - - - -def _adjust_completion_text_based_on_context(text, body, offset): - if text.endswith('=') and len(body) > offset and body[offset] == '=': - return text[:-1] - else: - return text - - -class IPythonPTCompleter(Completer): - """Adaptor to provide IPython completions to prompt_toolkit""" - def __init__(self, ipy_completer=None, shell=None): - if shell is None and ipy_completer is None: - raise TypeError("Please pass shell=an InteractiveShell instance.") - self._ipy_completer = ipy_completer - self.shell = shell - - @property - def ipy_completer(self): - if self._ipy_completer: - return self._ipy_completer - else: - return self.shell.Completer - - def get_completions(self, document, complete_event): - if not document.current_line.strip(): - return - # 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. - - with patch_stdout(), provisionalcompleter(): - 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) - try: - yield from self._get_completions(body, offset, cursor_position, self.ipy_completer) - except Exception as e: - 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') - - @staticmethod - def _get_completions(body, offset, cursor_position, ipyc): - """ - Private equivalent of get_completions() use only for unit_testing. - """ - debug = getattr(ipyc, 'debug', False) - completions = _deduplicate_completions( - body, ipyc.completions(body, offset)) - for c in completions: - if not c.text: - # Guard against completion machinery giving us an empty string. - continue - text = unicodedata.normalize('NFC', c.text) - # 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. - 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) - - # Yield the modified completion instead, if this worked. - if wcwidth(text[0:1]) == 1: - yield Completion(fixed_text, start_position=c.start - offset - 1) - 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) - display_text = c.text - - adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset) - if c.type == 'function': - yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()', body[c.start:c.end]), display_meta=c.type+c.signature) - else: - yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text, body[c.start:c.end]), display_meta=c.type) - -class IPythonPTLexer(Lexer): - """ - Wrapper around PythonLexer and BashLexer. - """ - def __init__(self): - l = pygments_lexers - self.python_lexer = PygmentsLexer(l.Python3Lexer) - 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), - } - - def lex_document(self, document): - 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 - - return lexer.lex_document(document) +"""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. + +import unicodedata +from wcwidth import wcwidth +import sys +import traceback + +from IPython.core.completer import ( + provisionalcompleter, cursor_to_position, + _deduplicate_completions) +from prompt_toolkit.completion import Completer, Completion +from prompt_toolkit.lexers import Lexer +from prompt_toolkit.lexers import PygmentsLexer +from prompt_toolkit.patch_stdout import patch_stdout + +import pygments.lexers as pygments_lexers +import os + +_completion_sentinel = object() + +def _elide_point(string:str, *, min_elide=30)->str: + """ + 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, + replace the middle part with ellipses. + + If three consecutive dots, or two consecutive dots are encountered these are + replaced by the equivalents HORIZONTAL ELLIPSIS or TWO DOT LEADER unicode + equivalents + """ + string = string.replace('...','\N{HORIZONTAL ELLIPSIS}') + string = string.replace('..','\N{TWO DOT LEADER}') + if len(string) < min_elide: + return string + + object_parts = string.split('.') + file_parts = string.split(os.sep) + if file_parts[-1] == '': + file_parts.pop() + + if len(object_parts) > 3: + return '{}.{}\N{HORIZONTAL ELLIPSIS}{}.{}'.format(object_parts[0], object_parts[1][0], object_parts[-2][-1], object_parts[-1]) + + elif len(file_parts) > 3: + return ('{}' + os.sep + '{}\N{HORIZONTAL ELLIPSIS}{}' + os.sep + '{}').format(file_parts[0], file_parts[1][0], file_parts[-2][-1], file_parts[-1]) + + return string + +def _elide_typed(string:str, typed:str, *, min_elide:int=30)->str: + """ + 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 + if cut_how_much < 7: + return string + if string.startswith(typed) and len(string)> len(typed): + return f"{string[:3]}\N{HORIZONTAL ELLIPSIS}{string[cut_how_much:]}" + return string + +def _elide(string:str, typed:str, min_elide=30)->str: + return _elide_typed( + _elide_point(string, min_elide=min_elide), + typed, min_elide=min_elide) + + + +def _adjust_completion_text_based_on_context(text, body, offset): + if text.endswith('=') and len(body) > offset and body[offset] == '=': + return text[:-1] + else: + return text + + +class IPythonPTCompleter(Completer): + """Adaptor to provide IPython completions to prompt_toolkit""" + def __init__(self, ipy_completer=None, shell=None): + if shell is None and ipy_completer is None: + raise TypeError("Please pass shell=an InteractiveShell instance.") + self._ipy_completer = ipy_completer + self.shell = shell + + @property + def ipy_completer(self): + if self._ipy_completer: + return self._ipy_completer + else: + return self.shell.Completer + + def get_completions(self, document, complete_event): + if not document.current_line.strip(): + return + # 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. + + with patch_stdout(), provisionalcompleter(): + 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) + try: + yield from self._get_completions(body, offset, cursor_position, self.ipy_completer) + except Exception as e: + 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') + + @staticmethod + def _get_completions(body, offset, cursor_position, ipyc): + """ + Private equivalent of get_completions() use only for unit_testing. + """ + debug = getattr(ipyc, 'debug', False) + completions = _deduplicate_completions( + body, ipyc.completions(body, offset)) + for c in completions: + if not c.text: + # Guard against completion machinery giving us an empty string. + continue + text = unicodedata.normalize('NFC', c.text) + # 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. + 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) + + # Yield the modified completion instead, if this worked. + if wcwidth(text[0:1]) == 1: + yield Completion(fixed_text, start_position=c.start - offset - 1) + 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) + display_text = c.text + + adjusted_text = _adjust_completion_text_based_on_context(c.text, body, offset) + if c.type == 'function': + yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text+'()', body[c.start:c.end]), display_meta=c.type+c.signature) + else: + yield Completion(adjusted_text, start_position=c.start - offset, display=_elide(display_text, body[c.start:c.end]), display_meta=c.type) + +class IPythonPTLexer(Lexer): + """ + Wrapper around PythonLexer and BashLexer. + """ + def __init__(self): + l = pygments_lexers + self.python_lexer = PygmentsLexer(l.Python3Lexer) + 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), + } + + def lex_document(self, document): + 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 + + return lexer.lex_document(document) |