From 7a0f5d2f25a807e80a691c1346043a993d9d2214 Mon Sep 17 00:00:00 2001 From: hut Date: Thu, 3 Jun 2010 21:15:18 +0200 Subject: added gui/ansi.py from David Barnetts branch --- ranger/gui/ansi.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ test/tc_ansi.py | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 ranger/gui/ansi.py create mode 100644 test/tc_ansi.py diff --git a/ranger/gui/ansi.py b/ranger/gui/ansi.py new file mode 100644 index 00000000..7d5f2623 --- /dev/null +++ b/ranger/gui/ansi.py @@ -0,0 +1,66 @@ +# Copyright (C) 2010 David Barnett +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import re + +ansi_re = re.compile('(\x1b' + r'\[\d+(?:;\d+)*?m)') + +def split_ansi_from_text(ansi_text): + return ansi_re.split(ansi_text) + +def text_with_fg_bg(ansi_text): + for chunk in split_ansi_from_text(ansi_text): + if chunk.startswith('\x1b'): + attr_text = re.match('\x1b' + r'\[(.*?)m', chunk).group(1) + fg, bg = -1, -1 + for attr in attr_text.split(';'): + m = re.match('^3(\d)$', attr) + if m: + fg = int(m.group(1)) + m = re.match('^4(\d)$', attr) + if m: + bg = int(m.group(1)) + yield (fg, bg) + else: + yield chunk + +def char_len(ansi_text): + return len(ansi_re.sub('', ansi_text)) + +def char_slice(ansi_text, start, end): + slice_chunks = [] + # skip to start + last_color = None + skip_len_left = start + len_left = end - start + for chunk in split_ansi_from_text(ansi_text): + m = ansi_re.match(chunk) + if m: + last_color = chunk + else: + if skip_len_left > len(chunk): + skip_len_left -= len(chunk) + else: # finished skipping to start + if skip_len_left > 0: + chunk = chunk[skip_len_left:] + chunk_left = chunk[:len_left] + if len(chunk_left): + if last_color is not None: + slice_chunks.append(last_color) + slice_chunks.append(chunk_left) + len_left -= len(chunk_left) + if len_left == 0: + break + return ''.join(slice_chunks) diff --git a/test/tc_ansi.py b/test/tc_ansi.py new file mode 100644 index 00000000..5fc4e53b --- /dev/null +++ b/test/tc_ansi.py @@ -0,0 +1,45 @@ +# Copyright (C) 2010 David Barnett +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +if __name__ == '__main__': from __init__ import init; init() + +import unittest +from ranger.gui import ansi + +class TestDisplayable(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def test_char_len(self): + ansi_string = "X" + self.assertEqual(ansi.char_len(ansi_string), 1) + + def test_char_len2(self): + ansi_string = "XY" + self.assertEqual(ansi.char_len(ansi_string), 2) + + def test_char_len3(self): + ansi_string = "XY" + self.assertEqual(ansi.char_len(ansi_string), 2) + + def test_char_slice(self): + ansi_string = "XY" + self.assertEqual(ansi.char_slice(ansi_string, 0, 1), "X") + +if __name__ == '__main__': + unittest.main() -- cgit 1.4.1-2-gfad0