[PATCH 5/9] ansi2html: add support for CSIs other than ESC[m

Paolo Bonzini posted 9 patches 7 years, 2 months ago
[PATCH 5/9] ansi2html: add support for CSIs other than ESC[m
Posted by Paolo Bonzini 7 years, 2 months ago
These include cursor movement, line clearing, and screen clearing.
For screen clearing, all of them are treated like ESC[2J, which is
the most commonly used screen clearing CSI.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
 patchew/logviewer.py    | 50 +++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test_ansi2html.py | 30 +++++++++++++++++++++++++++++
 2 files changed, 80 insertions(+)

diff --git a/patchew/logviewer.py b/patchew/logviewer.py
index fd53d14..b5627fa 100644
--- a/patchew/logviewer.py
+++ b/patchew/logviewer.py
@@ -96,6 +96,54 @@ class ANSI2HTMLConverter(object):
         yield suffix
         self._reset()
 
+    def _do_csi_C(self, it):
+        arg = next(it)
+        if arg == 0:
+            arg = 1
+        self._set_pos(self.pos + arg)
+
+    def _do_csi_D(self, it):
+        arg = next(it)
+        if arg == 0:
+            arg = 1
+        self._set_pos(max(0, self.pos - arg))
+
+    def _do_csi_K(self, it):
+        arg = next(it)
+        if arg == 0 or arg == 2:
+            assert not self.lazy_accumulate or self.lazy_contents == ''
+            if self.pos < len(self.line):
+                assert not self.lazy_accumulate
+                del self.line[self.pos:]
+        if arg == 1 or arg == 2:
+            save_pos = self.pos
+            if save_pos > 0:
+                self.pos = 0
+                self._write(' ' * save_pos)
+
+    def _parse_csi_with_args(self, csi, func):
+        if (len(csi) <= 3):
+            func(iter([0]))
+        else:
+            # thanks to the regular expression, we know arg is a number
+            # and there cannot be a ValueError
+            func((int(arg) for arg in csi[2:-1].split(";")))
+
+    def _parse_csi(self, csi):
+        if csi[1] != '[':
+            return
+
+        if csi[-1] == 'J':
+            save_pos = self.pos
+            yield from self._write_line('<hr>')
+            self._set_pos(save_pos)
+        elif csi[-1] == 'K':
+            self._parse_csi_with_args(csi, self._do_csi_K)
+        elif csi[-1] == 'C':
+            self._parse_csi_with_args(csi, self._do_csi_C)
+        elif csi[-1] == 'D':
+            self._parse_csi_with_args(csi, self._do_csi_D)
+
     def convert(self, input):
         yield from self._write_prefix()
         for m in self.RE.finditer(input):
@@ -127,6 +175,8 @@ class ANSI2HTMLConverter(object):
                     self._set_pos(self.pos + (8 - self.pos % 8))
                 elif seq == '\r':
                     self.pos = 0
+                elif len(seq) > 1:
+                    yield from self._parse_csi(seq)
 
     def finish(self):
         yield from self._write_prefix()
diff --git a/tests/test_ansi2html.py b/tests/test_ansi2html.py
index 731f243..ffbc98e 100644
--- a/tests/test_ansi2html.py
+++ b/tests/test_ansi2html.py
@@ -56,6 +56,36 @@ class ANSI2HTMLTest(unittest.TestCase):
         self.assertBlackBg('{\x1b]test\x1b[0m\x07}', '{}')
         self.assertBlackBg('{\x1b]test\x1b[7m\x07}', '{}')
 
+    # ESC [2J
+    def test_clear_screen(self):
+        self.assertBlackBg('a\n\x1b[2Jb', 'a\n<hr>b')
+        self.assertBlackBg('a\x1b[2J', 'a<hr> ')
+        self.assertBlackBg('a\x1b[2Jb', 'a<hr> b')
+
+    # ESC [C and ESC [D
+    def test_horiz_movement(self):
+        self.assertBlackBg('abc\x1b[2DB', 'aBc')
+        self.assertBlackBg('abc\x1b[3CD', 'abc   D')
+        self.assertBlackBg('abcd\x1b[3DB\x1b[1CD', 'aBcD')
+        self.assertBlackBg('abc\x1b[0CD', 'abc D')
+        self.assertBlackBg('abc\x1b[CD', 'abc D')
+
+    # ESC [K
+    def test_clear_line(self):
+        self.assertBlackBg('\x1b[Kabcd', 'abcd')
+        self.assertBlackBg('abcd\r\x1b[K', '')
+        self.assertBlackBg('abcd\b\x1b[K', 'abc')
+        self.assertBlackBg('abcd\r\x1b[KDef', 'Def')
+        self.assertBlackBg('abcd\b\x1b[KDef', 'abcDef')
+        self.assertBlackBg('abcd\r\x1b[0K', '')
+        self.assertBlackBg('abcd\b\x1b[0K', 'abc')
+        self.assertBlackBg('abcd\r\x1b[1K', 'abcd')
+        self.assertBlackBg('abcd\b\x1b[1K', '   d')
+        self.assertBlackBg('abcd\r\x1b[2K', '')
+        self.assertBlackBg('abcd\b\x1b[2K', '   ')
+        self.assertBlackBg('abcd\r\x1b[2KDef', 'Def')
+        self.assertBlackBg('abcd\b\x1b[2KDef', '   Def')
+
 
 if __name__ == '__main__':
     unittest.main()
-- 
2.14.3