Fix for prompts containing newlines. #1105

Merged
merged 2 commits into from Dec 8, 2011
View
@@ -232,7 +232,14 @@ def cwd_filt2(depth):
[LazyEvaluate(cwd_filt, x) for x in range(1,6)],
'cwd_y': [LazyEvaluate(cwd_filt2, x) for x in range(6)]
}
-
+
+def _lenlastline(s):
+ """Get the length of the last line. More intelligent than
+ len(s.splitlines()[-1]).
+ """
+ if not s or s.endswith(('\n', '\r')):
+ return 0
+ return len(s.splitlines()[-1])
class PromptManager(Configurable):
"""This is the primary interface for producing IPython's prompts."""
@@ -305,8 +312,10 @@ def update_prompt(self, name, new_template=None):
"""
if new_template is not None:
self.templates[name] = multiple_replace(prompt_abbreviations, new_template)
- invis_chars = len(self._render(name, color=True)) - \
- len(self._render(name, color=False))
+ # We count invisible characters (colour escapes) on the last line of the
+ # prompt, to calculate the width for lining up subsequent prompts.
+ invis_chars = _lenlastline(self._render(name, color=True)) - \
+ _lenlastline(self._render(name, color=False))
self.invisible_chars[name] = invis_chars
def _update_prompt_trait(self, traitname, new_template):
@@ -388,9 +397,10 @@ def render(self, name, color=True, just=None, **kwargs):
# Handle justification of prompt
invis_chars = self.invisible_chars[name] if color else 0
- self.txtwidth = len(res) - invis_chars
+ self.txtwidth = _lenlastline(res) - invis_chars
just = self.justify if (just is None) else just
- if just:
+ # If the prompt spans more than one line, don't try to justify it:
+ if just and ('\n' not in res) and ('\r' not in res):
res = res.rjust(self.width + invis_chars)
- self.width = len(res) - invis_chars
+ self.width = _lenlastline(res) - invis_chars
return res
@@ -0,0 +1,43 @@
+"""Tests for prompt generation."""
+
+import unittest
+
+import nose.tools as nt
+
+from IPython.testing import tools as tt, decorators as dec
+from IPython.core.prompts import PromptManager
+from IPython.testing.globalipapp import get_ipython
+
+ip = get_ipython()
+
+
+class PromptTests(unittest.TestCase):
+ def setUp(self):
+ self.pm = PromptManager(shell=ip, config=ip.config)
+
+ def test_multiline_prompt(self):
+ self.pm.in_template = "[In]\n>>>"
+ self.pm.render('in')
+ self.assertEqual(self.pm.width, 3)
+ self.assertEqual(self.pm.txtwidth, 3)
+
+ self.pm.in_template = '[In]\n'
+ self.pm.render('in')
+ self.assertEqual(self.pm.width, 0)
+ self.assertEqual(self.pm.txtwidth, 0)
+
+ def test_translate_abbreviations(self):
+ def do_translate(template):
+ self.pm.in_template = template
+ return self.pm.templates['in']
+
+ pairs = [(r'%n>', '{color.number}{count}{color.prompt}>'),
+ (r'\T', '{time}'),
+ (r'\n', '\n')
+ ]
+
+ tt.check_pairs(do_translate, pairs)
+
+ def test_render(self):
+ self.pm.in_template = r'\#>'
+ self.assertEqual(self.pm.render('in',color=False), '%d>' % ip.execution_count)