From 3cb877406fba7c49bf4f5f4ee7808eadfa9e4750 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Sat, 12 Mar 2022 14:28:42 +1000 Subject: [PATCH] Coding style --- diff_edit/__init__.py | 103 ++++++++++++++++++++------------------ diff_edit/editor.py | 12 +++-- tests/diff_editor_test.py | 2 +- 3 files changed, 61 insertions(+), 56 deletions(-) diff --git a/diff_edit/__init__.py b/diff_edit/__init__.py index 0f01316..c46ac4b 100755 --- a/diff_edit/__init__.py +++ b/diff_edit/__init__.py @@ -8,6 +8,7 @@ Edit two files side by side, showing differences. import asyncio +import contextlib import difflib import functools import os @@ -50,7 +51,7 @@ _LINE_MAP = {"━": 0b0101, "┃": 0b1010, "┏": 0b0110, "┗": 0b1100, "┛": _LINE_MAP_INVERTED = {v: k for k, v in _LINE_MAP.items()} -@functools.lru_cache() +@functools.cache def union_box_line(a_line, b_line): return _LINE_MAP_INVERTED[_LINE_MAP[a_line] | _LINE_MAP[b_line]] @@ -66,12 +67,13 @@ def highlight_str(line, bg_color, transparency): @functools.lru_cache(maxsize=500) -def get_diff(a_text, b_text): +def line_diff(a_text, b_text): return difflib.SequenceMatcher(a=a_text, b=b_text).get_opcodes() def get_lines(text_editor, start, end): - return tuple(text_editor.text_widget[start:end]), tuple(text_editor.text_widget.appearance_interval((start, end))) + return (tuple(text_editor.text_widget[start:end]), + tuple(text_editor.text_widget.appearance_interval((start, end)))) def replace_part(a_str, start, end, part): @@ -86,7 +88,7 @@ def highlight_modification(a_lines, b_lines, show_sub_highlights): right_line = fill3.join("\n", tuple(colored_line[:len(line)] for line, colored_line in zip(*b_lines))) if show_sub_highlights: - diff = get_diff(left_line.data, right_line.data) + diff = line_diff(left_line.data, right_line.data) for opcode, left_start, left_end, right_start, right_end in diff: color = termstr.Color.white if opcode == "replace" else termstr.Color.green if opcode == "delete" or opcode == "replace": @@ -148,50 +150,57 @@ class DiffEditor: self.right_editor = editor.Editor() self.right_editor.load(right_path) self.show_sub_highlights = True - self.diff = None - - def highlight_lines(appearance, start, end, opcode, change_opcode): - if opcode == change_opcode: - for index in range(start, end): - appearance[index] = highlight_str(appearance[index], (0, 200, 0), 0.6) - - def left_highlight_lines(appearance): - view_x, view_y = self.left_view.position - for op, left_start, left_end, right_start, right_end in self.diff: - if (op == "replace" - and ranges_overlap((left_start, left_end), (view_y, view_y + len(appearance)))): - left_lines = get_lines(self.left_editor, left_start, left_end) - right_lines = get_lines(self.right_editor, right_start, right_end) - left_appearance, right_appearance = highlight_modification( - left_lines, right_lines, self.show_sub_highlights) - overlay_list(appearance, left_appearance, left_start - view_y) - highlight_lines(appearance, max(left_start, view_y) - view_y, - min(left_end, view_y + len(appearance)) - view_y, op, "delete") - return appearance - - def right_highlight_lines(appearance): - view_x, view_y = self.right_view.position - for op, left_start, left_end, right_start, right_end in self.diff: - if (op == "replace" - and ranges_overlap((right_start, right_end), (view_y, view_y + len(appearance)))): - left_lines = get_lines(self.left_editor, left_start, left_end) - right_lines = get_lines(self.right_editor, right_start, right_end) - left_appearance, right_appearance = highlight_modification( - left_lines, right_lines, self.show_sub_highlights) - overlay_list(appearance, right_appearance, right_start - view_y) - highlight_lines(appearance, max(right_start, view_y) - view_y, - min(right_end, view_y + len(appearance)) - view_y, op, "insert") - return appearance - - left_decor = editor.Decor(self.left_editor.text_widget, left_highlight_lines) + left_decor = editor.Decor(self.left_editor.text_widget, self._left_highlight_lines) self.left_editor.decor_widget.widget = left_decor self.left_view = self.left_editor.view_widget - right_decor = editor.Decor(self.right_editor.text_widget, right_highlight_lines) + right_decor = editor.Decor(self.right_editor.text_widget, self._right_highlight_lines) self.right_editor.decor_widget.widget = right_decor self.right_view = self.right_editor.view_widget self.right_editor.is_editing = False self.editors = [self.left_editor, self.right_editor] + @functools.cached_property + def diff(self): + return difflib.SequenceMatcher(a=self.left_editor.text_widget, + b=self.right_editor.text_widget).get_opcodes() + + def diff_changed(self): + with contextlib.suppress(AttributeError): + del self.diff + + def _highlight_lines(self, appearance, start, end, opcode, change_opcode): + if opcode == change_opcode: + for index in range(start, end): + appearance[index] = highlight_str(appearance[index], (0, 200, 0), 0.6) + + def _left_highlight_lines(self, appearance): + view_x, view_y = self.left_view.position + view_end_y = view_y + len(appearance) + for op, left_start, left_end, right_start, right_end in self.diff: + if op == "replace" and ranges_overlap((left_start, left_end), (view_y, view_end_y)): + left_lines = get_lines(self.left_editor, left_start, left_end) + right_lines = get_lines(self.right_editor, right_start, right_end) + left_appearance, right_appearance = highlight_modification( + left_lines, right_lines, self.show_sub_highlights) + overlay_list(appearance, left_appearance, left_start - view_y) + self._highlight_lines(appearance, max(left_start, view_y) - view_y, + min(left_end, view_end_y) - view_y, op, "delete") + return appearance + + def _right_highlight_lines(self, appearance): + view_x, view_y = self.right_view.position + view_end_y = view_y + len(appearance) + for op, left_start, left_end, right_start, right_end in self.diff: + if op == "replace" and ranges_overlap((right_start, right_end), (view_y, view_end_y)): + left_lines = get_lines(self.left_editor, left_start, left_end) + right_lines = get_lines(self.right_editor, right_start, right_end) + left_appearance, right_appearance = highlight_modification( + left_lines, right_lines, self.show_sub_highlights) + overlay_list(appearance, right_appearance, right_start - view_y) + self._highlight_lines(appearance, max(right_start, view_y) - view_y, + min(right_end, view_end_y) - view_y, op, "insert") + return appearance + def _equivalent_line(self, y): for opcode, left_start, left_end, right_start, right_end in self.diff: if self.editors[0] == self.right_editor: @@ -229,12 +238,12 @@ class DiffEditor: self.left_editor.text_widget[left_start:left_end] = \ [self.right_editor.text_widget[line_num] for line_num in range(right_start, right_end)] - self.diff = None + self.diff_changed() elif x == right_x and right_y == y: self.right_editor.text_widget[right_start:right_end] = \ [self.left_editor.text_widget[line_num] for line_num in range(left_start, left_end)] - self.diff = None + self.diff_changed() def on_mouse_press(self, x, y, left_x, right_x): if x < left_x: @@ -261,10 +270,6 @@ class DiffEditor: elif x > right_x: self.right_editor.on_mouse_drag(x - right_x - 1, y) - def update_diff(self): - self.diff = difflib.SequenceMatcher(a=self.left_editor.text_widget, - b=self.right_editor.text_widget).get_opcodes() - def toggle_highlights(self): self.show_sub_highlights = not self.show_sub_highlights @@ -307,7 +312,7 @@ class DiffEditor: self.KEY_MAP[term_code](self) else: self.editors[0].on_keyboard_input(term_code) - self.diff = None + self.diff_changed() fill3.APPEARANCE_CHANGED_EVENT.set() def on_mouse_input(self, term_code): @@ -351,8 +356,6 @@ class DiffEditor: def appearance_for(self, dimensions): width, height = self.last_dimensions = dimensions - if self.diff is None: - self.update_diff() self.follow_scroll() divider_width = 3 left_width = (width - divider_width) // 2 diff --git a/diff_edit/editor.py b/diff_edit/editor.py index 0f2810e..8075287 100755 --- a/diff_edit/editor.py +++ b/diff_edit/editor.py @@ -275,11 +275,13 @@ class Editor: result[start_y] = highlight_part(result[start_y], screen_start_x, screen_end_x) else: if 0 <= start_y < len(result): - result[start_y] = highlight_part(result[start_y], screen_start_x, len(result[start_y])) + result[start_y] = highlight_part(result[start_y], screen_start_x, + len(result[start_y])) view_x, view_y = self.view_widget.position for line_num in range(max(start_y+1, 0), min(end_y, self.last_height)): if 0 <= line_num < len(result): - result[line_num] = highlight_part(result[line_num], 0, len(result[line_num])) + result[line_num] = highlight_part(result[line_num], 0, + len(result[line_num])) if 0 <= end_y < len(result): result[end_y] = highlight_part(result[end_y], 0, screen_end_x) if self.cursor_x >= len(result[0]): @@ -288,9 +290,9 @@ class Editor: screen_x = len(expand_str(self.text_widget[self.cursor_y][:self.cursor_x])) screen_x_after = (screen_x + 1 if self._current_character() in ["\t", "\n"] else len(expand_str(self.text_widget[self.cursor_y][:self.cursor_x+1]))) - result[self.cursor_y - view_y] = (cursor_line[:screen_x] + - termstr.TermStr(cursor_line[screen_x:screen_x_after]).invert() + - cursor_line[screen_x_after:]) + result[self.cursor_y - view_y] = ( + cursor_line[:screen_x] + termstr.TermStr(cursor_line[screen_x:screen_x_after]).invert() + + cursor_line[screen_x_after:]) return result def set_text(self, text): diff --git a/tests/diff_editor_test.py b/tests/diff_editor_test.py index be3667d..f752256 100755 --- a/tests/diff_editor_test.py +++ b/tests/diff_editor_test.py @@ -14,6 +14,6 @@ class OverlayListTestCase(unittest.TestCase): self.assertEqual(diff_edit.overlay_list([1, 2, 3, 4], [5, 6], -1), [6, 2, 3, 4]) self.assertEqual(diff_edit.overlay_list([5, 6], [1, 2, 3, 4], -1), [2, 3]) - + if __name__ == "__main__": unittest.main()