diff --git a/README.md b/README.md index 4b14fd0..c405f13 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,7 @@ Keybindings are vim-like by default. - gg / G: jump to top / bottom - zz: recenter on selected hunk start - j / k / up / down: vertical scroll +- ctrl+u / ctrl+d: half-page up / down - H / L / left / right: horizontal scroll ### Selection and apply diff --git a/internal/tui/tui.go b/internal/tui/tui.go index e03c4b3..77952a5 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -28,6 +28,8 @@ const ( keyQuit = "q" keyCtrlC = "ctrl+c" keyCtrlS = "ctrl+s" + keyCtrlD = "ctrl+d" + keyCtrlU = "ctrl+u" keyNextConflict = "n" keyPrevConflict = "p" keySelectOurs = "h" @@ -71,6 +73,7 @@ var resolverKeyHelp = []keyHelpEntry{ {key: "gg/G", description: "top/bottom"}, {key: "zz", description: "recenter hunk"}, {key: "j/k/up/down", description: "scroll"}, + {key: "ctrl+u/ctrl+d", description: "half-page"}, {key: "H/L/left/right", description: "scroll"}, {key: "h", description: "ours"}, {key: "l", description: "theirs"}, @@ -99,6 +102,8 @@ var resolverKeyActions = map[string]keyAction{ keyScrollDown: (*model).handleScrollDown, keyScrollUp: (*model).handleScrollUp, keyArrowLeft: (*model).handleScrollLeft, + keyCtrlU: (*model).handleHalfPageUp, + keyCtrlD: (*model).handleHalfPageDown, keyArrowRight: (*model).handleScrollRight, keyArrowDown: (*model).handleScrollDown, keyArrowUp: (*model).handleScrollUp, @@ -873,6 +878,16 @@ func (m *model) handleScrollUp() (tea.Cmd, error) { return nil, nil } +func (m *model) handleHalfPageDown() (tea.Cmd, error) { + m.scrollVertical(m.halfPageScrollDelta()) + return nil, nil +} + +func (m *model) handleHalfPageUp() (tea.Cmd, error) { + m.scrollVertical(-m.halfPageScrollDelta()) + return nil, nil +} + func (m *model) handleApplyOurs() (tea.Cmd, error) { if err := m.applyResolution(markers.ResolutionOurs); err != nil { return nil, fmt.Errorf("failed to apply ours: %w", err) @@ -1114,6 +1129,15 @@ func (m *model) scrollHorizontal(delta int) { apply(&m.viewportTheirs) } +func (m *model) halfPageScrollDelta() int { + height := max(m.viewportOurs.Height, m.viewportResult.Height) + delta := height / 2 + if delta < 1 { + return 1 + } + return delta +} + func (m *model) scrollVertical(delta int) { apply := func(viewportModel *viewport.Model) { if delta < 0 { diff --git a/internal/tui/tui_test.go b/internal/tui/tui_test.go index 3d70f88..0454d65 100644 --- a/internal/tui/tui_test.go +++ b/internal/tui/tui_test.go @@ -981,6 +981,34 @@ func TestUpdateVerticalScrollKeys(t *testing.T) { } } +func TestUpdateHalfPageScrollKeys(t *testing.T) { + lines := strings.Join([]string{"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"}, "\n") + m := model{ + viewportOurs: viewport.New(8, 6), + viewportResult: viewport.New(8, 6), + viewportTheirs: viewport.New(8, 6), + } + for _, viewportModel := range []*viewport.Model{&m.viewportOurs, &m.viewportResult, &m.viewportTheirs} { + viewportModel.SetContent(lines) + } + + updated, _ := m.Update(tea.KeyMsg{Type: tea.KeyCtrlD}) + result := updated.(model) + for _, viewportModel := range []*viewport.Model{&result.viewportOurs, &result.viewportResult, &result.viewportTheirs} { + if viewportModel.YOffset != 3 { + t.Fatalf("YOffset = %d, want 3 after ctrl+d", viewportModel.YOffset) + } + } + + updated, _ = result.Update(tea.KeyMsg{Type: tea.KeyCtrlU}) + result = updated.(model) + for _, viewportModel := range []*viewport.Model{&result.viewportOurs, &result.viewportResult, &result.viewportTheirs} { + if viewportModel.YOffset != 0 { + t.Fatalf("YOffset = %d, want 0 after ctrl+u", viewportModel.YOffset) + } + } +} + func TestUpdateWriteKey(t *testing.T) { tmpDir := t.TempDir() mergedPath := filepath.Join(tmpDir, "merged.txt")