Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8e996f8

Browse files
committed
Be a little smarter about invisible characters in terminal prompts
This is a partial fix to ipython#8724. Previously, only known color codes were considered to be invisible. Now, it looks for any kind of invisible sequence as defined by the \001 \002 delimiters (which is what readline uses). The situation could still be improved, as it still assumes that the number of invisible characters is constant for a given template. Making this work correctly with the existing API is awkward, so I didn't attempt it, especially since the readline frontend may be removed at some point in the near future.
1 parent 6c98e7a commit 8e996f8

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

IPython/core/prompts.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,14 @@ def _lenlastline(s):
257257
return len(s.splitlines()[-1])
258258

259259

260+
invisible_chars_re = re.compile('\001[^\001\002]*\002')
261+
def _invisible_characters(s):
262+
"""
263+
Get the number of invisible ANSI characters in s. Invisible characters
264+
must be delimited by \001 and \002.
265+
"""
266+
return _lenlastline(s) - _lenlastline(invisible_chars_re.sub('', s))
267+
260268
class UserNSFormatter(Formatter):
261269
"""A Formatter that falls back on a shell's user_ns and __builtins__ for name resolution"""
262270
def __init__(self, shell):
@@ -350,8 +358,7 @@ def update_prompt(self, name, new_template=None):
350358
self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
351359
# We count invisible characters (colour escapes) on the last line of the
352360
# prompt, to calculate the width for lining up subsequent prompts.
353-
invis_chars = _lenlastline(self._render(name, color=True)) - \
354-
_lenlastline(self._render(name, color=False))
361+
invis_chars = _invisible_characters(self._render(name, color=True))
355362
self.invisible_chars[name] = invis_chars
356363

357364
def _update_prompt_trait(self, traitname, new_template):

IPython/core/tests/test_prompts.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import os
77

88
from IPython.testing import tools as tt, decorators as dec
9-
from IPython.core.prompts import PromptManager, LazyEvaluate
9+
from IPython.core.prompts import PromptManager, LazyEvaluate, _invisible_characters
1010
from IPython.testing.globalipapp import get_ipython
1111
from IPython.utils.tempdir import TemporaryWorkingDirectory
1212
from IPython.utils import py3compat
@@ -106,4 +106,24 @@ def test_cwd_x(self):
106106
self.assertEqual(p, '~')
107107
finally:
108108
os.chdir(save)
109-
109+
110+
def test_invisible_chars(self):
111+
self.assertEqual(_invisible_characters('abc'), 0)
112+
self.assertEqual(_invisible_characters('\001\033[1;37m\002'), 9)
113+
# Sequences must be between \001 and \002 to be counted
114+
self.assertEqual(_invisible_characters('\033[1;37m'), 0)
115+
# Test custom escape sequences
116+
self.assertEqual(_invisible_characters('\001\033]133;A\a\002'), 10)
117+
118+
def test_width(self):
119+
default_in = '\x01\x1b]133;A\x07\x02In [\\#]: \x01\x1b]133;B\x07\x02'
120+
self.pm.in_template = default_in
121+
self.pm.render('in')
122+
self.assertEqual(self.pm.width, 8)
123+
self.assertEqual(self.pm.txtwidth, 8)
124+
125+
# Test custom escape sequences
126+
self.pm.in_template = '\001\033]133;A\a\002' + default_in + '\001\033]133;B\a\002'
127+
self.pm.render('in')
128+
self.assertEqual(self.pm.width, 8)
129+
self.assertEqual(self.pm.txtwidth, 8)

0 commit comments

Comments
 (0)