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