diff --git a/docs-master/keybindings/Keybindings_en.md b/docs-master/keybindings/Keybindings_en.md index 9eee70480..5f3598c93 100644 --- a/docs-master/keybindings/Keybindings_en.md +++ b/docs-master/keybindings/Keybindings_en.md @@ -225,6 +225,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Switch view | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Go to previous hunk | | +| `` , l `` | Go to next hunk | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Search the current view by text | | ## Main panel (patch building) @@ -331,6 +335,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Switch view | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Go to previous hunk | | +| `` , l `` | Go to next hunk | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Search the current view by text | | ## Stash diff --git a/docs-master/keybindings/Keybindings_ja.md b/docs-master/keybindings/Keybindings_ja.md index f67cdaed9..f763599f6 100644 --- a/docs-master/keybindings/Keybindings_ja.md +++ b/docs-master/keybindings/Keybindings_ja.md @@ -194,6 +194,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 | | `` `` | サイドパネルに戻る | | | `` `` | Show/hide selection | | +| `` , h `` | 前のハンクに移動 | | +| `` , l `` | 次のハンクに移動 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 現在のビューをテキストで検索 | | ## タグ @@ -308,6 +312,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 | | `` `` | サイドパネルに戻る | | | `` `` | Show/hide selection | | +| `` , h `` | 前のハンクに移動 | | +| `` , l `` | 次のハンクに移動 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 現在のビューをテキストで検索 | | ## メニュー diff --git a/docs-master/keybindings/Keybindings_ko.md b/docs-master/keybindings/Keybindings_ko.md index 4521b468b..907d4df20 100644 --- a/docs-master/keybindings/Keybindings_ko.md +++ b/docs-master/keybindings/Keybindings_ko.md @@ -86,6 +86,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 패널 전환 | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | 이전 hunk를 선택 | | +| `` , l `` | 다음 hunk를 선택 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 검색 시작 | | ## Stash @@ -165,6 +169,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 패널 전환 | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | 이전 hunk를 선택 | | +| `` , l `` | 다음 hunk를 선택 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 검색 시작 | | ## 메인 패널 (Patch Building) diff --git a/docs-master/keybindings/Keybindings_nl.md b/docs-master/keybindings/Keybindings_nl.md index 1339556a9..c172069bc 100644 --- a/docs-master/keybindings/Keybindings_nl.md +++ b/docs-master/keybindings/Keybindings_nl.md @@ -233,6 +233,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Selecteer de vorige hunk | | +| `` , l `` | Selecteer de volgende hunk | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Start met zoeken | | ## Patch bouwen @@ -309,6 +313,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Ga naar een ander paneel | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Selecteer de vorige hunk | | +| `` , l `` | Selecteer de volgende hunk | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Start met zoeken | | ## Staging diff --git a/docs-master/keybindings/Keybindings_pl.md b/docs-master/keybindings/Keybindings_pl.md index b91a75ea1..eab96368c 100644 --- a/docs-master/keybindings/Keybindings_pl.md +++ b/docs-master/keybindings/Keybindings_pl.md @@ -101,6 +101,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Idź do poprzedniego fragmentu | | +| `` , l `` | Idź do następnego fragmentu | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Szukaj w bieżącym widoku po tekście | | ## Drzewa pracy @@ -204,6 +208,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Przełącz widok | Przełącz na inny widok (zatwierdzone/niezatwierdzone zmiany). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Idź do poprzedniego fragmentu | | +| `` , l `` | Idź do następnego fragmentu | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Szukaj w bieżącym widoku po tekście | | ## Panel główny (scalanie) diff --git a/docs-master/keybindings/Keybindings_pt.md b/docs-master/keybindings/Keybindings_pt.md index 50e08defa..7eb473481 100644 --- a/docs-master/keybindings/Keybindings_pt.md +++ b/docs-master/keybindings/Keybindings_pt.md @@ -237,6 +237,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Ir para o local anterior | | +| `` , l `` | Ir para o próximo trecho | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Pesquisar na visualização atual por texto | | ## Painel Principal (preparação) @@ -340,6 +344,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Mudar de visão | Alternar para outra visão (staged/não processadas alterações). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Ir para o local anterior | | +| `` , l `` | Ir para o próximo trecho | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Pesquisar na visualização atual por texto | | ## Stash diff --git a/docs-master/keybindings/Keybindings_ru.md b/docs-master/keybindings/Keybindings_ru.md index e4edf7ba7..83bc6cba0 100644 --- a/docs-master/keybindings/Keybindings_ru.md +++ b/docs-master/keybindings/Keybindings_ru.md @@ -76,6 +76,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Выбрать предыдущую часть | | +| `` , l `` | Выбрать следующую часть | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Найти | | ## Главная панель (Индексирование) @@ -109,6 +113,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | Выбрать предыдущую часть | | +| `` , l `` | Выбрать следующую часть | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | Найти | | ## Главная панель (Слияние) diff --git a/docs-master/keybindings/Keybindings_zh-CN.md b/docs-master/keybindings/Keybindings_zh-CN.md index bef685944..b9735b50a 100644 --- a/docs-master/keybindings/Keybindings_zh-CN.md +++ b/docs-master/keybindings/Keybindings_zh-CN.md @@ -288,6 +288,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) | | `` `` | 退出回到侧边面板 | | | `` `` | Show/hide selection | | +| `` , h `` | 选择上一个区块 | | +| `` , l `` | 选择下一个区块 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 开始搜索 | | ## 正在合并 @@ -337,6 +341,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) | | `` `` | 退出回到侧边面板 | | | `` `` | Show/hide selection | | +| `` , h `` | 选择上一个区块 | | +| `` , l `` | 选择下一个区块 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 开始搜索 | | ## 状态 diff --git a/docs-master/keybindings/Keybindings_zh-TW.md b/docs-master/keybindings/Keybindings_zh-TW.md index 606c2c06d..6551f92b6 100644 --- a/docs-master/keybindings/Keybindings_zh-TW.md +++ b/docs-master/keybindings/Keybindings_zh-TW.md @@ -84,6 +84,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | 選擇上一段 | | +| `` , l `` | 選擇下一段 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 搜尋 | | ## 主面板(合併) @@ -366,6 +370,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切換至另一個面板 (已預存/未預存更改) | Switch to other view (staged/unstaged changes). | | `` `` | Exit back to side panel | | | `` `` | Show/hide selection | | +| `` , h `` | 選擇上一段 | | +| `` , l `` | 選擇下一段 | | +| `` N `` | Go to previous file | | +| `` n `` | Go to next file | | | `` / `` | 搜尋 | | ## 狀態 diff --git a/pkg/gui/controllers/helpers/diff_line_navigation.go b/pkg/gui/controllers/helpers/diff_line_navigation.go new file mode 100644 index 000000000..bba55dcd7 --- /dev/null +++ b/pkg/gui/controllers/helpers/diff_line_navigation.go @@ -0,0 +1,165 @@ +package helpers + +import ( + "github.com/jesseduffield/lazygit/pkg/gocui" +) + +// AdjacentChangeBlock returns the view line to move to for next/previous change-block +// navigation in view's displayed diff, starting from anchorViewLine. A "change block" +// is lazygit's notion of a hunk — a run of consecutive added/deleted lines separated +// by context, of which there may be several within one git @@ hunk — matching what +// the staging view's hunk navigation jumps between. forward=true targets the start of +// the next block; forward=false the start of the previous one (from mid-block this +// skips to the previous block, mirroring State.SelectPreviousHunk). ok is false when +// there's no further block, so the caller leaves the view where it is. +func (self *StagingHelper) AdjacentChangeBlock(view *gocui.View, anchorViewLine int, forward bool) (int, bool) { + contents := view.DiffLineContents() + anchor, ok := view.BufferLineForViewLine(anchorViewLine) + if !ok { + return 0, false + } + + isChange := make([]bool, len(contents)) + for i := range contents { + if info, ok := self.diffLineInfoFromContents(contents, i); ok { + isChange[i] = info.IsChange() + } + } + + target, ok := changeBlockStart(isChange, anchor, forward) + if !ok { + return 0, false + } + return view.ViewLineForBufferLine(target) +} + +// AdjacentFile returns the view line to move to for next/previous file navigation in +// view's (possibly multi-file) displayed diff, starting from anchorViewLine: the +// first row belonging to the next/previous file, found where the per-row metadata's +// file changes. ok is false at the first/last file. +func (self *StagingHelper) AdjacentFile(view *gocui.View, anchorViewLine int, forward bool) (int, bool) { + contents := view.DiffLineContents() + anchor, ok := view.BufferLineForViewLine(anchorViewLine) + if !ok { + return 0, false + } + + paths := make([]string, len(contents)) + for i := range contents { + if info, ok := self.diffLineInfoFromContents(contents, i); ok { + paths[i] = info.Path + } + } + + target, ok := fileStart(paths, anchor, forward) + if !ok { + return 0, false + } + return view.ViewLineForBufferLine(target) +} + +// changeBlockStart finds, in a diff whose lines are flagged by isChange, the first +// line of the change block adjacent to `from` in the given direction. It is the pure +// index arithmetic behind AdjacentChangeBlock, mirroring the staging view's +// State.SelectNextHunk / SelectPreviousHunk line by line. +func changeBlockStart(isChange []bool, from int, forward bool) (int, bool) { + if forward { + i := from + for i < len(isChange) && isChange[i] { // leave the current block + i++ + } + for i < len(isChange) && !isChange[i] { // skip the separating context + i++ + } + if i < len(isChange) { + return i, true + } + return 0, false + } + + i := from + for i >= 0 && isChange[i] { // leave the current block + i-- + } + for i >= 0 && !isChange[i] { // skip context, landing on the previous block's last line + i-- + } + if i < 0 { + return 0, false + } + for i > 0 && isChange[i-1] { // walk back to that block's first line + i-- + } + return i, true +} + +// fileStart finds, in a diff whose lines carry the file path they belong to (empty +// for a row no backend could place, e.g. a restructuring pager's file headers), the +// top row of the file adjacent to `from` in the given direction. It is the pure +// index arithmetic behind AdjacentFile. A file is identified by its path, so we find +// where the path changes and then back up over the neighbouring file's unplaced +// header rows, landing on its first row — the `diff --git`/`@@` header when the +// buffer is parseable, or whatever the pager renders above the file's first tagged +// line otherwise. +func fileStart(paths []string, from int, forward bool) (int, bool) { + anchorPath, ok := anchorFilePath(paths, from) + if !ok { + return 0, false + } + + if forward { + for i := from; i < len(paths); i++ { + if paths[i] != "" && paths[i] != anchorPath { + return backUpOverHeader(paths, i), true + } + } + return 0, false + } + + // Walk back past the current file (its rows and any unplaced rows) to the + // previous file's last located row, then back over that whole file to its top. + i := from + for i >= 0 && (paths[i] == "" || paths[i] == anchorPath) { + i-- + } + if i < 0 { + return 0, false + } + prevPath := paths[i] + for i > 0 && (paths[i-1] == "" || paths[i-1] == prevPath) { + i-- + } + return i, true +} + +// backUpOverHeader moves from a file's first located row up over the unplaced header +// rows directly above it, to the file's top. It stops at the previous file's last +// located row, so it never crosses into it. +func backUpOverHeader(paths []string, firstLocated int) int { + i := firstLocated + for i > 0 && paths[i-1] == "" { + i-- + } + return i +} + +// anchorFilePath returns the path of the file the anchor sits in: the first row at or +// below it that carries a path — the file whose content is at or below the top of the +// view — falling back to the nearest above when there's nothing below. Scanning down +// first matters because the anchor is often an untagged file-header row whose nearest +// tagged row is the *previous* file's content just above it; taking that would make +// next-file navigation jump back into the file just left (so a second `n` wouldn't +// advance). ok is false when no row carries a path. +func anchorFilePath(paths []string, from int) (string, bool) { + for i := from; i < len(paths); i++ { + if paths[i] != "" { + return paths[i], true + } + } + for i := from - 1; i >= 0; i-- { + if paths[i] != "" { + return paths[i], true + } + } + return "", false +} diff --git a/pkg/gui/controllers/helpers/diff_line_navigation_test.go b/pkg/gui/controllers/helpers/diff_line_navigation_test.go new file mode 100644 index 000000000..e5d1e6545 --- /dev/null +++ b/pkg/gui/controllers/helpers/diff_line_navigation_test.go @@ -0,0 +1,99 @@ +package helpers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestChangeBlockStart(t *testing.T) { + // A diff with three change blocks separated by context: + // 0 file header 1 hunk header 2 context + // 3 + 4 + (block A) + // 5 context + // 6 - (block B) + // 7 context + // 8 + (block C) + isChange := []bool{false, false, false, true, true, false, true, false, true} + + scenarios := []struct { + name string + from int + forward bool + expected int + found bool + }{ + {"forward from a header lands on the first block", 0, true, 3, true}, + {"forward from separating context lands on the next block", 5, true, 6, true}, + {"forward from the start of a block skips to the next", 3, true, 6, true}, + {"forward from inside a block skips the rest of it", 4, true, 6, true}, + {"forward from the last block finds nothing", 8, true, 0, false}, + {"backward from a later block lands on the previous one's start", 8, false, 6, true}, + {"backward from a block start lands on the previous block's start", 6, false, 3, true}, + {"backward from inside the first block finds nothing", 4, false, 0, false}, + {"backward from the first block's start finds nothing", 3, false, 0, false}, + {"backward from context lands on the preceding block's start", 7, false, 6, true}, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + got, found := changeBlockStart(isChange, s.from, s.forward) + assert.Equal(t, s.found, found) + if s.found { + assert.Equal(t, s.expected, got) + } + }) + } +} + +func TestFileStart(t *testing.T) { + // A parseable two-file diff: every row carries its file's path (the headers + // included), as the buffer parser reports. + parseable := []string{"a", "a", "a", "a", "b", "b", "b", "b"} + + // The same diff as a restructuring pager emits it: only content lines carry the + // path; the file/hunk header rows above each file are untagged (empty). + tagged := []string{"", "", "a", "a", "", "", "b", "b"} + + // Three such files, to exercise navigating from one file's untagged header to the + // next: the row just above b's header is a's content, so the anchor file must be + // found by scanning down (b), not up (a) — otherwise next-file would jump back + // into b and a second `n` couldn't advance. + taggedThree := []string{"", "", "a", "a", "", "", "b", "b", "", "", "c", "c"} + + scenarios := []struct { + name string + paths []string + from int + forward bool + expected int + found bool + }{ + {"parseable: next file lands on its header", parseable, 1, true, 4, true}, + {"parseable: next from the last file finds nothing", parseable, 6, true, 0, false}, + {"parseable: previous file lands on its header", parseable, 5, false, 0, true}, + {"parseable: previous from the first file finds nothing", parseable, 1, false, 0, false}, + + // With only content tagged, both directions still land on the file's top + // (the untagged header rows), so navigation feels the same. + {"tagged: next file lands on its header, not its first content", tagged, 2, true, 4, true}, + {"tagged: next from an untagged header still advances", tagged, 0, true, 4, true}, + {"tagged: previous file lands on its header", tagged, 7, false, 0, true}, + {"tagged: previous from the first file finds nothing", tagged, 2, false, 0, false}, + + // From b's untagged header (row 4), the anchor file is b (below), so next goes + // to c and previous goes to a — neither sticks on b. + {"tagged: next from a middle file's header advances past it", taggedThree, 4, true, 8, true}, + {"tagged: previous from a middle file's header lands on the prior file", taggedThree, 4, false, 0, true}, + } + + for _, s := range scenarios { + t.Run(s.name, func(t *testing.T) { + got, found := fileStart(s.paths, s.from, s.forward) + assert.Equal(t, s.found, found) + if s.found { + assert.Equal(t, s.expected, got) + } + }) + } +} diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go index c37a9930b..068318106 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -7,6 +7,7 @@ import ( "fmt" "path/filepath" + "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gocui" "github.com/jesseduffield/lazygit/pkg/gui/context" "github.com/jesseduffield/lazygit/pkg/gui/types" @@ -94,6 +95,26 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty Description: openPullRequestDescription, Tooltip: openPullRequestTooltip, }, + { + Keys: opts.GetKeys(opts.Config.Main.PrevHunk), + Handler: self.prevChangeBlock, + Description: self.c.Tr.PrevHunk, + }, + { + Keys: opts.GetKeys(opts.Config.Main.NextHunk), + Handler: self.nextChangeBlock, + Description: self.c.Tr.NextHunk, + }, + { + Keys: opts.GetKeys(config.Keybinding{"N"}), + Handler: self.prevFile, + Description: self.c.Tr.PrevFile, + }, + { + Keys: opts.GetKeys(config.Keybinding{"n"}), + Handler: self.nextFile, + Description: self.c.Tr.NextFile, + }, { // overriding this because we want to read all of the task's output before we start searching Keys: opts.GetKeys(opts.Config.Universal.StartSearch), @@ -161,7 +182,7 @@ func (self *MainViewController) toggleSelection() error { return nil } // Start the selection in the middle of the visible area. - showSelectionAtLine(v, v.OriginY()+v.InnerHeight()/2) + showSelectionAtLine(v, v.OriginY()+v.InnerHeight()/2, false) return nil } @@ -184,12 +205,56 @@ func (self *MainViewController) enterForLine(lineIdx int) error { } // showSelectionAtLine turns on the focused main view's selection and moves it to -// the given view line, clamped to the content. -func showSelectionAtLine(view *gocui.View, lineIdx int) { +// the given view line, clamped to the content. scrollIntoView scrolls the line into +// view if it's off-screen (used when navigating to it); a click leaves it false, the +// clicked line being visible already. +func showSelectionAtLine(view *gocui.View, lineIdx int, scrollIntoView bool) { view.Highlight = true view.HighlightInactive = false lineIdx = lo.Clamp(lineIdx, 0, view.ViewLinesHeight()-1) - view.FocusPoint(0, lineIdx, false) + view.FocusPoint(0, lineIdx, scrollIntoView) +} + +// navigate jumps the focused main view by file or change block (hunk), using find to +// locate the target row from the current anchor. The anchor is the selected line if a +// selection is showing, otherwise the top visible line. With a selection showing we +// move it to the target and scroll it into view, like the staging view; with none we +// stay in scroll mode, bringing the target to the top without selecting anything. +func (self *MainViewController) navigate(find func(*gocui.View, int, bool) (int, bool), forward bool) error { + v := self.context.GetView() + showSelection := v.Highlight + anchor := v.OriginY() + if showSelection { + anchor = v.SelectedLineIdx() + } + + target, ok := find(v, anchor, forward) + if !ok { + return nil + } + + if showSelection { + showSelectionAtLine(v, target, true) + } else { + v.SetOrigin(0, target) + } + return nil +} + +func (self *MainViewController) nextChangeBlock() error { + return self.navigate(self.c.Helpers().Staging.AdjacentChangeBlock, true) +} + +func (self *MainViewController) prevChangeBlock() error { + return self.navigate(self.c.Helpers().Staging.AdjacentChangeBlock, false) +} + +func (self *MainViewController) nextFile() error { + return self.navigate(self.c.Helpers().Staging.AdjacentFile, true) +} + +func (self *MainViewController) prevFile() error { + return self.navigate(self.c.Helpers().Staging.AdjacentFile, false) } // focusedMainViewContextForViewName maps a focused main view's view name (as @@ -333,7 +398,7 @@ func githubPullRequestLineURL(prURL string, commitSha string, relativePath strin func (self *MainViewController) onClickInAlreadyFocusedView(opts gocui.ViewMouseBindingOpts) error { // A click points at a line, so it sets the selection there; a double-click // additionally dives into staging/patch-building for that line. - showSelectionAtLine(self.context.GetView(), opts.Y) + showSelectionAtLine(self.context.GetView(), opts.Y, false) if opts.IsDoubleClick { return self.enterForLine(opts.Y) } @@ -342,7 +407,7 @@ func (self *MainViewController) onClickInAlreadyFocusedView(opts gocui.ViewMouse func (self *MainViewController) onClickInOtherViewOfMainViewPair(opts gocui.ViewMouseBindingOpts) error { self.c.Context().Push(self.context, types.OnFocusOpts{}) - showSelectionAtLine(self.context.GetView(), opts.Y) + showSelectionAtLine(self.context.GetView(), opts.Y, false) if opts.IsDoubleClick { return self.enterForLine(opts.Y) } diff --git a/pkg/gui/controllers/switch_to_focused_main_view_controller.go b/pkg/gui/controllers/switch_to_focused_main_view_controller.go index c7888135c..fcafea3b8 100644 --- a/pkg/gui/controllers/switch_to_focused_main_view_controller.go +++ b/pkg/gui/controllers/switch_to_focused_main_view_controller.go @@ -81,7 +81,7 @@ func (self *SwitchToFocusedMainViewController) focusMainView(mainViewContext typ } self.c.Context().Push(mainViewContext, types.OnFocusOpts{}) if clickedLineIdx >= 0 { - showSelectionAtLine(mainViewContext.GetView(), clickedLineIdx) + showSelectionAtLine(mainViewContext.GetView(), clickedLineIdx, false) } return nil } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index 5f426c662..acfc0b111 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -396,6 +396,8 @@ type TranslationSet struct { AskQuestion string PrevHunk string NextHunk string + PrevFile string + NextFile string PrevConflict string NextConflict string SelectPrevHunk string @@ -1561,6 +1563,8 @@ func EnglishTranslationSet() *TranslationSet { AskQuestion: "Ask Question", PrevHunk: "Go to previous hunk", NextHunk: "Go to next hunk", + PrevFile: "Go to previous file", + NextFile: "Go to next file", PrevConflict: "Previous conflict", NextConflict: "Next conflict", SelectPrevHunk: "Previous hunk",