diff --git a/docs/Config.md b/docs/Config.md index c2565ae93..355e23d02 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -64,7 +64,8 @@ git: disableForcePushing: false parseEmoji: false os: - editCommand: '' # see 'Configuring File Editing' section + editor: '' # see 'Configuring File Editing' section + editCommand: '{{editor}} {{filename}}' openCommand: '' refresher: refreshInterval: 10 # file/submodule refresh interval in seconds @@ -230,7 +231,7 @@ Lazygit will edit a file with the first set editor in the following: ```yaml os: - editCommand: 'vim' # as an example + editor: 'vim' # as an example ``` 2. \$(git config core.editor) diff --git a/pkg/commands/files.go b/pkg/commands/files.go index f4588aac5..1d4ad5fe6 100644 --- a/pkg/commands/files.go +++ b/pkg/commands/files.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "path/filepath" + "strconv" "time" "github.com/go-errors/errors" @@ -321,8 +322,8 @@ func (c *GitCommand) ResetAndClean() error { return c.RemoveUntrackedFiles() } -func (c *GitCommand) EditFileCmdStr(filename string) (string, error) { - editor := c.Config.GetUserConfig().OS.EditCommand +func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, error) { + editor := c.Config.GetUserConfig().OS.Editor if editor == "" { editor = c.GetConfigValue("core.editor") @@ -346,5 +347,12 @@ func (c *GitCommand) EditFileCmdStr(filename string) (string, error) { return "", errors.New("No editor defined in config file, $GIT_EDITOR, $VISUAL, $EDITOR, or git config") } - return fmt.Sprintf("%s %s", editor, c.OSCommand.Quote(filename)), nil + templateValues := map[string]string{ + "editor": editor, + "filename": c.OSCommand.Quote(filename), + "line": strconv.Itoa(lineNumber), + } + + editTemplate := c.Config.GetUserConfig().OS.EditCommand + return utils.ResolvePlaceholderString(editTemplate, templateValues), nil } diff --git a/pkg/commands/files_test.go b/pkg/commands/files_test.go index fd4b9f69f..46ccda947 100644 --- a/pkg/commands/files_test.go +++ b/pkg/commands/files_test.go @@ -742,6 +742,7 @@ func TestGitCommandRemoveUntrackedFiles(t *testing.T) { func TestEditFileCmdStr(t *testing.T) { type scenario struct { filename string + configEditor string configEditCommand string command func(string, ...string) *exec.Cmd getenv func(string) string @@ -753,6 +754,7 @@ func TestEditFileCmdStr(t *testing.T) { { "test", "", + "{{editor}} {{filename}}", func(name string, arg ...string) *exec.Cmd { return secureexec.Command("exit", "1") }, @@ -769,6 +771,7 @@ func TestEditFileCmdStr(t *testing.T) { { "test", "nano", + "{{editor}} {{filename}}", func(name string, args ...string) *exec.Cmd { assert.Equal(t, "which", name) return secureexec.Command("echo") @@ -787,6 +790,7 @@ func TestEditFileCmdStr(t *testing.T) { { "test", "", + "{{editor}} {{filename}}", func(name string, arg ...string) *exec.Cmd { assert.Equal(t, "which", name) return secureexec.Command("exit", "1") @@ -805,6 +809,7 @@ func TestEditFileCmdStr(t *testing.T) { { "test", "", + "{{editor}} {{filename}}", func(name string, arg ...string) *exec.Cmd { assert.Equal(t, "which", name) return secureexec.Command("exit", "1") @@ -826,6 +831,7 @@ func TestEditFileCmdStr(t *testing.T) { { "test", "", + "{{editor}} {{filename}}", func(name string, arg ...string) *exec.Cmd { assert.Equal(t, "which", name) return secureexec.Command("exit", "1") @@ -848,6 +854,7 @@ func TestEditFileCmdStr(t *testing.T) { { "test", "", + "{{editor}} {{filename}}", func(name string, arg ...string) *exec.Cmd { assert.Equal(t, "which", name) return secureexec.Command("echo") @@ -866,6 +873,7 @@ func TestEditFileCmdStr(t *testing.T) { { "file/with space", "", + "{{editor}} {{filename}}", func(name string, args ...string) *exec.Cmd { assert.Equal(t, "which", name) return secureexec.Command("echo") @@ -881,14 +889,34 @@ func TestEditFileCmdStr(t *testing.T) { assert.Equal(t, "vi \"file/with space\"", cmdStr) }, }, + { + "open file/at line", + "vim", + "{{editor}} +{{line}} {{filename}}", + func(name string, args ...string) *exec.Cmd { + assert.Equal(t, "which", name) + return secureexec.Command("echo") + }, + func(env string) string { + return "" + }, + func(cf string) (string, error) { + return "", nil + }, + func(cmdStr string, err error) { + assert.NoError(t, err) + assert.Equal(t, "vim +1 \"open file/at line\"", cmdStr) + }, + }, } for _, s := range scenarios { gitCmd := NewDummyGitCommand() + gitCmd.Config.GetUserConfig().OS.Editor = s.configEditor gitCmd.Config.GetUserConfig().OS.EditCommand = s.configEditCommand gitCmd.OSCommand.Command = s.command gitCmd.OSCommand.Getenv = s.getenv gitCmd.getGitConfigValue = s.getGitConfigValue - s.test(gitCmd.EditFileCmdStr(s.filename)) + s.test(gitCmd.EditFileCmdStr(s.filename, 1)) } } diff --git a/pkg/config/config_default_platform.go b/pkg/config/config_default_platform.go index fabdf8a88..cb6e338df 100644 --- a/pkg/config/config_default_platform.go +++ b/pkg/config/config_default_platform.go @@ -5,7 +5,8 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform func GetPlatformDefaultConfig() OSConfig { return OSConfig{ - EditCommand: ``, + Editor: ``, + EditCommand: `{{editor}} {{filename}}`, OpenCommand: "open {{filename}}", OpenLinkCommand: "open {{link}}", } diff --git a/pkg/config/config_linux.go b/pkg/config/config_linux.go index 8399e0e47..1eed335f0 100644 --- a/pkg/config/config_linux.go +++ b/pkg/config/config_linux.go @@ -3,7 +3,8 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform func GetPlatformDefaultConfig() OSConfig { return OSConfig{ - EditCommand: ``, + Editor: ``, + EditCommand: `{{editor}} {{filename}}`, OpenCommand: `sh -c "xdg-open {{filename}} >/dev/null"`, OpenLinkCommand: `sh -c "xdg-open {{link}} >/dev/null"`, } diff --git a/pkg/config/config_windows.go b/pkg/config/config_windows.go index f6780eb6b..1bc919848 100644 --- a/pkg/config/config_windows.go +++ b/pkg/config/config_windows.go @@ -3,7 +3,8 @@ package config // GetPlatformDefaultConfig gets the defaults for the platform func GetPlatformDefaultConfig() OSConfig { return OSConfig{ - EditCommand: ``, + Editor: ``, + EditCommand: `{{editor}} {{filename}}`, OpenCommand: `cmd /c "start "" {{filename}}"`, OpenLinkCommand: `cmd /c "start "" {{link}}"`, } diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 38259f6f2..ea079948e 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -252,7 +252,10 @@ type KeybindingSubmodulesConfig struct { // OSConfig contains config on the level of the os type OSConfig struct { - // EditCommand is the command for editing a file + // Editor is the command for editing a file + Editor string `yaml:"editor,omitempty"` + + // EditCommand is the command template for editing a file EditCommand string `yaml:"editCommand,omitempty"` // OpenCommand is the command for opening a file diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index 48c0621cb..f652c3f05 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -474,13 +474,17 @@ func (gui *Gui) handleCommitEditorPress() error { } func (gui *Gui) editFile(filename string) error { - cmdStr, err := gui.GitCommand.EditFileCmdStr(filename) + return gui.editFileAtLine(filename, 1) +} + +func (gui *Gui) editFileAtLine(filename string, lineNumber int) error { + cmdStr, err := gui.GitCommand.EditFileCmdStr(filename, lineNumber) if err != nil { return gui.surfaceError(err) } return gui.runSubprocessWithSuspenseAndRefresh( - gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).PrepareShellSubProcess(cmdStr), + gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).ShellCommandFromString(cmdStr), ) } diff --git a/pkg/gui/keybindings.go b/pkg/gui/keybindings.go index f9537e755..4b20bab95 100644 --- a/pkg/gui/keybindings.go +++ b/pkg/gui/keybindings.go @@ -1301,7 +1301,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding { ViewName: "main", Contexts: []string{string(MAIN_STAGING_CONTEXT_KEY)}, Key: gui.getKey(config.Universal.Edit), - Handler: gui.handleFileEdit, + Handler: gui.handleLineByLineEdit, Description: gui.Tr.LcEditFile, }, { diff --git a/pkg/gui/line_by_line_panel.go b/pkg/gui/line_by_line_panel.go index d7a5380b2..7171864f4 100644 --- a/pkg/gui/line_by_line_panel.go +++ b/pkg/gui/line_by_line_panel.go @@ -272,3 +272,14 @@ func (gui *Gui) withLBLActiveCheck(f func(*LblPanelState) error) error { return f(state) } + +func (gui *Gui) handleLineByLineEdit() error { + file := gui.getSelectedFile() + if file == nil { + return nil + } + + lineNumber := gui.State.Panels.LineByLine.CurrentLineNumber() + + return gui.editFileAtLine(file.Name, lineNumber) +}