editor: Fix syntax highlighting of wide characters in tabs

- Needed a wide character aware expandtabs.
This commit is contained in:
Andrew Hamilton 2022-03-06 22:43:21 +10:00
parent 8b93c3ae4e
commit 7986f1c7d9
2 changed files with 33 additions and 4 deletions

View file

@ -54,6 +54,7 @@ def _syntax_highlight(text, lexer, style):
token_style["underline"]) token_style["underline"])
default_bg_color = _parse_rgb(style.background_color) default_bg_color = _parse_rgb(style.background_color)
default_style = termstr.CharStyle(bg_color=default_bg_color) default_style = termstr.CharStyle(bg_color=default_bg_color)
text = expandtabs(text)
text = fill3.join("", [termstr.TermStr( text = fill3.join("", [termstr.TermStr(
text, _char_style_for_token_type(token_type, default_bg_color, default_style)) text, _char_style_for_token_type(token_type, default_bg_color, default_style))
for token_type, text in pygments.lex(text, lexer)]) for token_type, text in pygments.lex(text, lexer)])
@ -84,7 +85,7 @@ class Text:
return self.actual_text[line_index] return self.actual_text[line_index]
def _convert_line(self, line, max_line_length): def _convert_line(self, line, max_line_length):
return line.ljust(max_line_length) return expand_str(line).ljust(max_line_length)
def __setitem__(self, key, value): def __setitem__(self, key, value):
if type(key) == int: if type(key) == int:
@ -99,7 +100,7 @@ class Text:
padding = self.padding_char * (max_new_lengths - self.max_line_length) padding = self.padding_char * (max_new_lengths - self.max_line_length)
self.text = [line + padding for line in self.text] self.text = [line + padding for line in self.text]
self.max_line_length = max_new_lengths self.max_line_length = max_new_lengths
converted_lines = [self._convert_line(line, self.max_line_length) for line in fixed_lines] converted_lines = [self._convert_line(line, self.max_line_length) for line in new_lines]
self.text[slice_], self.actual_text[slice_] = converted_lines, new_lines self.text[slice_], self.actual_text[slice_] = converted_lines, new_lines
new_max_line_length = max(len(expand_str(line)) for line in self.actual_text) new_max_line_length = max(len(expand_str(line)) for line in self.actual_text)
if new_max_line_length < self.max_line_length: if new_max_line_length < self.max_line_length:
@ -132,8 +133,9 @@ class Code(Text):
Text.__init__(self, text, padding_char) Text.__init__(self, text, padding_char)
def _convert_line(self, line, max_line_length): def _convert_line(self, line, max_line_length):
return (termstr.TermStr(line.ljust(max_line_length)) if self.theme is None highlighted_line = (termstr.TermStr(line) if self.theme is None
else _syntax_highlight(line.ljust(max_line_length), self.lexer, self.theme)) else _syntax_highlight(line, self.lexer, self.theme))
return highlighted_line.ljust(max_line_length, fillchar=self.padding_char)
def syntax_highlight_all(self): def syntax_highlight_all(self):
if self.theme is None: if self.theme is None:
@ -163,6 +165,23 @@ def highlight_part(line, start, end):
line[end:]) line[end:])
@functools.lru_cache(maxsize=500)
def expandtabs(text):
result = []
for line in text.splitlines(keepends=True):
parts = line.split("\t")
if len(parts) == 1:
result.append(line)
continue
result.append(parts[0])
line_length = cwcwidth.wcswidth(parts[0])
for part in parts[1:]:
spacing = 8 - line_length % 8
result.extend([" " * spacing, part])
line_length += spacing + cwcwidth.wcswidth(part)
return "".join(result)
@functools.lru_cache(maxsize=500) @functools.lru_cache(maxsize=500)
def expand_str_inverse(str_): def expand_str_inverse(str_):
result = [] result = []

View file

@ -69,6 +69,16 @@ class TextWidgetTestCase(unittest.TestCase):
self.assertEqual(editor.expand_str_inverse("\tb"), [0, 0, 1, 1, 1, 1, 1, 1, 2]) self.assertEqual(editor.expand_str_inverse("\tb"), [0, 0, 1, 1, 1, 1, 1, 1, 2])
class ExpandTabsTestCase(unittest.TestCase):
def test_expand_tabs(self):
self.assertEqual(editor.expandtabs(""), "")
self.assertEqual(editor.expandtabs("a"), "a")
self.assertEqual(editor.expandtabs("a\tb"), "a b")
self.assertEqual(editor.expandtabs("a♓\tb"), "a♓ b")
self.assertEqual(editor.expandtabs("c\na♓\tb"), "c\na♓ b")
class EditorTestCase(unittest.TestCase): class EditorTestCase(unittest.TestCase):
def setUp(self): def setUp(self):