mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-01-10 04:07:18 +02:00
Merge pull request #2523 from stefanhaller/editor-config
This commit is contained in:
commit
04e0a9bb45
@ -96,9 +96,12 @@ git:
|
|||||||
parseEmoji: false
|
parseEmoji: false
|
||||||
diffContextSize: 3 # how many lines of context are shown around a change in diffs
|
diffContextSize: 3 # how many lines of context are shown around a change in diffs
|
||||||
os:
|
os:
|
||||||
editCommand: '' # see 'Configuring File Editing' section
|
editPreset: '' # see 'Configuring File Editing' section
|
||||||
editCommandTemplate: ''
|
edit: ''
|
||||||
openCommand: ''
|
editAtLine: ''
|
||||||
|
editAtLineAndWait: ''
|
||||||
|
open: ''
|
||||||
|
openLink: ''
|
||||||
refresher:
|
refresher:
|
||||||
refreshInterval: 10 # File/submodule refresh interval in seconds. Auto-refresh can be disabled via option 'git.autoRefresh'.
|
refreshInterval: 10 # File/submodule refresh interval in seconds. Auto-refresh can be disabled via option 'git.autoRefresh'.
|
||||||
fetchInterval: 60 # Re-fetch interval in seconds. Auto-fetch can be disabled via option 'git.autoFetch'.
|
fetchInterval: 60 # Re-fetch interval in seconds. Auto-fetch can be disabled via option 'git.autoFetch'.
|
||||||
@ -268,40 +271,41 @@ os:
|
|||||||
|
|
||||||
### Configuring File Editing
|
### Configuring File Editing
|
||||||
|
|
||||||
Lazygit will edit a file with the first set editor in the following:
|
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.
|
||||||
|
|
||||||
1. config.yaml
|
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
|
```yaml
|
||||||
os:
|
os:
|
||||||
editCommand: 'vim' # as an example
|
editPreset: 'vscode'
|
||||||
```
|
```
|
||||||
|
|
||||||
2. \$(git config core.editor)
|
Supported presets are `vim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`, and
|
||||||
3. \$GIT_EDITOR
|
`xcode`. In many cases lazygit will be able to guess the right preset from your
|
||||||
4. \$VISUAL
|
$(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||||
5. \$EDITOR
|
|
||||||
6. \$(which vi)
|
|
||||||
|
|
||||||
Lazygit will log an error if none of these options are set.
|
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
|
||||||
You can specify the current line number when you're in the patch explorer.
|
setting the `edit`, `editAtLine`, and `editAtLineAndWait` options, e.g.:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
os:
|
os:
|
||||||
editCommand: 'vim'
|
edit: 'myeditor {{filename}}'
|
||||||
editCommandTemplate: '{{editor}} +{{line}} -- {{filename}}'
|
editAtLine: 'myeditor --line={{line}} {{filename}}'
|
||||||
|
editAtLineAndWait: 'myeditor --block --line={{line}} {{filename}}'
|
||||||
|
editInTerminal: true
|
||||||
```
|
```
|
||||||
|
|
||||||
or
|
The `editInTerminal` option is used to decide whether lazygit needs to suspend
|
||||||
|
itself to the background before calling the editor.
|
||||||
|
|
||||||
```yaml
|
Contributions of new editor presets are welcome; see the `getPreset` function in
|
||||||
os:
|
[`editor_presets.go`](https://github.com/jesseduffield/lazygit/blob/master/pkg/config/editor_presets.go).
|
||||||
editCommand: 'code'
|
|
||||||
editCommandTemplate: '{{editor}} --goto -- {{filename}}:{{line}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
`{{editor}}` in `editCommandTemplate` is replaced with the value of `editCommand`.
|
|
||||||
|
|
||||||
### Overriding default config file location
|
### Overriding default config file location
|
||||||
|
|
||||||
@ -317,15 +321,6 @@ or
|
|||||||
LG_CONFIG_FILE="$HOME/.base_lg_conf,$HOME/.light_theme_lg_conf" lazygit
|
LG_CONFIG_FILE="$HOME/.base_lg_conf,$HOME/.light_theme_lg_conf" lazygit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Recommended Config Values
|
|
||||||
|
|
||||||
for users of VSCode
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
os:
|
|
||||||
openCommand: 'code -rg {{filename}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Color Attributes
|
## Color Attributes
|
||||||
|
|
||||||
For color attributes you can choose an array of attributes (with max one color attribute)
|
For color attributes you can choose an array of attributes (with max one color attribute)
|
||||||
|
@ -3,8 +3,10 @@ package git_commands
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,7 +29,7 @@ func (self *FileCommands) Cat(fileName string) (string, error) {
|
|||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FileCommands) GetEditCmdStr(filename string, lineNumber int) (string, error) {
|
func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (string, error) {
|
||||||
editor := self.UserConfig.OS.EditCommand
|
editor := self.UserConfig.OS.EditCommand
|
||||||
|
|
||||||
if editor == "" {
|
if editor == "" {
|
||||||
@ -72,3 +74,82 @@ func (self *FileCommands) GetEditCmdStr(filename string, lineNumber int) (string
|
|||||||
}
|
}
|
||||||
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
|
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *FileCommands) GetEditCmdStr(filename string) (string, bool) {
|
||||||
|
// Legacy support for old config; to be removed at some point
|
||||||
|
if self.UserConfig.OS.Edit == "" && self.UserConfig.OS.EditCommandTemplate != "" {
|
||||||
|
if cmdStr, err := self.GetEditCmdStrLegacy(filename, 1); err == nil {
|
||||||
|
return cmdStr, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template, editInTerminal := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||||
|
|
||||||
|
templateValues := map[string]string{
|
||||||
|
"filename": self.cmd.Quote(filename),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
|
||||||
|
return cmdStr, editInTerminal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FileCommands) GetEditAtLineCmdStr(filename string, lineNumber int) (string, bool) {
|
||||||
|
// Legacy support for old config; to be removed at some point
|
||||||
|
if self.UserConfig.OS.EditAtLine == "" && self.UserConfig.OS.EditCommandTemplate != "" {
|
||||||
|
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
|
||||||
|
return cmdStr, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template, editInTerminal := config.GetEditAtLineTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||||
|
|
||||||
|
templateValues := map[string]string{
|
||||||
|
"filename": self.cmd.Quote(filename),
|
||||||
|
"line": strconv.Itoa(lineNumber),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
|
||||||
|
return cmdStr, editInTerminal
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FileCommands) GetEditAtLineAndWaitCmdStr(filename string, lineNumber int) string {
|
||||||
|
// Legacy support for old config; to be removed at some point
|
||||||
|
if self.UserConfig.OS.EditAtLineAndWait == "" && self.UserConfig.OS.EditCommandTemplate != "" {
|
||||||
|
if cmdStr, err := self.GetEditCmdStrLegacy(filename, lineNumber); err == nil {
|
||||||
|
return cmdStr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template := config.GetEditAtLineAndWaitTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
|
||||||
|
|
||||||
|
templateValues := map[string]string{
|
||||||
|
"filename": self.cmd.Quote(filename),
|
||||||
|
"line": strconv.Itoa(lineNumber),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
|
||||||
|
return cmdStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *FileCommands) guessDefaultEditor() string {
|
||||||
|
// Try to query a few places where editors get configured
|
||||||
|
editor := self.config.GetCoreEditor()
|
||||||
|
if editor == "" {
|
||||||
|
editor = self.os.Getenv("GIT_EDITOR")
|
||||||
|
}
|
||||||
|
if editor == "" {
|
||||||
|
editor = self.os.Getenv("VISUAL")
|
||||||
|
}
|
||||||
|
if editor == "" {
|
||||||
|
editor = self.os.Getenv("EDITOR")
|
||||||
|
}
|
||||||
|
|
||||||
|
if editor != "" {
|
||||||
|
// At this point, it might be more than just the name of the editor;
|
||||||
|
// e.g. it might be "code -w" or "vim -u myvim.rc". So assume that
|
||||||
|
// everything up to the first space is the editor name.
|
||||||
|
editor = strings.Split(editor, " ")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return editor
|
||||||
|
}
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEditFileCmdStr(t *testing.T) {
|
func TestEditFileCmdStrLegacy(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
filename string
|
filename string
|
||||||
configEditCommand string
|
configEditCommand string
|
||||||
@ -172,7 +172,206 @@ func TestEditFileCmdStr(t *testing.T) {
|
|||||||
getenv: s.getenv,
|
getenv: s.getenv,
|
||||||
})
|
})
|
||||||
|
|
||||||
s.test(instance.GetEditCmdStr(s.filename, 1))
|
s.test(instance.GetEditCmdStrLegacy(s.filename, 1))
|
||||||
s.runner.CheckForMissingCalls()
|
s.runner.CheckForMissingCalls()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEditFileCmd(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
filename string
|
||||||
|
osConfig config.OSConfig
|
||||||
|
expectedCmdStr string
|
||||||
|
expectedEditInTerminal bool
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
filename: "test",
|
||||||
|
osConfig: config.OSConfig{},
|
||||||
|
expectedCmdStr: `vim -- "test"`,
|
||||||
|
expectedEditInTerminal: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "test",
|
||||||
|
osConfig: config.OSConfig{
|
||||||
|
Edit: "nano {{filename}}",
|
||||||
|
},
|
||||||
|
expectedCmdStr: `nano "test"`,
|
||||||
|
expectedEditInTerminal: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "file/with space",
|
||||||
|
osConfig: config.OSConfig{
|
||||||
|
EditPreset: "sublime",
|
||||||
|
},
|
||||||
|
expectedCmdStr: `subl -- "file/with space"`,
|
||||||
|
expectedEditInTerminal: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
userConfig := config.GetDefaultConfig()
|
||||||
|
userConfig.OS = s.osConfig
|
||||||
|
|
||||||
|
instance := buildFileCommands(commonDeps{
|
||||||
|
userConfig: userConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
cmdStr, editInTerminal := instance.GetEditCmdStr(s.filename)
|
||||||
|
assert.Equal(t, s.expectedCmdStr, cmdStr)
|
||||||
|
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEditFileAtLineCmd(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
filename string
|
||||||
|
lineNumber int
|
||||||
|
osConfig config.OSConfig
|
||||||
|
expectedCmdStr string
|
||||||
|
expectedEditInTerminal bool
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
filename: "test",
|
||||||
|
lineNumber: 42,
|
||||||
|
osConfig: config.OSConfig{},
|
||||||
|
expectedCmdStr: `vim +42 -- "test"`,
|
||||||
|
expectedEditInTerminal: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "test",
|
||||||
|
lineNumber: 35,
|
||||||
|
osConfig: config.OSConfig{
|
||||||
|
EditAtLine: "nano +{{line}} {{filename}}",
|
||||||
|
},
|
||||||
|
expectedCmdStr: `nano +35 "test"`,
|
||||||
|
expectedEditInTerminal: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "file/with space",
|
||||||
|
lineNumber: 12,
|
||||||
|
osConfig: config.OSConfig{
|
||||||
|
EditPreset: "sublime",
|
||||||
|
},
|
||||||
|
expectedCmdStr: `subl -- "file/with space":12`,
|
||||||
|
expectedEditInTerminal: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
userConfig := config.GetDefaultConfig()
|
||||||
|
userConfig.OS = s.osConfig
|
||||||
|
|
||||||
|
instance := buildFileCommands(commonDeps{
|
||||||
|
userConfig: userConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
cmdStr, editInTerminal := instance.GetEditAtLineCmdStr(s.filename, s.lineNumber)
|
||||||
|
assert.Equal(t, s.expectedCmdStr, cmdStr)
|
||||||
|
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEditFileAtLineAndWaitCmd(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
filename string
|
||||||
|
lineNumber int
|
||||||
|
osConfig config.OSConfig
|
||||||
|
expectedCmdStr string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
filename: "test",
|
||||||
|
lineNumber: 42,
|
||||||
|
osConfig: config.OSConfig{},
|
||||||
|
expectedCmdStr: `vim +42 -- "test"`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filename: "file/with space",
|
||||||
|
lineNumber: 12,
|
||||||
|
osConfig: config.OSConfig{
|
||||||
|
EditPreset: "sublime",
|
||||||
|
},
|
||||||
|
expectedCmdStr: `subl --wait -- "file/with space":12`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
userConfig := config.GetDefaultConfig()
|
||||||
|
userConfig.OS = s.osConfig
|
||||||
|
|
||||||
|
instance := buildFileCommands(commonDeps{
|
||||||
|
userConfig: userConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
cmdStr := instance.GetEditAtLineAndWaitCmdStr(s.filename, s.lineNumber)
|
||||||
|
assert.Equal(t, s.expectedCmdStr, cmdStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGuessDefaultEditor(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
gitConfigMockResponses map[string]string
|
||||||
|
getenv func(string) string
|
||||||
|
expectedResult string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
gitConfigMockResponses: nil,
|
||||||
|
getenv: func(env string) string {
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
expectedResult: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gitConfigMockResponses: map[string]string{"core.editor": "nano"},
|
||||||
|
getenv: func(env string) string {
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
expectedResult: "nano",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gitConfigMockResponses: map[string]string{"core.editor": "code -w"},
|
||||||
|
getenv: func(env string) string {
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
expectedResult: "code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gitConfigMockResponses: nil,
|
||||||
|
getenv: func(env string) string {
|
||||||
|
if env == "VISUAL" {
|
||||||
|
return "emacs"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
expectedResult: "emacs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
gitConfigMockResponses: nil,
|
||||||
|
getenv: func(env string) string {
|
||||||
|
if env == "EDITOR" {
|
||||||
|
return "bbedit -w"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
expectedResult: "bbedit",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
instance := buildFileCommands(commonDeps{
|
||||||
|
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
||||||
|
getenv: s.getenv,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(t, s.expectedResult, instance.guessDefaultEditor())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -78,21 +78,30 @@ func FileType(path string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) OpenFile(filename string) error {
|
func (c *OSCommand) OpenFile(filename string) error {
|
||||||
return c.OpenFileAtLine(filename, 1)
|
commandTemplate := c.UserConfig.OS.Open
|
||||||
}
|
if commandTemplate == "" {
|
||||||
|
// Legacy support
|
||||||
func (c *OSCommand) OpenFileAtLine(filename string, lineNumber int) error {
|
commandTemplate = c.UserConfig.OS.OpenCommand
|
||||||
commandTemplate := c.UserConfig.OS.OpenCommand
|
}
|
||||||
|
if commandTemplate == "" {
|
||||||
|
commandTemplate = config.GetPlatformDefaultConfig().Open
|
||||||
|
}
|
||||||
templateValues := map[string]string{
|
templateValues := map[string]string{
|
||||||
"filename": c.Quote(filename),
|
"filename": c.Quote(filename),
|
||||||
"line": fmt.Sprintf("%d", lineNumber),
|
|
||||||
}
|
}
|
||||||
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
||||||
return c.Cmd.NewShell(command).Run()
|
return c.Cmd.NewShell(command).Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *OSCommand) OpenLink(link string) error {
|
func (c *OSCommand) OpenLink(link string) error {
|
||||||
commandTemplate := c.UserConfig.OS.OpenLinkCommand
|
commandTemplate := c.UserConfig.OS.OpenLink
|
||||||
|
if commandTemplate == "" {
|
||||||
|
// Legacy support
|
||||||
|
commandTemplate = c.UserConfig.OS.OpenLinkCommand
|
||||||
|
}
|
||||||
|
if commandTemplate == "" {
|
||||||
|
commandTemplate = config.GetPlatformDefaultConfig().OpenLink
|
||||||
|
}
|
||||||
templateValues := map[string]string{
|
templateValues := map[string]string{
|
||||||
"link": c.Quote(link),
|
"link": c.Quote(link),
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func TestOSCommandOpenFileDarwin(t *testing.T) {
|
|||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
||||||
oSCmd.Platform.OS = "darwin"
|
oSCmd.Platform.OS = "darwin"
|
||||||
oSCmd.UserConfig.OS.OpenCommand = "open {{filename}}"
|
oSCmd.UserConfig.OS.Open = "open {{filename}}"
|
||||||
|
|
||||||
s.test(oSCmd.OpenFile(s.filename))
|
s.test(oSCmd.OpenFile(s.filename))
|
||||||
}
|
}
|
||||||
@ -135,7 +135,7 @@ func TestOSCommandOpenFileLinux(t *testing.T) {
|
|||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
oSCmd := NewDummyOSCommandWithRunner(s.runner)
|
||||||
oSCmd.Platform.OS = "linux"
|
oSCmd.Platform.OS = "linux"
|
||||||
oSCmd.UserConfig.OS.OpenCommand = `xdg-open {{filename}} > /dev/null`
|
oSCmd.UserConfig.OS.Open = `xdg-open {{filename}} > /dev/null`
|
||||||
|
|
||||||
s.test(oSCmd.OpenFile(s.filename))
|
s.test(oSCmd.OpenFile(s.filename))
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ package oscommands
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cli/safeexec"
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -19,11 +20,13 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
|||||||
test func(error)
|
test func(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fullCmdPath, _ := safeexec.LookPath("cmd")
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
filename: "test",
|
filename: "test",
|
||||||
runner: NewFakeRunner(t).
|
runner: NewFakeRunner(t).
|
||||||
ExpectArgs([]string{"cmd", "/c", "start", "", "test"}, "", errors.New("error")),
|
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "test"}, "", errors.New("error")),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
},
|
},
|
||||||
@ -31,7 +34,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
|||||||
{
|
{
|
||||||
filename: "test",
|
filename: "test",
|
||||||
runner: NewFakeRunner(t).
|
runner: NewFakeRunner(t).
|
||||||
ExpectArgs([]string{"cmd", "/c", "start", "", "test"}, "", nil),
|
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "test"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@ -39,7 +42,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
|||||||
{
|
{
|
||||||
filename: "filename with spaces",
|
filename: "filename with spaces",
|
||||||
runner: NewFakeRunner(t).
|
runner: NewFakeRunner(t).
|
||||||
ExpectArgs([]string{"cmd", "/c", "start", "", "filename with spaces"}, "", nil),
|
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "filename with spaces"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@ -47,7 +50,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
|||||||
{
|
{
|
||||||
filename: "let's_test_with_single_quote",
|
filename: "let's_test_with_single_quote",
|
||||||
runner: NewFakeRunner(t).
|
runner: NewFakeRunner(t).
|
||||||
ExpectArgs([]string{"cmd", "/c", "start", "", "let's_test_with_single_quote"}, "", nil),
|
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "let's_test_with_single_quote"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
||||||
@ -55,7 +58,7 @@ func TestOSCommandOpenFileWindows(t *testing.T) {
|
|||||||
{
|
{
|
||||||
filename: "$USER.txt",
|
filename: "$USER.txt",
|
||||||
runner: NewFakeRunner(t).
|
runner: NewFakeRunner(t).
|
||||||
ExpectArgs([]string{"cmd", "/c", "start", "", "$USER.txt"}, "", nil),
|
ExpectArgs([]string{fullCmdPath, "/c", "start", "", "$USER.txt"}, "", nil),
|
||||||
test: func(err error) {
|
test: func(err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
},
|
},
|
@ -6,9 +6,7 @@ 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: ``,
|
Open: "open -- {{filename}}",
|
||||||
EditCommandTemplate: "",
|
OpenLink: "open {{link}}",
|
||||||
OpenCommand: "open -- {{filename}}",
|
|
||||||
OpenLinkCommand: "open {{link}}",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,17 +29,13 @@ func isContainer() bool {
|
|||||||
func GetPlatformDefaultConfig() OSConfig {
|
func GetPlatformDefaultConfig() OSConfig {
|
||||||
if isWSL() && !isContainer() {
|
if isWSL() && !isContainer() {
|
||||||
return OSConfig{
|
return OSConfig{
|
||||||
EditCommand: ``,
|
Open: `powershell.exe start explorer.exe {{filename}} >/dev/null`,
|
||||||
EditCommandTemplate: "",
|
OpenLink: `powershell.exe start {{link}} >/dev/null`,
|
||||||
OpenCommand: `powershell.exe start explorer.exe {{filename}} >/dev/null`,
|
|
||||||
OpenLinkCommand: `powershell.exe start {{link}} >/dev/null`,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return OSConfig{
|
return OSConfig{
|
||||||
EditCommand: ``,
|
Open: `xdg-open {{filename}} >/dev/null`,
|
||||||
EditCommandTemplate: "",
|
OpenLink: `xdg-open {{link}} >/dev/null`,
|
||||||
OpenCommand: `xdg-open {{filename}} >/dev/null`,
|
|
||||||
OpenLinkCommand: `xdg-open {{link}} >/dev/null`,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,7 @@ 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: ``,
|
Open: `start "" {{filename}}`,
|
||||||
EditCommandTemplate: "",
|
OpenLink: `start "" {{link}}`,
|
||||||
OpenCommand: `start "" {{filename}}`,
|
|
||||||
OpenLinkCommand: `start "" {{link}}`,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
109
pkg/config/editor_presets.go
Normal file
109
pkg/config/editor_presets.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
func GetEditTemplate(osConfig *OSConfig, guessDefaultEditor func() string) (string, bool) {
|
||||||
|
preset := getPreset(osConfig, guessDefaultEditor)
|
||||||
|
template := osConfig.Edit
|
||||||
|
if template == "" {
|
||||||
|
template = preset.editTemplate
|
||||||
|
}
|
||||||
|
|
||||||
|
return template, getEditInTerminal(osConfig, preset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEditAtLineTemplate(osConfig *OSConfig, guessDefaultEditor func() string) (string, bool) {
|
||||||
|
preset := getPreset(osConfig, guessDefaultEditor)
|
||||||
|
template := osConfig.EditAtLine
|
||||||
|
if template == "" {
|
||||||
|
template = preset.editAtLineTemplate
|
||||||
|
}
|
||||||
|
return template, getEditInTerminal(osConfig, preset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetEditAtLineAndWaitTemplate(osConfig *OSConfig, guessDefaultEditor func() string) string {
|
||||||
|
preset := getPreset(osConfig, guessDefaultEditor)
|
||||||
|
template := osConfig.EditAtLineAndWait
|
||||||
|
if template == "" {
|
||||||
|
template = preset.editAtLineAndWaitTemplate
|
||||||
|
}
|
||||||
|
return template
|
||||||
|
}
|
||||||
|
|
||||||
|
type editPreset struct {
|
||||||
|
editTemplate string
|
||||||
|
editAtLineTemplate string
|
||||||
|
editAtLineAndWaitTemplate string
|
||||||
|
editInTerminal bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset {
|
||||||
|
presets := map[string]*editPreset{
|
||||||
|
"vi": standardTerminalEditorPreset("vi"),
|
||||||
|
"vim": standardTerminalEditorPreset("vim"),
|
||||||
|
"nvim": standardTerminalEditorPreset("nvim"),
|
||||||
|
"emacs": standardTerminalEditorPreset("emacs"),
|
||||||
|
"nano": standardTerminalEditorPreset("nano"),
|
||||||
|
"vscode": {
|
||||||
|
editTemplate: "code --reuse-window -- {{filename}}",
|
||||||
|
editAtLineTemplate: "code --reuse-window --goto -- {{filename}}:{{line}}",
|
||||||
|
editAtLineAndWaitTemplate: "code --reuse-window --goto --wait -- {{filename}}:{{line}}",
|
||||||
|
editInTerminal: false,
|
||||||
|
},
|
||||||
|
"sublime": {
|
||||||
|
editTemplate: "subl -- {{filename}}",
|
||||||
|
editAtLineTemplate: "subl -- {{filename}}:{{line}}",
|
||||||
|
editAtLineAndWaitTemplate: "subl --wait -- {{filename}}:{{line}}",
|
||||||
|
editInTerminal: false,
|
||||||
|
},
|
||||||
|
"bbedit": {
|
||||||
|
editTemplate: "bbedit -- {{filename}}",
|
||||||
|
editAtLineTemplate: "bbedit +{{line}} -- {{filename}}",
|
||||||
|
editAtLineAndWaitTemplate: "bbedit +{{line}} --wait -- {{filename}}",
|
||||||
|
editInTerminal: false,
|
||||||
|
},
|
||||||
|
"xcode": {
|
||||||
|
editTemplate: "xed -- {{filename}}",
|
||||||
|
editAtLineTemplate: "xed --line {{line}} -- {{filename}}",
|
||||||
|
editAtLineAndWaitTemplate: "xed --line {{line}} --wait -- {{filename}}",
|
||||||
|
editInTerminal: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some of our presets have a different name than the editor they are using.
|
||||||
|
editorToPreset := map[string]string{
|
||||||
|
"code": "vscode",
|
||||||
|
"subl": "sublime",
|
||||||
|
"xed": "xcode",
|
||||||
|
}
|
||||||
|
|
||||||
|
presetName := osConfig.EditPreset
|
||||||
|
if presetName == "" {
|
||||||
|
defaultEditor := guessDefaultEditor()
|
||||||
|
if presets[defaultEditor] != nil {
|
||||||
|
presetName = defaultEditor
|
||||||
|
} else if p := editorToPreset[defaultEditor]; p != "" {
|
||||||
|
presetName = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if presetName == "" || presets[presetName] == nil {
|
||||||
|
presetName = "vim"
|
||||||
|
}
|
||||||
|
|
||||||
|
return presets[presetName]
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardTerminalEditorPreset(editor string) *editPreset {
|
||||||
|
return &editPreset{
|
||||||
|
editTemplate: editor + " -- {{filename}}",
|
||||||
|
editAtLineTemplate: editor + " +{{line}} -- {{filename}}",
|
||||||
|
editAtLineAndWaitTemplate: editor + " +{{line}} -- {{filename}}",
|
||||||
|
editInTerminal: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEditInTerminal(osConfig *OSConfig, preset *editPreset) bool {
|
||||||
|
if osConfig.EditInTerminal != nil {
|
||||||
|
return *osConfig.EditInTerminal
|
||||||
|
}
|
||||||
|
return preset.editInTerminal
|
||||||
|
}
|
126
pkg/config/editor_presets_test.go
Normal file
126
pkg/config/editor_presets_test.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetEditTemplate(t *testing.T) {
|
||||||
|
trueVal := true
|
||||||
|
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
osConfig *OSConfig
|
||||||
|
guessDefaultEditor func() string
|
||||||
|
expectedEditTemplate string
|
||||||
|
expectedEditAtLineTemplate string
|
||||||
|
expectedEditAtLineAndWaitTemplate string
|
||||||
|
expectedEditInTerminal bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Default template is vim",
|
||||||
|
&OSConfig{},
|
||||||
|
func() string { return "" },
|
||||||
|
"vim -- {{filename}}",
|
||||||
|
"vim +{{line}} -- {{filename}}",
|
||||||
|
"vim +{{line}} -- {{filename}}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Setting a preset",
|
||||||
|
&OSConfig{
|
||||||
|
EditPreset: "vscode",
|
||||||
|
},
|
||||||
|
func() string { return "" },
|
||||||
|
"code --reuse-window -- {{filename}}",
|
||||||
|
"code --reuse-window --goto -- {{filename}}:{{line}}",
|
||||||
|
"code --reuse-window --goto --wait -- {{filename}}:{{line}}",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Setting a preset wins over guessed editor",
|
||||||
|
&OSConfig{
|
||||||
|
EditPreset: "vscode",
|
||||||
|
},
|
||||||
|
func() string { return "nano" },
|
||||||
|
"code --reuse-window -- {{filename}}",
|
||||||
|
"code --reuse-window --goto -- {{filename}}:{{line}}",
|
||||||
|
"code --reuse-window --goto --wait -- {{filename}}:{{line}}",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Overriding a preset with explicit config (edit)",
|
||||||
|
&OSConfig{
|
||||||
|
EditPreset: "vscode",
|
||||||
|
Edit: "myeditor {{filename}}",
|
||||||
|
EditInTerminal: &trueVal,
|
||||||
|
},
|
||||||
|
func() string { return "" },
|
||||||
|
"myeditor {{filename}}",
|
||||||
|
"code --reuse-window --goto -- {{filename}}:{{line}}",
|
||||||
|
"code --reuse-window --goto --wait -- {{filename}}:{{line}}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Overriding a preset with explicit config (edit at line)",
|
||||||
|
&OSConfig{
|
||||||
|
EditPreset: "vscode",
|
||||||
|
EditAtLine: "myeditor --line={{line}} {{filename}}",
|
||||||
|
EditInTerminal: &trueVal,
|
||||||
|
},
|
||||||
|
func() string { return "" },
|
||||||
|
"code --reuse-window -- {{filename}}",
|
||||||
|
"myeditor --line={{line}} {{filename}}",
|
||||||
|
"code --reuse-window --goto --wait -- {{filename}}:{{line}}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Overriding a preset with explicit config (edit at line and wait)",
|
||||||
|
&OSConfig{
|
||||||
|
EditPreset: "vscode",
|
||||||
|
EditAtLineAndWait: "myeditor --line={{line}} -w {{filename}}",
|
||||||
|
EditInTerminal: &trueVal,
|
||||||
|
},
|
||||||
|
func() string { return "" },
|
||||||
|
"code --reuse-window -- {{filename}}",
|
||||||
|
"code --reuse-window --goto -- {{filename}}:{{line}}",
|
||||||
|
"myeditor --line={{line}} -w {{filename}}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Unknown preset name",
|
||||||
|
&OSConfig{
|
||||||
|
EditPreset: "thisPresetDoesNotExist",
|
||||||
|
},
|
||||||
|
func() string { return "" },
|
||||||
|
"vim -- {{filename}}",
|
||||||
|
"vim +{{line}} -- {{filename}}",
|
||||||
|
"vim +{{line}} -- {{filename}}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Guessing a preset from guessed editor",
|
||||||
|
&OSConfig{},
|
||||||
|
func() string { return "emacs" },
|
||||||
|
"emacs -- {{filename}}",
|
||||||
|
"emacs +{{line}} -- {{filename}}",
|
||||||
|
"emacs +{{line}} -- {{filename}}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.name, func(t *testing.T) {
|
||||||
|
template, editInTerminal := GetEditTemplate(s.osConfig, s.guessDefaultEditor)
|
||||||
|
assert.Equal(t, s.expectedEditTemplate, template)
|
||||||
|
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
|
||||||
|
|
||||||
|
template, editInTerminal = GetEditAtLineTemplate(s.osConfig, s.guessDefaultEditor)
|
||||||
|
assert.Equal(t, s.expectedEditAtLineTemplate, template)
|
||||||
|
assert.Equal(t, s.expectedEditInTerminal, editInTerminal)
|
||||||
|
|
||||||
|
template = GetEditAtLineAndWaitTemplate(s.osConfig, s.guessDefaultEditor)
|
||||||
|
assert.Equal(t, s.expectedEditAtLineAndWaitTemplate, template)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -291,16 +291,54 @@ type KeybindingSubmodulesConfig struct {
|
|||||||
|
|
||||||
// OSConfig contains config on the level of the os
|
// OSConfig contains config on the level of the os
|
||||||
type OSConfig struct {
|
type OSConfig struct {
|
||||||
// EditCommand is the command for editing a file
|
// Command for editing a file. Should contain "{{filename}}".
|
||||||
|
Edit string `yaml:"edit,omitempty"`
|
||||||
|
|
||||||
|
// Command for editing a file at a given line number. Should contain
|
||||||
|
// "{{filename}}", and may optionally contain "{{line}}".
|
||||||
|
EditAtLine string `yaml:"editAtLine,omitempty"`
|
||||||
|
|
||||||
|
// Same as EditAtLine, except that the command needs to wait until the
|
||||||
|
// 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.
|
||||||
|
// Pointer to bool so that we can distinguish unset (nil) from false.
|
||||||
|
EditInTerminal *bool `yaml:"editInTerminal,omitempty"`
|
||||||
|
|
||||||
|
// A built-in preset that sets all of the above settings. Supported presets
|
||||||
|
// are defined in the getPreset function in editor_presets.go.
|
||||||
|
EditPreset string `yaml:"editPreset,omitempty"`
|
||||||
|
|
||||||
|
// Command for opening a file, as if the file is double-clicked. Should
|
||||||
|
// contain "{{filename}}", but doesn't support "{{line}}".
|
||||||
|
Open string `yaml:"open,omitempty"`
|
||||||
|
|
||||||
|
// Command for opening a link. Should contain "{{link}}".
|
||||||
|
OpenLink string `yaml:"openLink,omitempty"`
|
||||||
|
|
||||||
|
// --------
|
||||||
|
|
||||||
|
// The following configs are all deprecated and kept for backward
|
||||||
|
// compatibility. They will be removed in the future.
|
||||||
|
|
||||||
|
// EditCommand is the command for editing a file.
|
||||||
|
// Deprecated: use Edit instead. Note that semantics are different:
|
||||||
|
// EditCommand is just the command itself, whereas Edit contains a
|
||||||
|
// "{{filename}}" variable.
|
||||||
EditCommand string `yaml:"editCommand,omitempty"`
|
EditCommand string `yaml:"editCommand,omitempty"`
|
||||||
|
|
||||||
// EditCommandTemplate is the command template for editing a file
|
// EditCommandTemplate is the command template for editing a file
|
||||||
|
// Deprecated: use EditAtLine instead.
|
||||||
EditCommandTemplate string `yaml:"editCommandTemplate,omitempty"`
|
EditCommandTemplate string `yaml:"editCommandTemplate,omitempty"`
|
||||||
|
|
||||||
// OpenCommand is the command for opening a file
|
// OpenCommand is the command for opening a file
|
||||||
|
// Deprecated: use Open instead.
|
||||||
OpenCommand string `yaml:"openCommand,omitempty"`
|
OpenCommand string `yaml:"openCommand,omitempty"`
|
||||||
|
|
||||||
// OpenCommand is the command for opening a link
|
// OpenLinkCommand is the command for opening a link
|
||||||
|
// Deprecated: use OpenLink instead.
|
||||||
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
|
OpenLinkCommand string `yaml:"openLinkCommand,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,7 +608,7 @@ func GetDefaultConfig() *UserConfig {
|
|||||||
BulkMenu: "b",
|
BulkMenu: "b",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OS: GetPlatformDefaultConfig(),
|
OS: OSConfig{},
|
||||||
DisableStartupPopups: false,
|
DisableStartupPopups: false,
|
||||||
CustomCommands: []CustomCommand(nil),
|
CustomCommands: []CustomCommand(nil),
|
||||||
Services: map[string]string(nil),
|
Services: map[string]string(nil),
|
||||||
|
@ -10,7 +10,6 @@ type IFilesHelper interface {
|
|||||||
EditFile(filename string) error
|
EditFile(filename string) error
|
||||||
EditFileAtLine(filename string, lineNumber int) error
|
EditFileAtLine(filename string, lineNumber int) error
|
||||||
OpenFile(filename string) error
|
OpenFile(filename string) error
|
||||||
OpenFileAtLine(filename string, lineNumber int) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilesHelper struct {
|
type FilesHelper struct {
|
||||||
@ -34,28 +33,37 @@ func NewFilesHelper(
|
|||||||
var _ IFilesHelper = &FilesHelper{}
|
var _ IFilesHelper = &FilesHelper{}
|
||||||
|
|
||||||
func (self *FilesHelper) EditFile(filename string) error {
|
func (self *FilesHelper) EditFile(filename string) error {
|
||||||
return self.EditFileAtLine(filename, 1)
|
cmdStr, editInTerminal := self.git.File.GetEditCmdStr(filename)
|
||||||
|
return self.callEditor(cmdStr, editInTerminal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesHelper) EditFileAtLine(filename string, lineNumber int) error {
|
func (self *FilesHelper) EditFileAtLine(filename string, lineNumber int) error {
|
||||||
cmdStr, err := self.git.File.GetEditCmdStr(filename, lineNumber)
|
cmdStr, editInTerminal := self.git.File.GetEditAtLineCmdStr(filename, lineNumber)
|
||||||
if err != nil {
|
return self.callEditor(cmdStr, editInTerminal)
|
||||||
return self.c.Error(err)
|
}
|
||||||
|
|
||||||
|
func (self *FilesHelper) EditFileAtLineAndWait(filename string, lineNumber int) error {
|
||||||
|
cmdStr := self.git.File.GetEditAtLineAndWaitCmdStr(filename, lineNumber)
|
||||||
|
|
||||||
|
// Always suspend, regardless of the value of the editInTerminal 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) callEditor(cmdStr string, editInTerminal bool) error {
|
||||||
|
if editInTerminal {
|
||||||
|
return self.c.RunSubprocessAndRefresh(
|
||||||
|
self.os.Cmd.NewShell(cmdStr),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.EditFile)
|
return self.os.Cmd.NewShell(cmdStr).Run()
|
||||||
return self.c.RunSubprocessAndRefresh(
|
|
||||||
self.os.Cmd.NewShell(cmdStr),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *FilesHelper) OpenFile(filename string) error {
|
func (self *FilesHelper) OpenFile(filename string) error {
|
||||||
return self.OpenFileAtLine(filename, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *FilesHelper) OpenFileAtLine(filename string, lineNumber int) error {
|
|
||||||
self.c.LogAction(self.c.Tr.Actions.OpenFile)
|
self.c.LogAction(self.c.Tr.Actions.OpenFile)
|
||||||
if err := self.os.OpenFileAtLine(filename, lineNumber); err != nil {
|
if err := self.os.OpenFile(filename); err != nil {
|
||||||
return self.c.Error(err)
|
return self.c.Error(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -166,8 +166,7 @@ func (self *MergeConflictsController) HandleEditFile() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) HandleOpenFile() error {
|
func (self *MergeConflictsController) HandleOpenFile() error {
|
||||||
lineNumber := self.context().GetState().GetSelectedLine()
|
return self.helpers.Files.OpenFile(self.context().GetState().GetPath())
|
||||||
return self.helpers.Files.OpenFileAtLine(self.context().GetState().GetPath(), lineNumber)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *MergeConflictsController) HandleScrollLeft() error {
|
func (self *MergeConflictsController) HandleScrollLeft() error {
|
||||||
|
@ -69,8 +69,7 @@ func (self *PatchBuildingController) OpenFile() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lineNumber := self.context().GetState().CurrentLineNumber()
|
return self.helpers.Files.OpenFile(path)
|
||||||
return self.helpers.Files.OpenFileAtLine(path, lineNumber)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *PatchBuildingController) EditFile() error {
|
func (self *PatchBuildingController) EditFile() error {
|
||||||
|
@ -109,8 +109,7 @@ func (self *StagingController) OpenFile() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
lineNumber := self.context.GetState().CurrentLineNumber()
|
return self.helpers.Files.OpenFile(path)
|
||||||
return self.helpers.Files.OpenFileAtLine(path, lineNumber)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StagingController) EditFile() error {
|
func (self *StagingController) EditFile() error {
|
||||||
@ -247,7 +246,7 @@ func (self *StagingController) editHunk() error {
|
|||||||
|
|
||||||
lineOffset := 3
|
lineOffset := 3
|
||||||
lineIdxInHunk := state.GetSelectedLineIdx() - hunkStartIdx
|
lineIdxInHunk := state.GetSelectedLineIdx() - hunkStartIdx
|
||||||
if err := self.helpers.Files.EditFileAtLine(patchFilepath, lineIdxInHunk+lineOffset); err != nil {
|
if err := self.helpers.Files.EditFileAtLineAndWait(patchFilepath, lineIdxInHunk+lineOffset); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,6 +495,8 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defer gui.checkForDeprecatedEditConfigs()
|
||||||
|
|
||||||
gui.g = g
|
gui.g = g
|
||||||
defer gui.g.Close()
|
defer gui.g.Close()
|
||||||
|
|
||||||
@ -583,6 +585,37 @@ func (gui *Gui) RunAndHandleError(startArgs appTypes.StartArgs) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gui *Gui) checkForDeprecatedEditConfigs() {
|
||||||
|
osConfig := &gui.UserConfig.OS
|
||||||
|
deprecatedConfigs := []struct {
|
||||||
|
config string
|
||||||
|
oldName string
|
||||||
|
newName string
|
||||||
|
}{
|
||||||
|
{osConfig.EditCommand, "EditCommand", "Edit"},
|
||||||
|
{osConfig.EditCommandTemplate, "EditCommandTemplate", "Edit,EditAtLine"},
|
||||||
|
{osConfig.OpenCommand, "OpenCommand", "Open"},
|
||||||
|
{osConfig.OpenLinkCommand, "OpenLinkCommand", "OpenLink"},
|
||||||
|
}
|
||||||
|
deprecatedConfigStrings := []string{}
|
||||||
|
|
||||||
|
for _, dc := range deprecatedConfigs {
|
||||||
|
if dc.config != "" {
|
||||||
|
deprecatedConfigStrings = append(deprecatedConfigStrings, fmt.Sprintf(" OS.%s -> OS.%s", dc.oldName, dc.newName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(deprecatedConfigStrings) != 0 {
|
||||||
|
warningMessage := utils.ResolvePlaceholderString(
|
||||||
|
gui.c.Tr.DeprecatedEditConfigWarning,
|
||||||
|
map[string]string{
|
||||||
|
"configs": strings.Join(deprecatedConfigStrings, "\n"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
os.Stdout.Write([]byte(warningMessage))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// returns whether command exited without error or not
|
// returns whether command exited without error or not
|
||||||
func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess oscommands.ICmdObj) error {
|
func (gui *Gui) runSubprocessWithSuspenseAndRefresh(subprocess oscommands.ICmdObj) error {
|
||||||
_, err := gui.runSubprocessWithSuspense(subprocess)
|
_, err := gui.runSubprocessWithSuspense(subprocess)
|
||||||
|
@ -155,6 +155,7 @@ type TranslationSet struct {
|
|||||||
MergeToolTitle string
|
MergeToolTitle string
|
||||||
MergeToolPrompt string
|
MergeToolPrompt string
|
||||||
IntroPopupMessage string
|
IntroPopupMessage string
|
||||||
|
DeprecatedEditConfigWarning string
|
||||||
GitconfigParseErr string
|
GitconfigParseErr string
|
||||||
LcEditFile string
|
LcEditFile string
|
||||||
LcOpenFile string
|
LcOpenFile string
|
||||||
@ -659,6 +660,21 @@ Thanks for using lazygit! Seriously you rock. Three things to share with you:
|
|||||||
Or even just star the repo to share the love!
|
Or even just star the repo to share the love!
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const englishDeprecatedEditConfigWarning = `
|
||||||
|
### Deprecated config warning ###
|
||||||
|
|
||||||
|
The following config settings are deprecated and will be removed in a future
|
||||||
|
version:
|
||||||
|
{{configs}}
|
||||||
|
|
||||||
|
Please refer to
|
||||||
|
|
||||||
|
https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md#configuring-file-editing
|
||||||
|
|
||||||
|
for up-to-date information how to configure your editor.
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
// exporting this so we can use it in tests
|
// exporting this so we can use it in tests
|
||||||
func EnglishTranslationSet() TranslationSet {
|
func EnglishTranslationSet() TranslationSet {
|
||||||
return TranslationSet{
|
return TranslationSet{
|
||||||
@ -805,6 +821,7 @@ func EnglishTranslationSet() TranslationSet {
|
|||||||
MergeToolTitle: "Merge tool",
|
MergeToolTitle: "Merge tool",
|
||||||
MergeToolPrompt: "Are you sure you want to open `git mergetool`?",
|
MergeToolPrompt: "Are you sure you want to open `git mergetool`?",
|
||||||
IntroPopupMessage: englishIntroPopupMessage,
|
IntroPopupMessage: englishIntroPopupMessage,
|
||||||
|
DeprecatedEditConfigWarning: englishDeprecatedEditConfigWarning,
|
||||||
GitconfigParseErr: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
|
GitconfigParseErr: `Gogit failed to parse your gitconfig file due to the presence of unquoted '\' characters. Removing these should fix the issue.`,
|
||||||
LcEditFile: `edit file`,
|
LcEditFile: `edit file`,
|
||||||
LcOpenFile: `open file`,
|
LcOpenFile: `open file`,
|
||||||
|
Loading…
Reference in New Issue
Block a user