diff --git a/docs/editing.md b/docs/editing.md index 50df929..9c16c9c 100644 --- a/docs/editing.md +++ b/docs/editing.md @@ -255,7 +255,7 @@ In split diffs, changed text rows use muted red backgrounds in Before and muted Missing counterparts use adjacent muted `╱` diagonals (U+2571, box drawings light diagonal) on the normal terminal background, one per column. Unlike the text slash `/`, this glyph is designed to reach cell corners so diagonals can join end to end across successive rows. Pixel-perfect joins depend on the terminal's font and line-spacing rendering. Actual empty source lines retain their normal/change background and are not hatched. -In Normal mode, Tab/Shift+Tab cycles focus between Files and the visible text panes, skipping absent panes. Switching Before/After synchronizes the primary cursor by aligned diff row and display column. A missing counterpart uses the nearest visible source row; shorter lines clamp the column while retaining the preferred display column for the return trip. Vertical wheel input scrolls the visible panes without moving the focused text caret; switching sides by vertical wheel also synchronizes the cursor. Over Files it navigates visible tree rows without opening them. Horizontal wheel input pans only the hovered text pane, with independent Before/After offsets and no change to focus, cursor, or selections; over Files it is consumed. Escape cancels modes, prompts, or filtering, or returns picker focus to the last bound text pane; **it never closes the diff**. Use **`:diff.close`**, find `diff.close` in the palette, or bind **`config.core.diff.close`** to leave the comparison explicitly. The dotted command is intentional; there is no `:diffclose` alias. +In Normal mode, Tab/Shift+Tab cycles focus between Files and the visible text panes, skipping absent panes. Switching Before/After synchronizes the primary cursor by aligned diff row and display column. A missing counterpart uses the nearest visible source row; shorter lines clamp the column while retaining the preferred display column for the return trip. Vertical wheel input scrolls the visible panes without moving the focused text caret; switching sides by vertical wheel also synchronizes the cursor. Over Files it navigates visible tree rows without opening them. Horizontal wheel input in either split synchronizes the Before/After horizontal offsets, bounded by the longer side, without changing focus, cursor, or selections; over Files it is consumed. Added/deleted files retain single-pane scrolling. Escape cancels modes, prompts, or filtering, or returns picker focus to the last bound text pane; **it never closes the diff**. Use **`:diff.close`**, find `diff.close` in the palette, or bind **`config.core.diff.close`** to leave the comparison explicitly. The dotted command is intentional; there is no `:diffclose` alias. Both diff panes apply available semantic tokens from their own buffer, independently of keyboard focus. Historical snapshots never borrow the working buffer's tokens; live After tokens must match the displayed text revision. Missing or invalidated tokens leave Tree-sitter syntax colors intact. Semantic colors and modifiers preserve the change backgrounds. diff --git a/src/diff.rs b/src/diff.rs index 5d7e5e4..a23f444 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -615,6 +615,11 @@ impl DiffState { Focus::After => self.max_after, Focus::Files => return 0, }; + let columns = if self.pane_layout() == PaneLayout::Split { + self.max_before.max(self.max_after) + } else { + columns + }; left.min(columns.saturating_add(1).saturating_sub(width.max(1))) } @@ -630,10 +635,15 @@ impl DiffState { left.saturating_sub(3) }; let left = self.bound_horizontal(focus, left, width); - match focus { - Focus::Before => self.left_before = left, - Focus::After => self.left_after = left, - Focus::Files => unreachable!(), + if self.pane_layout() == PaneLayout::Split { + self.left_before = left; + self.left_after = left; + } else { + match focus { + Focus::Before => self.left_before = left, + Focus::After => self.left_after = left, + Focus::Files => unreachable!(), + } } self.follow_caret = false; } diff --git a/src/tui.rs b/src/tui.rs index 447205e..eb0b2e2 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -4317,16 +4317,16 @@ mod tests { .unwrap(); assert!(screen_text(&terminal).contains("ore.before.")); assert_eq!(session.editor().diff().unwrap().left_before, 3); - assert_eq!(session.editor().diff().unwrap().left_after, 0); + assert_eq!(session.editor().diff().unwrap().left_after, 3); let mut shift = document_wheel(MouseEventKind::ScrollDown, after.x + 1, after.y + 1); shift.modifiers = KeyModifiers::SHIFT; assert!(viewports.scroll(&mut session, shift)); terminal .draw(|frame| viewports.draw_session(frame, &mut session)) .unwrap(); - assert!(screen_text(&terminal).contains("er.after.")); - assert_eq!(session.editor().diff().unwrap().left_before, 3); - assert_eq!(session.editor().diff().unwrap().left_after, 3); + assert!(screen_text(&terminal).contains("after.after.")); + assert_eq!(session.editor().diff().unwrap().left_before, 6); + assert_eq!(session.editor().diff().unwrap().left_after, 6); assert_eq!(session.editor().diff().unwrap().top, 0); assert_eq!(session.editor().diff().unwrap().focus, initial_focus); assert_eq!(session.editor().cursor(), initial_cursor);