1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-23 12:18:51 +02:00

Support editing files in existing neovim instance (#2916)

This commit is contained in:
Jesse Duffield 2023-08-10 17:23:58 +10:00 committed by GitHub
commit c43830b027
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 109 deletions

View File

@ -16,11 +16,7 @@ If you want to change the config directory:
- MacOS: `export XDG_CONFIG_HOME="$HOME/.config"`
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code
(completion and error checking) is automatically enabled when the [YAML Red Hat][yaml]
extension is installed. However, note that automatic schema detection only works
if your config file is in one of the standard paths mentioned above. If you
override the path to the file, you can still make IntelliSense work by adding
JSON schema is available for `config.yml` so that IntelliSense in Visual Studio Code (completion and error checking) is automatically enabled when the [YAML Red Hat][yaml] extension is installed. However, note that automatic schema detection only works if your config file is in one of the standard paths mentioned above. If you override the path to the file, you can still make IntelliSense work by adding
```yaml
# yaml-language-server: $schema=https://json.schemastore.org/lazygit.json
@ -310,27 +306,20 @@ os:
### Configuring File Editing
There are two commands for opening files, `o` for "open" and `e` for "edit". `o`
acts as if the file was double-clicked in the Finder/Explorer, so it also works
for non-text files, whereas `e` opens the file in an editor. `e` can also jump
to the right line in the file if you invoke it from the staging panel, for
example.
There are two commands for opening files, `o` for "open" and `e` for "edit". `o` acts as if the file was double-clicked in the Finder/Explorer, so it also works for non-text files, whereas `e` opens the file in an editor. `e` can also jump to the right line in the file if you invoke it from the staging panel, for example.
To tell lazygit which editor to use for the `e` command, the easiest way to do
that is to provide an editPreset config, e.g.
To tell lazygit which editor to use for the `e` command, the easiest way to do that is to provide an editPreset config, e.g.
```yaml
os:
editPreset: 'vscode'
```
Supported presets are `vim`, `nvim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`,
`kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset
from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
Supported presets are `vim`, `nvim`, `nvim-remote`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`, `kakoune`, `helix`, and `xcode`. In many cases lazygit will be able to guess the right preset from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
If for some reason you are not happy with the default commands from a preset, or
there simply is no preset for your editor, you can customize the commands by
setting the `edit`, `editAtLine`, and `editAtLineAndWait` options, e.g.:
`nvim-remote` is an experimental preset for when you have invoked lazygit from within a neovim process, allowing lazygit to open the file from within the parent process rather than spawning a new one.
If for some reason you are not happy with the default commands from a preset, or there simply is no preset for your editor, you can customize the commands by setting the `edit`, `editAtLine`, and `editAtLineAndWait` options, e.g.:
```yaml
os:
@ -341,11 +330,9 @@ os:
openDirInEditor: 'myeditor {{dir}}'
```
The `editInTerminal` option is used to decide whether lazygit needs to suspend
itself to the background before calling the editor.
The `editInTerminal` option is used to decide whether lazygit needs to suspend itself to the background before calling the editor. It should really be named `suspend` because for some cases like when lazygit is opened from within a neovim session and you're using the `nvim-remote` preset, you're technically still in a terminal. Nonetheless we're sticking with the name `editInTerminal` for backwards compatibility.
Contributions of new editor presets are welcome; see the `getPreset` function in
[`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go).
Contributions of new editor presets are welcome; see the `getPreset` function in [`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go).
### Overriding default config file location
@ -457,8 +444,7 @@ gui:
nerdFontsVersion: "3"
```
Supported versions are "2" and "3". The deprecated config `showIcons` sets the
version to "2" for backwards compatibility.
Supported versions are "2" and "3". The deprecated config `showIcons` sets the version to "2" for backwards compatibility.
## Keybindings
@ -506,9 +492,7 @@ keybinding:
## Custom pull request URLs
Some git provider setups (e.g. on-premises GitLab) can have distinct URLs for git-related calls and
the web interface/API itself. To work with those, Lazygit needs to know where it needs to create
the pull request. You can do so on your `config.yml` file using the following syntax:
Some git provider setups (e.g. on-premises GitLab) can have distinct URLs for git-related calls and the web interface/API itself. To work with those, Lazygit needs to know where it needs to create the pull request. You can do so on your `config.yml` file using the following syntax:
```yaml
services:
@ -523,8 +507,7 @@ Where:
## Predefined commit message prefix
In situations where certain naming pattern is used for branches and commits, pattern can be used to populate
commit message with prefix that is parsed from the branch name.
In situations where certain naming pattern is used for branches and commits, pattern can be used to populate commit message with prefix that is parsed from the branch name.
Example:
@ -554,9 +537,7 @@ Result:
## Launching not in a repository behaviour
By default, when launching lazygit from a directory that is not a repository,
you will be prompted to choose if you would like to initialize a repo. You can
override this behaviour in the config with one of the following:
By default, when launching lazygit from a directory that is not a repository, you will be prompted to choose if you would like to initialize a repo. You can override this behaviour in the config with one of the following:
```yaml
# for default prompting behaviour

View File

@ -83,14 +83,14 @@ func (self *FileCommands) GetEditCmdStr(filename string) (string, bool) {
}
}
template, editInTerminal := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
template, suspend := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, editInTerminal
return cmdStr, suspend
}
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
@ -101,7 +101,7 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
}
}
template, editInTerminal := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
template, suspend := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
templateValues := map[string]string{
"filename": self.cmd.Quote(filename),
@ -109,7 +109,7 @@ func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr, editInTerminal
return cmdStr, suspend
}
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
@ -131,15 +131,15 @@ func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber
return cmdStr
}
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) string {
template := config.GetOpenDirInEditorTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
func (self *FileCommands) GetOpenDirInEditorCmdStr(path string) (string, bool) {
template, suspend := config.GetOpenDirInEditorTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
templateValues := map[string]string{
"dir": self.cmd.Quote(path),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
return cmdStr
return cmdStr, suspend
}
func (self *FileCommands) guessDefaultEditor() string {

View File

@ -179,34 +179,34 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
func TestEditFileCmd(t *testing.T) {
type scenario struct {
filename string
osConfig config.OSConfig
expectedCmdStr string
expectedEditInTerminal bool
filename string
osConfig config.OSConfig
expectedCmdStr string
suspend bool
}
scenarios := []scenario{
{
filename: "test",
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
expectedEditInTerminal: true,
filename: "test",
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
suspend: true,
},
{
filename: "test",
osConfig: config.OSConfig{
Edit: "nano {{filename}}",
},
expectedCmdStr: `nano "test"`,
expectedEditInTerminal: true,
expectedCmdStr: `nano "test"`,
suspend: true,
},
{
filename: "file/with space",
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space"`,
expectedEditInTerminal: false,
expectedCmdStr: `subl -- "file/with space"`,
suspend: false,
},
}
@ -218,28 +218,28 @@ func TestEditFileCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, editInTerminal := instance.GetEditCmdStr(s.filename)
cmdStr, suspend := instance.GetEditCmdStr(s.filename)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
assert.Equal(t, s.suspend, suspend)
}
}
func TestEditFileAtLineCmd(t *testing.T) {
type scenario struct {
filename string
lineNumber int
osConfig config.OSConfig
expectedCmdStr string
expectedEditInTerminal bool
filename string
lineNumber int
osConfig config.OSConfig
expectedCmdStr string
suspend bool
}
scenarios := []scenario{
{
filename: "test",
lineNumber: 42,
osConfig: config.OSConfig{},
expectedCmdStr: `vim +42 -- "test"`,
expectedEditInTerminal: true,
filename: "test",
lineNumber: 42,
osConfig: config.OSConfig{},
expectedCmdStr: `vim +42 -- "test"`,
suspend: true,
},
{
filename: "test",
@ -247,8 +247,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
osConfig: config.OSConfig{
EditAtLine: "nano +{{line}} {{filename}}",
},
expectedCmdStr: `nano +35 "test"`,
expectedEditInTerminal: true,
expectedCmdStr: `nano +35 "test"`,
suspend: true,
},
{
filename: "file/with space",
@ -256,8 +256,8 @@ func TestEditFileAtLineCmd(t *testing.T) {
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space":12`,
expectedEditInTerminal: false,
expectedCmdStr: `subl -- "file/with space":12`,
suspend: false,
},
}
@ -269,9 +269,9 @@ func TestEditFileAtLineCmd(t *testing.T) {
userConfig: userConfig,
})
cmdStr, editInTerminal := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
cmdStr, suspend := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
assert.Equal(t, s.suspend, suspend)
}
}

View File

@ -28,13 +28,13 @@ func GetEditAtLineAndWaitTemplate(osConfig *OSConfig, guessDefaultEditor func()
return template
}
func GetOpenDirInEditorTemplate(osConfig *OSConfig, guessDefaultEditor func() string) string {
func GetOpenDirInEditorTemplate(osConfig *OSConfig, guessDefaultEditor func() string) (string, bool) {
preset := getPreset(osConfig, guessDefaultEditor)
template := osConfig.OpenDirInEditor
if template == "" {
template = preset.openDirInEditorTemplate
}
return template
return template, getEditInTerminal(osConfig, preset)
}
type editPreset struct {
@ -42,15 +42,23 @@ type editPreset struct {
editAtLineTemplate string
editAtLineAndWaitTemplate string
openDirInEditorTemplate string
editInTerminal bool
suspend bool
}
// IF YOU ADD A PRESET TO THIS FUNCTION YOU MUST UPDATE THE `Supported presets` SECTION OF docs/Config.md
func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset {
presets := map[string]*editPreset{
"vi": standardTerminalEditorPreset("vi"),
"vim": standardTerminalEditorPreset("vim"),
"nvim": standardTerminalEditorPreset("nvim"),
"vi": standardTerminalEditorPreset("vi"),
"vim": standardTerminalEditorPreset("vim"),
"nvim": standardTerminalEditorPreset("nvim"),
"nvim-remote": {
editTemplate: `nvim --server "$NVIM" --remote-tab {{filename}}`,
editAtLineTemplate: `nvim --server "$NVIM" --remote-tab {{filename}}; [ -z "$NVIM" ] || nvim --server "$NVIM" --remote-send ":{{line}}<CR>"`,
// No remote-wait support yet. See https://github.com/neovim/neovim/pull/17856
editAtLineAndWaitTemplate: `nvim +{{line}} {{filename}}`,
openDirInEditorTemplate: `nvim --server "$NVIM" --remote-tab {{dir}}`,
suspend: false,
},
"emacs": standardTerminalEditorPreset("emacs"),
"nano": standardTerminalEditorPreset("nano"),
"kakoune": standardTerminalEditorPreset("kak"),
@ -59,35 +67,35 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
editAtLineTemplate: "hx -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "hx -- {{filename}}:{{line}}",
openDirInEditorTemplate: "hx -- {{dir}}",
editInTerminal: true,
suspend: true,
},
"vscode": {
editTemplate: "code --reuse-window -- {{filename}}",
editAtLineTemplate: "code --reuse-window --goto -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "code --reuse-window --goto --wait -- {{filename}}:{{line}}",
openDirInEditorTemplate: "code -- {{dir}}",
editInTerminal: false,
suspend: false,
},
"sublime": {
editTemplate: "subl -- {{filename}}",
editAtLineTemplate: "subl -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "subl --wait -- {{filename}}:{{line}}",
openDirInEditorTemplate: "subl -- {{dir}}",
editInTerminal: false,
suspend: false,
},
"bbedit": {
editTemplate: "bbedit -- {{filename}}",
editAtLineTemplate: "bbedit +{{line}} -- {{filename}}",
editAtLineAndWaitTemplate: "bbedit +{{line}} --wait -- {{filename}}",
openDirInEditorTemplate: "bbedit -- {{dir}}",
editInTerminal: false,
suspend: false,
},
"xcode": {
editTemplate: "xed -- {{filename}}",
editAtLineTemplate: "xed --line {{line}} -- {{filename}}",
editAtLineAndWaitTemplate: "xed --line {{line}} --wait -- {{filename}}",
openDirInEditorTemplate: "xed -- {{dir}}",
editInTerminal: false,
suspend: false,
},
}
@ -123,13 +131,13 @@ func standardTerminalEditorPreset(editor string) *editPreset {
editAtLineTemplate: editor + " +{{line}} -- {{filename}}",
editAtLineAndWaitTemplate: editor + " +{{line}} -- {{filename}}",
openDirInEditorTemplate: editor + " -- {{dir}}",
editInTerminal: true,
suspend: true,
}
}
func getEditInTerminal(osConfig *OSConfig, preset *editPreset) bool {
if osConfig.EditInTerminal != nil {
return *osConfig.EditInTerminal
if osConfig.SuspendOnEdit != nil {
return *osConfig.SuspendOnEdit
}
return preset.editInTerminal
return preset.suspend
}

View File

@ -16,7 +16,7 @@ func TestGetEditTemplate(t *testing.T) {
expectedEditTemplate string
expectedEditAtLineTemplate string
expectedEditAtLineAndWaitTemplate string
expectedEditInTerminal bool
expectedSuspend bool
}{
{
"Default template is vim",
@ -52,9 +52,9 @@ func TestGetEditTemplate(t *testing.T) {
{
"Overriding a preset with explicit config (edit)",
&OSConfig{
EditPreset: "vscode",
Edit: "myeditor {{filename}}",
EditInTerminal: &trueVal,
EditPreset: "vscode",
Edit: "myeditor {{filename}}",
SuspendOnEdit: &trueVal,
},
func() string { return "" },
"myeditor {{filename}}",
@ -65,9 +65,9 @@ func TestGetEditTemplate(t *testing.T) {
{
"Overriding a preset with explicit config (edit at line)",
&OSConfig{
EditPreset: "vscode",
EditAtLine: "myeditor --line={{line}} {{filename}}",
EditInTerminal: &trueVal,
EditPreset: "vscode",
EditAtLine: "myeditor --line={{line}} {{filename}}",
SuspendOnEdit: &trueVal,
},
func() string { return "" },
"code --reuse-window -- {{filename}}",
@ -80,7 +80,7 @@ func TestGetEditTemplate(t *testing.T) {
&OSConfig{
EditPreset: "vscode",
EditAtLineAndWait: "myeditor --line={{line}} -w {{filename}}",
EditInTerminal: &trueVal,
SuspendOnEdit: &trueVal,
},
func() string { return "" },
"code --reuse-window -- {{filename}}",
@ -111,13 +111,13 @@ func TestGetEditTemplate(t *testing.T) {
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
template, editInTerminal := GetEditTemplate(s.osConfig, s.guessDefaultEditor)
template, suspend := GetEditTemplate(s.osConfig, s.guessDefaultEditor)
assert.Equal(t, s.expectedEditTemplate, template)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
assert.Equal(t, s.expectedSuspend, suspend)
template, editInTerminal = GetEditAtLineTemplate(s.osConfig, s.guessDefaultEditor)
template, suspend = GetEditAtLineTemplate(s.osConfig, s.guessDefaultEditor)
assert.Equal(t, s.expectedEditAtLineTemplate, template)
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
assert.Equal(t, s.expectedSuspend, suspend)
template = GetEditAtLineAndWaitTemplate(s.osConfig, s.guessDefaultEditor)
assert.Equal(t, s.expectedEditAtLineAndWaitTemplate, template)

View File

@ -316,10 +316,10 @@ type OSConfig struct {
// window is closed.
EditAtLineAndWait string `yaml:"editAtLineAndWait,omitempty"`
// Whether the given edit commands use the terminal. Used to decide whether
// lazygit needs to suspend to the background before calling the editor.
// Whether lazygit suspends until an edit process returns
// Pointer to bool so that we can distinguish unset (nil) from false.
EditInTerminal *bool `yaml:"editInTerminal,omitempty"`
// We're naming this `editInTerminal` for backwards compatibility
SuspendOnEdit *bool `yaml:"editInTerminal,omitempty"`
// For opening a directory in an editor
OpenDirInEditor string `yaml:"openDirInEditor,omitempty"`

View File

@ -19,33 +19,32 @@ func NewFilesHelper(c *HelperCommon) *FilesHelper {
var _ IFilesHelper = &FilesHelper{}
func (self *FilesHelper) EditFile(filename string) error {
cmdStr, editInTerminal := self.c.Git().File.GetEditCmdStr(filename)
return self.callEditor(cmdStr, editInTerminal)
cmdStr, suspend := self.c.Git().File.GetEditCmdStr(filename)
return self.callEditor(cmdStr, suspend)
}
func (self *FilesHelper) EditFileAtLine(filename string, lineNumber int) error {
cmdStr, editInTerminal := self.c.Git().File.GetEditAtLineCmdStr(filename, lineNumber)
return self.callEditor(cmdStr, editInTerminal)
cmdStr, suspend := self.c.Git().File.GetEditAtLineCmdStr(filename, lineNumber)
return self.callEditor(cmdStr, suspend)
}
func (self *FilesHelper) EditFileAtLineAndWait(filename string, lineNumber int) error {
cmdStr := self.c.Git().File.GetEditAtLineAndWaitCmdStr(filename, lineNumber)
// Always suspend, regardless of the value of the editInTerminal config,
// Always suspend, regardless of the value of the suspend config,
// since we want to prevent interacting with the UI until the editor
// returns, even if the editor doesn't use the terminal
return self.callEditor(cmdStr, true)
}
func (self *FilesHelper) OpenDirInEditor(path string) error {
cmdStr := self.c.Git().File.GetOpenDirInEditorCmdStr(path)
cmdStr, suspend := self.c.Git().File.GetOpenDirInEditorCmdStr(path)
// Not editing in terminal because surely that's not a thing.
return self.callEditor(cmdStr, false)
return self.callEditor(cmdStr, suspend)
}
func (self *FilesHelper) callEditor(cmdStr string, editInTerminal bool) error {
if editInTerminal {
func (self *FilesHelper) callEditor(cmdStr string, suspend bool) error {
if suspend {
return self.c.RunSubprocessAndRefresh(
self.c.OS().Cmd.NewShell(cmdStr),
)