From 47f5f580b8034429f2436cb49d9a3274d7d07d9d Mon Sep 17 00:00:00 2001 From: herr kaste Date: Sun, 19 Nov 2023 23:04:50 +0100 Subject: [PATCH] Add `[a]/[b]` to toggle sides in the inline diff Typically diffs show two sides, a and b, in red and green. For the inline diff add shortcuts to hide and show one of the sides. This comes in handy for complicated diffs to quickly switch between the previous and the next state. --- Default.sublime-keymap | 18 +++++++++ core/commands/_help_popups.py | 1 + core/commands/inline_diff.py | 73 ++++++++++++++++++++++++++++++----- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/Default.sublime-keymap b/Default.sublime-keymap index d46f168f0..5ea27c67c 100644 --- a/Default.sublime-keymap +++ b/Default.sublime-keymap @@ -150,6 +150,24 @@ { "key": "setting.git_savvy.inline_diff_view", "operator": "equal", "operand": true } ] }, + { + "keys": ["a"], + "command": "gs_inline_diff_toggle_side", + "args": {"side": "a"}, + "context": [ + { "key": "setting.command_mode", "operator": "equal", "operand": false }, + { "key": "setting.git_savvy.inline_diff_view", "operator": "equal", "operand": true } + ] + }, + { + "keys": ["b"], + "command": "gs_inline_diff_toggle_side", + "args": {"side": "b"}, + "context": [ + { "key": "setting.command_mode", "operator": "equal", "operand": false }, + { "key": "setting.git_savvy.inline_diff_view", "operator": "equal", "operand": true } + ] + }, { "keys": ["tab"], "command": "gs_inline_diff_toggle_cached_mode", diff --git a/core/commands/_help_popups.py b/core/commands/_help_popups.py index 26ba7025e..4109dc779 100644 --- a/core/commands/_help_popups.py +++ b/core/commands/_help_popups.py @@ -112,6 +112,7 @@ class gs_inline_diff_help_tooltip(GsAbstractHelpPopup): key_bindings = dedent("""\ ### Actions ### [tab] switch between staged/unstaged area + [a]/[b] show/hide the a and b (red or green) sides of the diff [l] stage line, unstage in cached mode [h] stage hunk, unstage in cached mode [L] reset line diff --git a/core/commands/inline_diff.py b/core/commands/inline_diff.py index 97385fd30..84df8841d 100644 --- a/core/commands/inline_diff.py +++ b/core/commands/inline_diff.py @@ -21,6 +21,7 @@ "gs_inline_diff", "gs_inline_diff_open", "gs_inline_diff_refresh", + "gs_inline_diff_toggle_side", "gs_inline_diff_toggle_cached_mode", "gs_inline_diff_stage_or_reset_line", "gs_inline_diff_stage_or_reset_hunk", @@ -37,7 +38,7 @@ MYPY = False if MYPY: - from typing import Dict, Iterable, List, NamedTuple, Optional, Tuple + from typing import Dict, Iterable, List, Literal, NamedTuple, Optional, Tuple from ..types import LineNo, ColNo, Row from GitSavvy.common.util.parse_diff import Hunk as InlineDiff_Hunk @@ -471,17 +472,18 @@ def draw(self, view, title, match_position, inline_diff_contents, hunks): and self.savvy_settings.get("inline_diff_auto_scroll", True) ) - replace_view_content(view, inline_diff_contents) - self.view.set_name(title) + with reapply_possible_fold(view): + replace_view_content(view, inline_diff_contents) + view.set_name(title) - if match_position: - row, col, row_offset = match_position - new_row = translate_row_to_inline_diff(view, row) - apply_position(view, new_row, col, row_offset) - elif navigate_to_first_hunk: - view.run_command("gs_inline_diff_navigate_hunk") + if match_position: + row, col, row_offset = match_position + new_row = translate_row_to_inline_diff(view, row) + apply_position(view, new_row, col, row_offset) + elif navigate_to_first_hunk: + view.run_command("gs_inline_diff_navigate_hunk") - self.highlight_regions(hunks) + self.highlight_regions(hunks) def get_inline_diff_contents(self, original_contents, diff): # type: (str, List[InlineDiff_Hunk]) -> Tuple[str, List[HunkReference]] @@ -608,6 +610,57 @@ def highlight_regions(self, replaced_lines): ) +@contextmanager +def reapply_possible_fold(view): + current_fold_mode = fold_mode(view) + if current_fold_mode == "ab": + yield + else: + view.run_command("unfold_all") + yield + view.run_command("gs_inline_diff_toggle_side", {"side": current_fold_mode}) + + +def fold_mode(view): + # type: (sublime.View) -> Literal["a", "b", "ab"] + currently_folded = view.folded_regions() + if not currently_folded: + return "ab" + if currently_folded == regions(view, "b"): + return "a" + if currently_folded == regions(view, "a"): + return "b" + return "ab" + + +def regions(view, side): + # type: (sublime.View, Literal["a", "b"]) -> List[sublime.Region] + selector = "git-savvy-removed-lines" if side == "a" else "git-savvy-added-lines" + return [sublime.Region(r.a, r.b - 1) for r in view.get_regions(selector)] + + +class gs_inline_diff_toggle_side(TextCommand, GitCommand): + def run(self, edit, side): + # type: (sublime.Edit, Literal["a", "b"]) -> None + view = self.view + currently_folded = view.folded_regions() + + if side == "a": + b_regions = regions(view, "b") + if currently_folded: + view.run_command("unfold_all") + if currently_folded == b_regions: + return + view.fold(b_regions) + else: + a_regions = regions(view, "a") + if currently_folded: + view.run_command("unfold_all") + if currently_folded == a_regions: + return + view.fold(a_regions) + + class gs_inline_diff_toggle_cached_mode(TextCommand, GitCommand): """