From aea5e15f519fc8c71b38563483f9048793a79565 2020-05-10 18:13:56 From: Matthias Bussonnier Date: 2020-05-10 18:13:56 Subject: [PATCH] Merge pull request #12302 from meeseeksmachine/auto-backport-of-pr-12284-on-7.x --- diff --git a/.travis.yml b/.travis.yml index ad1aab7..c4a309e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ install: - pip install setuptools --upgrade - pip install -e file://$PWD#egg=ipython[test] --upgrade - pip install trio curio --upgrade --upgrade-strategy eager - - pip install pytest 'matplotlib !=3.2.0' + - pip install pytest 'matplotlib !=3.2.0' mypy - pip install codecov check-manifest --upgrade script: @@ -50,6 +50,7 @@ script: fi - cd /tmp && iptest --coverage xml && cd - - pytest IPython + - mypy --ignore-missing-imports -m IPython.terminal.ptutils # On the latest Python (on Linux) only, make sure that the docs build. - | if [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]] && [[ "$TRAVIS_OS_NAME" == "linux" ]]; then diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 848a14a..f5fdba6 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -2217,7 +2217,7 @@ class InteractiveShell(SingletonConfigurable): The position argument (defaults to 0) is the index in the completers list where you want the completer to be inserted.""" - newcomp = types.MethodType(completer,self.Completer) + newcomp = types.MethodType(completer, self.Completer) self.Completer.custom_matchers.insert(pos,newcomp) def set_completer_frame(self, frame=None): diff --git a/IPython/terminal/ptutils.py b/IPython/terminal/ptutils.py index 47fd6f4..ed7ad45 100644 --- a/IPython/terminal/ptutils.py +++ b/IPython/terminal/ptutils.py @@ -23,7 +23,7 @@ import os _completion_sentinel = object() -def _elide(string, *, min_elide=30): +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. @@ -53,6 +53,26 @@ def _elide(string, *, min_elide=30): 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] == '=': @@ -89,7 +109,11 @@ class IPythonPTCompleter(Completer): cursor_col = document.cursor_position_col cursor_position = document.cursor_position offset = cursor_to_position(body, cursor_row, cursor_col) - yield from self._get_completions(body, offset, cursor_position, self.ipy_completer) + try: + yield from self._get_completions(body, offset, cursor_position, self.ipy_completer) + except Exception as e: + from traceback import print_tb + print_tb(e) @staticmethod def _get_completions(body, offset, cursor_position, ipyc): @@ -128,9 +152,9 @@ class IPythonPTCompleter(Completer): 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+'()'), display_meta=c.type+c.signature) + 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), display_meta=c.type) + 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): """ diff --git a/IPython/terminal/tests/test_interactivshell.py b/IPython/terminal/tests/test_interactivshell.py index 6bacc8e..640e5d4 100644 --- a/IPython/terminal/tests/test_interactivshell.py +++ b/IPython/terminal/tests/test_interactivshell.py @@ -17,14 +17,32 @@ import nose.tools as nt class TestElide(unittest.TestCase): def test_elide(self): - _elide('concatenate((a1, a2, ...), axis') # do not raise - _elide('concatenate((a1, a2, ..), . axis') # do not raise - nt.assert_equal(_elide('aaaa.bbbb.ccccc.dddddd.eeeee.fffff.gggggg.hhhhhh'), 'aaaa.b…g.hhhhhh') - + _elide('concatenate((a1, a2, ...), axis', '') # do not raise + _elide('concatenate((a1, a2, ..), . axis', '') # do not raise + nt.assert_equal(_elide('aaaa.bbbb.ccccc.dddddd.eeeee.fffff.gggggg.hhhhhh',''), 'aaaa.b…g.hhhhhh') + test_string = os.sep.join(['', 10*'a', 10*'b', 10*'c', '']) expect_stirng = os.sep + 'a' + '\N{HORIZONTAL ELLIPSIS}' + 'b' + os.sep + 10*'c' - nt.assert_equal(_elide(test_string), expect_stirng) - + nt.assert_equal(_elide(test_string, ''), expect_stirng) + + def test_elide_typed_normal(self): + nt.assert_equal(_elide('the quick brown fox jumped over the lazy dog', 'the quick brown fox', min_elide=10), 'the…fox jumped over the lazy dog') + + + def test_elide_typed_short_match(self): + """ + if the match is too short we don't elide. + avoid the "the...the" + """ + nt.assert_equal(_elide('the quick brown fox jumped over the lazy dog', 'the', min_elide=10), 'the quick brown fox jumped over the lazy dog') + + def test_elide_typed_no_match(self): + """ + if the match is too short we don't elide. + avoid the "the...the" + """ + # here we typed red instead of brown + nt.assert_equal(_elide('the quick brown fox jumped over the lazy dog', 'the quick red fox', min_elide=10), 'the quick brown fox jumped over the lazy dog') class TestContextAwareCompletion(unittest.TestCase):