mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-04-23 12:18:51 +02:00
Merge pull request #1413 from Ryooooooga/feature/edit-line
Make os.editCommand customizable using template
This commit is contained in:
commit
487ad196a7
@ -65,6 +65,7 @@ git:
|
|||||||
parseEmoji: false
|
parseEmoji: false
|
||||||
os:
|
os:
|
||||||
editCommand: '' # see 'Configuring File Editing' section
|
editCommand: '' # see 'Configuring File Editing' section
|
||||||
|
editCommandTemplate: '{{editor}} {{filename}}'
|
||||||
openCommand: ''
|
openCommand: ''
|
||||||
refresher:
|
refresher:
|
||||||
refreshInterval: 10 # file/submodule refresh interval in seconds
|
refreshInterval: 10 # file/submodule refresh interval in seconds
|
||||||
@ -241,6 +242,24 @@ os:
|
|||||||
|
|
||||||
Lazygit will log an error if none of these options are set.
|
Lazygit will log an error if none of these options are set.
|
||||||
|
|
||||||
|
You can specify a line number you are currently at when in the line-by-line mode.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
os:
|
||||||
|
editCommand: 'vim'
|
||||||
|
editCommandTemplate: '{{editor}} +{{line}} {{filename}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
os:
|
||||||
|
editCommand: 'code'
|
||||||
|
editCommandTemplate: '{{editor}} --goto {{filename}}:{{line}}'
|
||||||
|
```
|
||||||
|
|
||||||
|
`{{editor}}` in `editCommandTemplate` is replaced with the value of `editCommand`.
|
||||||
|
|
||||||
### Recommended Config Values
|
### Recommended Config Values
|
||||||
|
|
||||||
for users of VSCode
|
for users of VSCode
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
@ -321,7 +322,7 @@ func (c *GitCommand) ResetAndClean() error {
|
|||||||
return c.RemoveUntrackedFiles()
|
return c.RemoveUntrackedFiles()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *GitCommand) EditFileCmdStr(filename string) (string, error) {
|
func (c *GitCommand) EditFileCmdStr(filename string, lineNumber int) (string, error) {
|
||||||
editor := c.Config.GetUserConfig().OS.EditCommand
|
editor := c.Config.GetUserConfig().OS.EditCommand
|
||||||
|
|
||||||
if editor == "" {
|
if 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 "", 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),
|
||||||
|
}
|
||||||
|
|
||||||
|
editCmdTemplate := c.Config.GetUserConfig().OS.EditCommandTemplate
|
||||||
|
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
|
||||||
}
|
}
|
||||||
|
@ -741,18 +741,20 @@ func TestGitCommandRemoveUntrackedFiles(t *testing.T) {
|
|||||||
// TestEditFileCmdStr is a function.
|
// TestEditFileCmdStr is a function.
|
||||||
func TestEditFileCmdStr(t *testing.T) {
|
func TestEditFileCmdStr(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
filename string
|
filename string
|
||||||
configEditCommand string
|
configEditCommand string
|
||||||
command func(string, ...string) *exec.Cmd
|
configEditCommandTemplate string
|
||||||
getenv func(string) string
|
command func(string, ...string) *exec.Cmd
|
||||||
getGitConfigValue func(string) (string, error)
|
getenv func(string) string
|
||||||
test func(string, error)
|
getGitConfigValue func(string) (string, error)
|
||||||
|
test func(string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
"",
|
"",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
return secureexec.Command("exit", "1")
|
return secureexec.Command("exit", "1")
|
||||||
},
|
},
|
||||||
@ -769,6 +771,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
"nano",
|
"nano",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, args ...string) *exec.Cmd {
|
func(name string, args ...string) *exec.Cmd {
|
||||||
assert.Equal(t, "which", name)
|
assert.Equal(t, "which", name)
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
@ -787,6 +790,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
"",
|
"",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
assert.Equal(t, "which", name)
|
assert.Equal(t, "which", name)
|
||||||
return secureexec.Command("exit", "1")
|
return secureexec.Command("exit", "1")
|
||||||
@ -805,6 +809,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
"",
|
"",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
assert.Equal(t, "which", name)
|
assert.Equal(t, "which", name)
|
||||||
return secureexec.Command("exit", "1")
|
return secureexec.Command("exit", "1")
|
||||||
@ -826,6 +831,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
"",
|
"",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
assert.Equal(t, "which", name)
|
assert.Equal(t, "which", name)
|
||||||
return secureexec.Command("exit", "1")
|
return secureexec.Command("exit", "1")
|
||||||
@ -848,6 +854,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"test",
|
"test",
|
||||||
"",
|
"",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, arg ...string) *exec.Cmd {
|
func(name string, arg ...string) *exec.Cmd {
|
||||||
assert.Equal(t, "which", name)
|
assert.Equal(t, "which", name)
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
@ -866,6 +873,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
{
|
{
|
||||||
"file/with space",
|
"file/with space",
|
||||||
"",
|
"",
|
||||||
|
"{{editor}} {{filename}}",
|
||||||
func(name string, args ...string) *exec.Cmd {
|
func(name string, args ...string) *exec.Cmd {
|
||||||
assert.Equal(t, "which", name)
|
assert.Equal(t, "which", name)
|
||||||
return secureexec.Command("echo")
|
return secureexec.Command("echo")
|
||||||
@ -881,14 +889,34 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
assert.Equal(t, "vi \"file/with space\"", cmdStr)
|
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 {
|
for _, s := range scenarios {
|
||||||
gitCmd := NewDummyGitCommand()
|
gitCmd := NewDummyGitCommand()
|
||||||
gitCmd.Config.GetUserConfig().OS.EditCommand = s.configEditCommand
|
gitCmd.Config.GetUserConfig().OS.EditCommand = s.configEditCommand
|
||||||
|
gitCmd.Config.GetUserConfig().OS.EditCommandTemplate = s.configEditCommandTemplate
|
||||||
gitCmd.OSCommand.Command = s.command
|
gitCmd.OSCommand.Command = s.command
|
||||||
gitCmd.OSCommand.Getenv = s.getenv
|
gitCmd.OSCommand.Getenv = s.getenv
|
||||||
gitCmd.getGitConfigValue = s.getGitConfigValue
|
gitCmd.getGitConfigValue = s.getGitConfigValue
|
||||||
s.test(gitCmd.EditFileCmdStr(s.filename))
|
s.test(gitCmd.EditFileCmdStr(s.filename, 1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,14 @@ func ModifiedPatchForLines(log *logrus.Entry, filename string, diffText string,
|
|||||||
|
|
||||||
// I want to know, given a hunk, what line a given index is on
|
// I want to know, given a hunk, what line a given index is on
|
||||||
func (hunk *PatchHunk) LineNumberOfLine(idx int) int {
|
func (hunk *PatchHunk) LineNumberOfLine(idx int) int {
|
||||||
lines := hunk.bodyLines[0 : idx-hunk.FirstLineIdx-1]
|
n := idx - hunk.FirstLineIdx - 1
|
||||||
|
if n < 0 {
|
||||||
|
n = 0
|
||||||
|
} else if n >= len(hunk.bodyLines) {
|
||||||
|
n = len(hunk.bodyLines) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := hunk.bodyLines[0:n]
|
||||||
|
|
||||||
offset := nLinesWithPrefix(lines, []string{"+", " "})
|
offset := nLinesWithPrefix(lines, []string{"+", " "})
|
||||||
|
|
||||||
|
@ -5,8 +5,9 @@ package config
|
|||||||
// GetPlatformDefaultConfig gets the defaults for the platform
|
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||||
func GetPlatformDefaultConfig() OSConfig {
|
func GetPlatformDefaultConfig() OSConfig {
|
||||||
return OSConfig{
|
return OSConfig{
|
||||||
EditCommand: ``,
|
EditCommand: ``,
|
||||||
OpenCommand: "open {{filename}}",
|
EditCommandTemplate: `{{editor}} {{filename}}`,
|
||||||
OpenLinkCommand: "open {{link}}",
|
OpenCommand: "open {{filename}}",
|
||||||
|
OpenLinkCommand: "open {{link}}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ package config
|
|||||||
// GetPlatformDefaultConfig gets the defaults for the platform
|
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||||
func GetPlatformDefaultConfig() OSConfig {
|
func GetPlatformDefaultConfig() OSConfig {
|
||||||
return OSConfig{
|
return OSConfig{
|
||||||
EditCommand: ``,
|
EditCommand: ``,
|
||||||
OpenCommand: `sh -c "xdg-open {{filename}} >/dev/null"`,
|
EditCommandTemplate: `{{editor}} {{filename}}`,
|
||||||
OpenLinkCommand: `sh -c "xdg-open {{link}} >/dev/null"`,
|
OpenCommand: `sh -c "xdg-open {{filename}} >/dev/null"`,
|
||||||
|
OpenLinkCommand: `sh -c "xdg-open {{link}} >/dev/null"`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,9 @@ package config
|
|||||||
// GetPlatformDefaultConfig gets the defaults for the platform
|
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||||
func GetPlatformDefaultConfig() OSConfig {
|
func GetPlatformDefaultConfig() OSConfig {
|
||||||
return OSConfig{
|
return OSConfig{
|
||||||
EditCommand: ``,
|
EditCommand: ``,
|
||||||
OpenCommand: `cmd /c "start "" {{filename}}"`,
|
EditCommandTemplate: `{{editor}} {{filename}}`,
|
||||||
OpenLinkCommand: `cmd /c "start "" {{link}}"`,
|
OpenCommand: `cmd /c "start "" {{filename}}"`,
|
||||||
|
OpenLinkCommand: `cmd /c "start "" {{link}}"`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -255,6 +255,9 @@ type OSConfig struct {
|
|||||||
// EditCommand is the command for editing a file
|
// EditCommand is the command for editing a file
|
||||||
EditCommand string `yaml:"editCommand,omitempty"`
|
EditCommand string `yaml:"editCommand,omitempty"`
|
||||||
|
|
||||||
|
// EditCommandTemplate is the command template for editing a file
|
||||||
|
EditCommandTemplate string `yaml:"editCommandTemplate,omitempty"`
|
||||||
|
|
||||||
// OpenCommand is the command for opening a file
|
// OpenCommand is the command for opening a file
|
||||||
OpenCommand string `yaml:"openCommand,omitempty"`
|
OpenCommand string `yaml:"openCommand,omitempty"`
|
||||||
|
|
||||||
|
@ -474,13 +474,17 @@ func (gui *Gui) handleCommitEditorPress() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) editFile(filename string) 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 {
|
if err != nil {
|
||||||
return gui.surfaceError(err)
|
return gui.surfaceError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return gui.runSubprocessWithSuspenseAndRefresh(
|
return gui.runSubprocessWithSuspenseAndRefresh(
|
||||||
gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).PrepareShellSubProcess(cmdStr),
|
gui.OSCommand.WithSpan(gui.Tr.Spans.EditFile).ShellCommandFromString(cmdStr),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,7 +1301,7 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
|||||||
ViewName: "main",
|
ViewName: "main",
|
||||||
Contexts: []string{string(MAIN_STAGING_CONTEXT_KEY)},
|
Contexts: []string{string(MAIN_STAGING_CONTEXT_KEY)},
|
||||||
Key: gui.getKey(config.Universal.Edit),
|
Key: gui.getKey(config.Universal.Edit),
|
||||||
Handler: gui.handleFileEdit,
|
Handler: gui.handleLineByLineEdit,
|
||||||
Description: gui.Tr.LcEditFile,
|
Description: gui.Tr.LcEditFile,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -272,3 +272,13 @@ func (gui *Gui) withLBLActiveCheck(f func(*LblPanelState) error) error {
|
|||||||
|
|
||||||
return f(state)
|
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)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user