summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2024-03-20 21:01:20 +0100
committerStefan Haller <stefan@haller-berlin.de>2024-03-22 08:20:16 +0100
commit73019574a886a825a09dbc8619f883983cd39278 (patch)
tree976d8bbb57dd146d1a05707d77c18c1061b3d60f
parent9b5269b4905aaea56a57cf2d4123a16fb3fed91c (diff)
Support editing multiple files at once using range selection
We pass all of them to a single editor command, hoping that the editor will be able to handle multiple files (VS Code and vim do). We ignore directories that happen to be in the selection range; this makes it easier to edit multiple files in different folders in tree view. We show an error if only directories are selected, though.
-rw-r--r--pkg/commands/git_commands/file.go10
-rw-r--r--pkg/commands/git_commands/file_test.go20
-rw-r--r--pkg/gui/controllers/commits_files_controller.go22
-rw-r--r--pkg/gui/controllers/files_controller.go22
-rw-r--r--pkg/gui/controllers/helpers/files_helper.go19
-rw-r--r--pkg/gui/controllers/status_controller.go4
-rw-r--r--pkg/i18n/english.go2
7 files changed, 69 insertions, 30 deletions
diff --git a/pkg/commands/git_commands/file.go b/pkg/commands/git_commands/file.go
index 173e342f7..9401e6d54 100644
--- a/pkg/commands/git_commands/file.go
+++ b/pkg/commands/git_commands/file.go
@@ -8,6 +8,7 @@ import (
"github.com/go-errors/errors"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/utils"
+ "github.com/samber/lo"
)
type FileCommands struct {
@@ -75,18 +76,21 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
return utils.ResolvePlaceholderString(editCmdTemplate, templateValues), nil
}
-func (self *FileCommands) GetEditCmdStr(filename string) (string, bool) {
+func (self *FileCommands) GetEditCmdStr(filenames []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 {
+ // If multiple files are selected, we'll simply edit just the first one.
+ // It's not worth fixing this for the legacy support.
+ if cmdStr, err := self.GetEditCmdStrLegacy(filenames[0], 1); err == nil {
return cmdStr, true
}
}
template, suspend := config.GetEditTemplate(&self.UserConfig.OS, self.guessDefaultEditor)
+ quotedFilenames := lo.Map(filenames, func(filename string, _ int) string { return self.cmd.Quote(filename) })
templateValues := map[string]string{
- "filename": self.cmd.Quote(filename),
+ "filename": strings.Join(quotedFilenames, " "),
}
cmdStr := utils.ResolvePlaceholderString(template, templateValues)
diff --git a/pkg/commands/git_commands/file_test.go b/pkg/commands/git_commands/file_test.go
index c87e56683..25dd0a5d0 100644
--- a/pkg/commands/git_commands/file_test.go
+++ b/pkg/commands/git_commands/file_test.go
@@ -177,9 +177,9 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
}
}
-func TestEditFileCmd(t *testing.T) {
+func TestEditFilesCmd(t *testing.T) {
type scenario struct {
- filename string
+ filenames []string
osConfig config.OSConfig
expectedCmdStr string
suspend bool
@@ -187,13 +187,13 @@ func TestEditFileCmd(t *testing.T) {
scenarios := []scenario{
{
- filename: "test",
+ filenames: []string{"test"},
osConfig: config.OSConfig{},
expectedCmdStr: `vim -- "test"`,
suspend: true,
},
{
- filename: "test",
+ filenames: []string{"test"},
osConfig: config.OSConfig{
Edit: "nano {{filename}}",
},
@@ -201,13 +201,21 @@ func TestEditFileCmd(t *testing.T) {
suspend: true,
},
{
- filename: "file/with space",
+ filenames: []string{"file/with space"},
osConfig: config.OSConfig{
EditPreset: "sublime",
},
expectedCmdStr: `subl -- "file/with space"`,
suspend: false,
},
+ {
+ filenames: []string{"multiple", "files"},
+ osConfig: config.OSConfig{
+ EditPreset: "sublime",
+ },
+ expectedCmdStr: `subl -- "multiple" "files"`,
+ suspend: false,
+ },
}
for _, s := range scenarios {
@@ -218,7 +226,7 @@ func TestEditFileCmd(t *testing.T) {
userConfig: userConfig,
})
- cmdStr, suspend := instance.GetEditCmdStr(s.filename)
+ cmdStr, suspend := instance.GetEditCmdStr(s.filenames)
assert.Equal(t, s.expectedCmdStr, cmdStr)
assert.Equal(t, s.suspend, suspend)
}
diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go
index 59e0a7f4e..75524938c 100644
--- a/pkg/gui/controllers/commits_files_controller.go
+++ b/pkg/gui/controllers/commits_files_controller.go
@@ -65,8 +65,8 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []
},
{
Key: opts.GetKey(opts.Config.Universal.Edit),
- Handler: self.withItem(self.edit),
- GetDisabledReason: self.require(self.singleItemSelected()),
+ Handler: self.withItems(self.edit),
+ GetDisabledReason: self.require(self.itemsSelected(self.canEditFiles)),
Description: self.c.Tr.Edit,
Tooltip: self.c.Tr.EditFileTooltip,
DisplayOnScreen: true,
@@ -230,12 +230,22 @@ func (self *CommitFilesController) open(node *filetree.CommitFileNode) error {
return self.c.Helpers().Files.OpenFile(node.GetPath())
}
-func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
- if node.File == nil {
- return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
+func (self *CommitFilesController) edit(nodes []*filetree.CommitFileNode) error {
+ return self.c.Helpers().Files.EditFiles(lo.FilterMap(nodes,
+ func(node *filetree.CommitFileNode, _ int) (string, bool) {
+ return node.GetPath(), node.IsFile()
+ }))
+}
+
+func (self *CommitFilesController) canEditFiles(nodes []*filetree.CommitFileNode) *types.DisabledReason {
+ if lo.NoneBy(nodes, func(node *filetree.CommitFileNode) bool { return node.IsFile() }) {
+ return &types.DisabledReason{
+ Text: self.c.Tr.ErrCannotEditDirectory,
+ ShowErrorInPanel: true,
+ }
}
- return self.c.Helpers().Files.EditFile(node.GetPath())
+ return nil
}
func (self *CommitFilesController) openDiffTool(node *filetree.CommitFileNode) error {
diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go
index 504617218..313c23b42 100644
--- a/pkg/gui/controllers/files_controller.go
+++ b/pkg/gui/controllers/files_controller.go
@@ -86,8 +86,8 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
},
{
Key: opts.GetKey(opts.Config.Universal.Edit),
- Handler: self.withItem(self.edit),
- GetDisabledReason: self.require(self.singleItemSelected()),
+ Handler: self.withItems(self.edit),
+ GetDisabledReason: self.require(self.itemsSelected(self.canEditFiles)),
Description: self.c.Tr.Edit,
Tooltip: self.c.Tr.EditFileTooltip,
DisplayOnScreen: true,
@@ -714,12 +714,22 @@ func (self *FilesController) setStatusFiltering(filter filetree.FileTreeDisplayF
return self.c.PostRefreshUpdate(self.context())
}
-func (self *FilesController) edit(node *filetree.FileNode) error {
- if node.File == nil {
- return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
+func (self *FilesController) edit(nodes []*filetree.FileNode) error {
+ return self.c.Helpers().Files.EditFiles(lo.FilterMap(nodes,
+ func(node *filetree.FileNode, _ int) (string, bool) {
+ return node.GetPath(), node.IsFile()
+ }))
+}
+
+func (self *FilesController) canEditFiles(nodes []*filetree.FileNode) *types.DisabledReason {
+ if lo.NoneBy(nodes, func(node *filetree.FileNode) bool { return node.IsFile() }) {
+ return &types.DisabledReason{
+ Text: self.c.Tr.ErrCannotEditDirectory,
+ ShowErrorInPanel: true,
+ }
}
- return self.c.Helpers().Files.EditFile(node.GetPath())
+ return nil
}
func (self *FilesController) Open() error {
diff --git a/pkg/gui/controllers/helpers/files_helper.go b/pkg/gui/controllers/helpers/files_helper.go
index 35cd9d0c9..dded9877f 100644
--- a/pkg/gui/controllers/helpers/files_helper.go
+++ b/pkg/gui/controllers/helpers/files_helper.go
@@ -2,10 +2,12 @@ package helpers
import (
"path/filepath"
+
+ "github.com/samber/lo"
)
type IFilesHelper interface {
- EditFile(filename string) error
+ EditFiles(filenames []string) error
EditFileAtLine(filename string, lineNumber int) error
OpenFile(filename string) error
}
@@ -22,12 +24,15 @@ func NewFilesHelper(c *HelperCommon) *FilesHelper {
var _ IFilesHelper = &FilesHelper{}
-func (self *FilesHelper) EditFile(filename string) error {
- absPath, err := filepath.Abs(filename)
- if err != nil {
- return err
- }
- cmdStr, suspend := self.c.Git().File.GetEditCmdStr(absPath)
+func (self *FilesHelper) EditFiles(filenames []string) error {
+ absPaths := lo.Map(filenames, func(filename string, _ int) string {
+ absPath, err := filepath.Abs(filename)
+ if err != nil {
+ return filename
+ }
+ return absPath
+ })
+ cmdStr, suspend := self.c.Git().File.GetEditCmdStr(absPaths)
return self.callEditor(cmdStr, suspend)
}
diff --git a/pkg/gui/controllers/status_controller.go b/pkg/gui/controllers/status_controller.go
index f0289bf3c..e8455fe22 100644
--- a/pkg/gui/controllers/status_controller.go
+++ b/pkg/gui/controllers/status_controller.go
@@ -217,7 +217,9 @@ func (self *StatusController) openConfig() error {
}
func (self *StatusController) editConfig() error {
- return self.askForConfigFile(self.c.Helpers().Files.EditFile)
+ return self.askForConfigFile(func(file string) error {
+ return self.c.Helpers().Files.EditFiles([]string{file})
+ })
}
func (self *StatusController) showAllBranchLogs() error {
diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go
index 7715001aa..6716673b3 100644
--- a/pkg/i18n/english.go
+++ b/pkg/i18n/english.go
@@ -1596,7 +1596,7 @@ func EnglishTranslationSet() TranslationSet {
CommitAuthorCopiedToClipboard: "Commit author copied to clipboard",
PatchCopiedToClipboard: "Patch copied to clipboard",
CopiedToClipboard: "Copied to clipboard",
- ErrCannotEditDirectory: "Cannot edit directory: you can only edit individual files",
+ ErrCannotEditDirectory: "Cannot edit directories: you can only edit individual files",
ErrStageDirWithInlineMergeConflicts: "Cannot stage/unstage directory containing files with inline merge conflicts. Please fix up the merge conflicts first",
ErrRepositoryMovedOrDeleted: "Cannot find repo. It might have been moved or deleted ¯\\_(ツ)_/¯",
CommandLog: "Command log",