From ec02df73cbac6ab1f0c47b5d65a9bd0e9e54b0a0 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 9 Aug 2026 20:20:48 +0200 Subject: [PATCH] Edit the selected line of a diff from the focused main view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reading a diff is often how you notice something to fix, and the file and line are right there in front of you — so pressing edit opens the file at the line under the selection, as it does in the staging view. The line number the diff shows is the line number in the version the diff is of, which for a commit's diff is not where that line sits today, so it is carried forward the same way clicking a diff-renderer hyperlink already does. A file header names no line, so it just opens the file. Co-authored-by: Claude Opus 5 (1M context) --- docs-master/keybindings/Keybindings_en.md | 2 + docs-master/keybindings/Keybindings_ja.md | 2 + docs-master/keybindings/Keybindings_ko.md | 2 + docs-master/keybindings/Keybindings_nl.md | 2 + docs-master/keybindings/Keybindings_pl.md | 2 + docs-master/keybindings/Keybindings_pt.md | 2 + docs-master/keybindings/Keybindings_ru.md | 2 + docs-master/keybindings/Keybindings_zh-CN.md | 2 + docs-master/keybindings/Keybindings_zh-TW.md | 2 + pkg/gui/controllers/main_view_controller.go | 32 +++++++++++++ .../main_view/edit_selected_diff_line.go | 45 +++++++++++++++++++ pkg/integration/tests/test_list.go | 1 + 12 files changed, 96 insertions(+) create mode 100644 pkg/integration/tests/main_view/edit_selected_diff_line.go diff --git a/docs-master/keybindings/Keybindings_en.md b/docs-master/keybindings/Keybindings_en.md index a20005ba4..463d6ac19 100644 --- a/docs-master/keybindings/Keybindings_en.md +++ b/docs-master/keybindings/Keybindings_en.md @@ -225,6 +225,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Switch view | Switch to other view (staged/unstaged changes). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | Toggle range select | | +| `` e `` | Edit file | Open file in external editor. | | `` , h `` | Go to previous hunk | | | `` , l `` | Go to next hunk | | | `` N `` | Go to previous file | | @@ -336,6 +337,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Switch view | Switch to other view (staged/unstaged changes). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | Toggle range select | | +| `` e `` | Edit file | Open file in external editor. | | `` , h `` | Go to previous hunk | | | `` , l `` | Go to next hunk | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_ja.md b/docs-master/keybindings/Keybindings_ja.md index 9c051f152..9865e2dee 100644 --- a/docs-master/keybindings/Keybindings_ja.md +++ b/docs-master/keybindings/Keybindings_ja.md @@ -194,6 +194,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 | | `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. | | `` v `` | 範囲選択を切り替え | | +| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 | | `` , h `` | 前のハンクに移動 | | | `` , l `` | 次のハンクに移動 | | | `` N `` | Go to previous file | | @@ -313,6 +314,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | ビューを切り替え | 他のビュー(ステージされた変更/ステージされていない変更)に切り替えます。 | | `` a `` | ハンクの選択を切り替える | Toggle line-by-line vs. hunk selection mode. | | `` v `` | 範囲選択を切り替え | | +| `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 | | `` , h `` | 前のハンクに移動 | | | `` , l `` | 次のハンクに移動 | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_ko.md b/docs-master/keybindings/Keybindings_ko.md index d3c83bd91..b2d3f5e6a 100644 --- a/docs-master/keybindings/Keybindings_ko.md +++ b/docs-master/keybindings/Keybindings_ko.md @@ -86,6 +86,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 패널 전환 | Switch to other view (staged/unstaged changes). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | 드래그 선택 전환 | | +| `` e `` | 파일 편집 | Open file in external editor. | | `` , h `` | 이전 hunk를 선택 | | | `` , l `` | 다음 hunk를 선택 | | | `` N `` | Go to previous file | | @@ -170,6 +171,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 패널 전환 | Switch to other view (staged/unstaged changes). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | 드래그 선택 전환 | | +| `` e `` | 파일 편집 | Open file in external editor. | | `` , h `` | 이전 hunk를 선택 | | | `` , l `` | 다음 hunk를 선택 | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_nl.md b/docs-master/keybindings/Keybindings_nl.md index a997a5525..1b2bca0c1 100644 --- a/docs-master/keybindings/Keybindings_nl.md +++ b/docs-master/keybindings/Keybindings_nl.md @@ -233,6 +233,7 @@ _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). | | `` a `` | Wissel tussen hunk selectie aan of uit | Wissel tussen regel-voor-regel of hunk selectie modus. | | `` v `` | Toggle drag selecteer | | +| `` e `` | Verander bestand | Open bestand in externe editor. | | `` , h `` | Selecteer de vorige hunk | | | `` , l `` | Selecteer de volgende hunk | | | `` N `` | Go to previous file | | @@ -314,6 +315,7 @@ _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). | | `` a `` | Wissel tussen hunk selectie aan of uit | Wissel tussen regel-voor-regel of hunk selectie modus. | | `` v `` | Toggle drag selecteer | | +| `` e `` | Verander bestand | Open bestand in externe editor. | | `` , h `` | Selecteer de vorige hunk | | | `` , l `` | Selecteer de volgende hunk | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_pl.md b/docs-master/keybindings/Keybindings_pl.md index 7f3ee08fb..3da5d4eb8 100644 --- a/docs-master/keybindings/Keybindings_pl.md +++ b/docs-master/keybindings/Keybindings_pl.md @@ -101,6 +101,7 @@ _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). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | Przełącz zaznaczenie zakresu | | +| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. | | `` , h `` | Idź do poprzedniego fragmentu | | | `` , l `` | Idź do następnego fragmentu | | | `` N `` | Go to previous file | | @@ -209,6 +210,7 @@ _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). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | Przełącz zaznaczenie zakresu | | +| `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. | | `` , h `` | Idź do poprzedniego fragmentu | | | `` , l `` | Idź do następnego fragmentu | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_pt.md b/docs-master/keybindings/Keybindings_pt.md index d6a1ee3bb..04af6d319 100644 --- a/docs-master/keybindings/Keybindings_pt.md +++ b/docs-master/keybindings/Keybindings_pt.md @@ -237,6 +237,7 @@ _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). | | `` a `` | Toggle hunk selection | Ativa/desativa modo linha por linha vs. modo de seleção por partes. | | `` v `` | Toggle range select | | +| `` e `` | Editar arquivo | Abrir arquivo no editor externo. | | `` , h `` | Ir para o local anterior | | | `` , l `` | Ir para o próximo trecho | | | `` N `` | Go to previous file | | @@ -345,6 +346,7 @@ _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). | | `` a `` | Toggle hunk selection | Ativa/desativa modo linha por linha vs. modo de seleção por partes. | | `` v `` | Toggle range select | | +| `` e `` | Editar arquivo | Abrir arquivo no editor externo. | | `` , h `` | Ir para o local anterior | | | `` , l `` | Ir para o próximo trecho | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_ru.md b/docs-master/keybindings/Keybindings_ru.md index cb6e1dd1b..feba4b266 100644 --- a/docs-master/keybindings/Keybindings_ru.md +++ b/docs-master/keybindings/Keybindings_ru.md @@ -76,6 +76,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | Переключить выборку перетаскивания | | +| `` e `` | Редактировать файл | Open file in external editor. | | `` , h `` | Выбрать предыдущую часть | | | `` , l `` | Выбрать следующую часть | | | `` N `` | Go to previous file | | @@ -114,6 +115,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | Переключиться на другую панель (проиндексированные/непроиндексированные изменения) | Switch to other view (staged/unstaged changes). | | `` a `` | Toggle hunk selection | Toggle line-by-line vs. hunk selection mode. | | `` v `` | Переключить выборку перетаскивания | | +| `` e `` | Редактировать файл | Open file in external editor. | | `` , h `` | Выбрать предыдущую часть | | | `` , l `` | Выбрать следующую часть | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_zh-CN.md b/docs-master/keybindings/Keybindings_zh-CN.md index 41659cd0a..9ab544400 100644 --- a/docs-master/keybindings/Keybindings_zh-CN.md +++ b/docs-master/keybindings/Keybindings_zh-CN.md @@ -288,6 +288,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) | | `` a `` | 切换代码块选择 | 切换逐行选择与代码块选择模式。 | | `` v `` | 切换拖动选择 | | +| `` e `` | 编辑文件 | 使用外部编辑器打开文件 | | `` , h `` | 选择上一个区块 | | | `` , l `` | 选择下一个区块 | | | `` N `` | Go to previous file | | @@ -342,6 +343,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切换到其他面板 | 切换到其他视图(已暂存/未暂存的变更) | | `` a `` | 切换代码块选择 | 切换逐行选择与代码块选择模式。 | | `` v `` | 切换拖动选择 | | +| `` e `` | 编辑文件 | 使用外部编辑器打开文件 | | `` , h `` | 选择上一个区块 | | | `` , l `` | 选择下一个区块 | | | `` N `` | Go to previous file | | diff --git a/docs-master/keybindings/Keybindings_zh-TW.md b/docs-master/keybindings/Keybindings_zh-TW.md index 20fd69618..484ac4994 100644 --- a/docs-master/keybindings/Keybindings_zh-TW.md +++ b/docs-master/keybindings/Keybindings_zh-TW.md @@ -77,6 +77,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切換至另一個面板 (已預存/未預存更改) | 切換到其他檢視(已暫存/未暫存的變更)。 | | `` a `` | 切換程式碼塊選擇 | 切換逐行選擇與程式碼塊選擇模式。 | | `` v `` | 切換拖曳選擇 | | +| `` e `` | 編輯檔案 | 使用外部編輯器開啟 | | `` , h `` | 選擇上一段 | | | `` , l `` | 選擇下一段 | | | `` N `` | Go to previous file | | @@ -364,6 +365,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct | `` `` | 切換至另一個面板 (已預存/未預存更改) | 切換到其他檢視(已暫存/未暫存的變更)。 | | `` a `` | 切換程式碼塊選擇 | 切換逐行選擇與程式碼塊選擇模式。 | | `` v `` | 切換拖曳選擇 | | +| `` e `` | 編輯檔案 | 使用外部編輯器開啟 | | `` , h `` | 選擇上一段 | | | `` , l `` | 選擇下一段 | | | `` N `` | Go to previous file | | diff --git a/pkg/gui/controllers/main_view_controller.go b/pkg/gui/controllers/main_view_controller.go index bb61bebcf..b40055025 100644 --- a/pkg/gui/controllers/main_view_controller.go +++ b/pkg/gui/controllers/main_view_controller.go @@ -71,6 +71,14 @@ func (self *MainViewController) GetKeybindings(opts types.KeybindingsOpts) []*ty DescriptionFunc: self.diffSelectionDescriptionText(self.c.Tr.ToggleRangeSelect), GetDisabledReason: self.diffSelectionDisabledReason, }, + { + Keys: opts.GetKeys(opts.Config.Universal.Edit), + Handler: self.editLine, + Description: self.c.Tr.EditFile, + DescriptionFunc: self.diffSelectionDescriptionText(self.c.Tr.EditFile), + GetDisabledReason: self.diffSelectionDisabledReason, + Tooltip: self.c.Tr.EditFileTooltip, + }, { Keys: opts.GetKeys(opts.Config.Main.PrevHunk), Handler: self.prevChangeBlock, @@ -825,6 +833,30 @@ func (self *MainViewController) handleGotoBottom() error { return nil } +func (self *MainViewController) editLine() error { + view := self.context.GetView() + if !view.Highlight { + return nil + } + + info, ok := self.c.Helpers().DiffLine.GetDiffLineInfo(view, view.SelectedLineIdx()) + if !ok { + return nil + } + + // A file-header row points at the file as a whole rather than at a line in it, so + // it opens the file without jumping anywhere — as pressing edit on a file in a side + // panel does. + if info.Type == types.DiffLineFileHeader { + return self.c.Helpers().Files.EditFiles([]string{info.Path}) + } + + // The diff may be of an older commit, whose line numbers aren't the file's current + // ones, so they have to be carried forward before we can point an editor at them. + lineNumber := self.c.Helpers().Diff.AdjustLineNumber(info.Path, info.NewLine, self.context.GetViewName()) + return self.c.Helpers().Files.EditFileAtLine(info.Path, lineNumber) +} + func (self *MainViewController) openSearch() error { if manager := self.c.GetViewBufferManagerForView(self.context.GetView()); manager != nil { manager.ReadToEnd(func() { diff --git a/pkg/integration/tests/main_view/edit_selected_diff_line.go b/pkg/integration/tests/main_view/edit_selected_diff_line.go new file mode 100644 index 000000000..ce9b2df0c --- /dev/null +++ b/pkg/integration/tests/main_view/edit_selected_diff_line.go @@ -0,0 +1,45 @@ +package main_view + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var EditSelectedDiffLine = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Open the selected line of the main view's diff in the editor, at that line of the file", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) { + config.GetUserConfig().Gui.UseHunkModeInStagingView = false + config.GetUserConfig().OS.EditAtLine = "echo {{filename}}:{{line}} > edit-command" + config.GetUserConfig().OS.Edit = "echo {{filename}} > edit-command" + }, + SetupRepo: func(shell *Shell) { + shell.CreateFileAndAdd("file1", "one\ntwo\nthree\nfour\nfive\n") + shell.Commit("one") + + shell.UpdateFile("file1", "one\ntwo\nTHREE\nfour\nfive\n") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Files(). + IsFocused(). + Press(keys.Universal.FocusMainView) + + // The addition is the third line of the file as it now stands. + t.Views().Main(). + IsFocused(). + NavigateToLine(Contains("+THREE")). + Press(keys.Universal.Edit) + + // The editor is pointed at the file by absolute path. + t.FileSystem().FileContent("edit-command", Contains("/repo/file1:3\n")) + + // A file header points at the file rather than at a line in it, so it opens the + // file with no line to jump to. + t.Views().Main(). + NavigateToLine(Contains("diff --git a/file1 b/file1")). + Press(keys.Universal.Edit) + + t.FileSystem().FileContent("edit-command", Contains("/repo/file1\n")) + }, +}) diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 674637cac..47954f48a 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -366,6 +366,7 @@ var tests = []*components.IntegrationTest{ main_view.ClickSelectsDiffLine, main_view.DragRangeWithAutoscroll, main_view.DragSelectsDiffLineRange, + main_view.EditSelectedDiffLine, main_view.EscapeDismissesSelection, main_view.FileNavigationScrollsToTheTop, main_view.HideSelectionWhenChangesVanish,