From 7d05f6cee2c88c846c9e5005f8e17f287dbb7249 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Fri, 2 Dec 2011 11:04:44 -0500 Subject: [PATCH 1/3] Support carriage return ('\r') characters in the qtconsole. Closes #629. --- .../qt/console/ansi_code_processor.py | 42 +++++++++++-------- IPython/frontend/qt/console/console_widget.py | 4 ++ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/IPython/frontend/qt/console/ansi_code_processor.py b/IPython/frontend/qt/console/ansi_code_processor.py index eeee2180184..ac47fd8ff35 100644 --- a/IPython/frontend/qt/console/ansi_code_processor.py +++ b/IPython/frontend/qt/console/ansi_code_processor.py @@ -26,12 +26,16 @@ # An action for scroll requests (SU and ST) and form feeds. ScrollAction = namedtuple('ScrollAction', ['action', 'dir', 'unit', 'count']) +# An action for the carriage return character +CarriageReturnAction = namedtuple('CarriageReturnAction', ['action']) + # Regular expressions. CSI_COMMANDS = 'ABCDEFGHJKSTfmnsu' CSI_SUBPATTERN = '\[(.*?)([%s])' % CSI_COMMANDS OSC_SUBPATTERN = '\](.*?)[\x07\x1b]' -ANSI_PATTERN = re.compile('\x01?\x1b(%s|%s)\x02?' % \ - (CSI_SUBPATTERN, OSC_SUBPATTERN)) +ANSI_PATTERN = ('\x01?\x1b(%s|%s)\x02?' % \ + (CSI_SUBPATTERN, OSC_SUBPATTERN)) +ANSI_OR_CR_PATTERN = re.compile('(\r)|(?:%s)' % ANSI_PATTERN) SPECIAL_PATTERN = re.compile('([\f])') #----------------------------------------------------------------------------- @@ -76,7 +80,7 @@ def split_string(self, string): self.actions = [] start = 0 - for match in ANSI_PATTERN.finditer(string): + for match in ANSI_OR_CR_PATTERN.finditer(string): raw = string[start:match.start()] substring = SPECIAL_PATTERN.sub(self._replace_special, raw) if substring or self.actions: @@ -85,20 +89,24 @@ def split_string(self, string): self.actions = [] groups = filter(lambda x: x is not None, match.groups()) - params = [ param for param in groups[1].split(';') if param ] - if groups[0].startswith('['): - # Case 1: CSI code. - try: - params = map(int, params) - except ValueError: - # Silently discard badly formed codes. - pass - else: - self.set_csi_code(groups[2], params) - - elif groups[0].startswith(']'): - # Case 2: OSC code. - self.set_osc_code(params) + if groups[0] == '\r': + self.actions.append(CarriageReturnAction('carriage-return')) + yield '' + else: + params = [ param for param in groups[1].split(';') if param ] + if groups[0].startswith('['): + # Case 1: CSI code. + try: + params = map(int, params) + except ValueError: + # Silently discard badly formed codes. + pass + else: + self.set_csi_code(groups[2], params) + + elif groups[0].startswith(']'): + # Case 2: OSC code. + self.set_osc_code(params) raw = string[start:] substring = SPECIAL_PATTERN.sub(self._replace_special, raw) diff --git a/IPython/frontend/qt/console/console_widget.py b/IPython/frontend/qt/console/console_widget.py index 6e1a9d81deb..8fe94408c70 100644 --- a/IPython/frontend/qt/console/console_widget.py +++ b/IPython/frontend/qt/console/console_widget.py @@ -1513,6 +1513,10 @@ def _insert_plain_text(self, cursor, text): cursor.joinPreviousEditBlock() cursor.deletePreviousChar() + elif act.action == 'carriage-return': + cursor.movePosition( + cursor.StartOfLine, cursor.KeepAnchor) + format = self._ansi_processor.get_format() cursor.insertText(substring, format) else: From 8eb8d564daf396cd6c5e340a696257188207da23 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Fri, 2 Dec 2011 11:22:03 -0500 Subject: [PATCH 2/3] Add support for beep ('\b') character in qtconsole. --- IPython/frontend/qt/console/ansi_code_processor.py | 10 ++++++++-- IPython/frontend/qt/console/console_widget.py | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/IPython/frontend/qt/console/ansi_code_processor.py b/IPython/frontend/qt/console/ansi_code_processor.py index ac47fd8ff35..f027ccf31ae 100644 --- a/IPython/frontend/qt/console/ansi_code_processor.py +++ b/IPython/frontend/qt/console/ansi_code_processor.py @@ -29,13 +29,16 @@ # An action for the carriage return character CarriageReturnAction = namedtuple('CarriageReturnAction', ['action']) +# An action for the beep character +BeepAction = namedtuple('BeepAction', ['action']) + # Regular expressions. CSI_COMMANDS = 'ABCDEFGHJKSTfmnsu' CSI_SUBPATTERN = '\[(.*?)([%s])' % CSI_COMMANDS OSC_SUBPATTERN = '\](.*?)[\x07\x1b]' ANSI_PATTERN = ('\x01?\x1b(%s|%s)\x02?' % \ (CSI_SUBPATTERN, OSC_SUBPATTERN)) -ANSI_OR_CR_PATTERN = re.compile('(\r)|(?:%s)' % ANSI_PATTERN) +ANSI_OR_SPECIAL_PATTERN = re.compile('(\b|\r)|(?:%s)' % ANSI_PATTERN) SPECIAL_PATTERN = re.compile('([\f])') #----------------------------------------------------------------------------- @@ -80,7 +83,7 @@ def split_string(self, string): self.actions = [] start = 0 - for match in ANSI_OR_CR_PATTERN.finditer(string): + for match in ANSI_OR_SPECIAL_PATTERN.finditer(string): raw = string[start:match.start()] substring = SPECIAL_PATTERN.sub(self._replace_special, raw) if substring or self.actions: @@ -92,6 +95,9 @@ def split_string(self, string): if groups[0] == '\r': self.actions.append(CarriageReturnAction('carriage-return')) yield '' + elif groups[0] == '\b': + self.actions.append(BeepAction('beep')) + yield '' else: params = [ param for param in groups[1].split(';') if param ] if groups[0].startswith('['): diff --git a/IPython/frontend/qt/console/console_widget.py b/IPython/frontend/qt/console/console_widget.py index 8fe94408c70..69c273cf503 100644 --- a/IPython/frontend/qt/console/console_widget.py +++ b/IPython/frontend/qt/console/console_widget.py @@ -1517,6 +1517,9 @@ def _insert_plain_text(self, cursor, text): cursor.movePosition( cursor.StartOfLine, cursor.KeepAnchor) + elif act.action == 'beep': + QtGui.qApp.beep() + format = self._ansi_processor.get_format() cursor.insertText(substring, format) else: From 6a95b5de686c33fd28bb499af01d865d8d9aa679 Mon Sep 17 00:00:00 2001 From: Michael Droettboom Date: Mon, 5 Dec 2011 12:01:08 -0500 Subject: [PATCH 3/3] Add tests for carriage-return and beep support. --- .../console/tests/test_ansi_code_processor.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/IPython/frontend/qt/console/tests/test_ansi_code_processor.py b/IPython/frontend/qt/console/tests/test_ansi_code_processor.py index f71334a6c25..0f602b15fb0 100644 --- a/IPython/frontend/qt/console/tests/test_ansi_code_processor.py +++ b/IPython/frontend/qt/console/tests/test_ansi_code_processor.py @@ -90,8 +90,8 @@ def test_scroll(self): self.fail('Too many substrings.') self.assertEquals(i, 1, 'Too few substrings.') - def test_specials(self): - """ Are special characters processed correctly? + def test_formfeed(self): + """ Are formfeed characters processed correctly? """ string = '\f' # form feed self.assertEquals(list(self.processor.split_string(string)), ['']) @@ -102,6 +102,26 @@ def test_specials(self): self.assertEquals(action.unit, 'page') self.assertEquals(action.count, 1) + def test_carriage_return(self): + """ Are carriage return characters processed correctly? + """ + string = 'foo\rbar' # form feed + self.assertEquals(list(self.processor.split_string(string)), ['foo', '', 'bar']) + self.assertEquals(len(self.processor.actions), 1) + action = self.processor.actions[0] + self.assertEquals(action.action, 'carriage-return') + self.assertEquals(action.count, 1) + + def test_beep(self): + """ Are beep characters processed correctly? + """ + string = 'foo\bbar' # form feed + self.assertEquals(list(self.processor.split_string(string)), ['foo', '', 'bar']) + self.assertEquals(len(self.processor.actions), 1) + action = self.processor.actions[0] + self.assertEquals(action.action, 'beep') + self.assertEquals(action.count, 1) + if __name__ == '__main__': unittest.main()