##// END OF EJS Templates
Be a little smarter about invisible characters in terminal prompts...
Aaron Meurer -
Show More
@@ -257,6 +257,14 b' def _lenlastline(s):'
257 257 return len(s.splitlines()[-1])
258 258
259 259
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
260 268 class UserNSFormatter(Formatter):
261 269 """A Formatter that falls back on a shell's user_ns and __builtins__ for name resolution"""
262 270 def __init__(self, shell):
@@ -350,8 +358,7 b' class PromptManager(Configurable):'
350 358 self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
351 359 # We count invisible characters (colour escapes) on the last line of the
352 360 # 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))
355 362 self.invisible_chars[name] = invis_chars
356 363
357 364 def _update_prompt_trait(self, traitname, new_template):
@@ -6,7 +6,7 b' import unittest'
6 6 import os
7 7
8 8 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
10 10 from IPython.testing.globalipapp import get_ipython
11 11 from IPython.utils.tempdir import TemporaryWorkingDirectory
12 12 from IPython.utils import py3compat
@@ -106,4 +106,24 b' class PromptTests(unittest.TestCase):'
106 106 self.assertEqual(p, '~')
107 107 finally:
108 108 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)
General Comments 0
You need to be logged in to leave comments. Login now