diff --git a/docs/Config.md b/docs/Config.md index ed39cd4883e..ce1fff462f8 100644 --- a/docs/Config.md +++ b/docs/Config.md @@ -571,6 +571,8 @@ keybinding: openMergeTool: M openStatusFilter: copyFileInfoToClipboard: "y" + collapseAll: "-" + expandAll: "=" branches: createPullRequest: o viewPullRequestOptions: O diff --git a/pkg/config/user_config.go b/pkg/config/user_config.go index 36dc02a68a7..3a3fed029ae 100644 --- a/pkg/config/user_config.go +++ b/pkg/config/user_config.go @@ -453,6 +453,8 @@ type KeybindingFilesConfig struct { OpenMergeTool string `yaml:"openMergeTool"` OpenStatusFilter string `yaml:"openStatusFilter"` CopyFileInfoToClipboard string `yaml:"copyFileInfoToClipboard"` + CollapseAll string `yaml:"collapseAll"` + ExpandAll string `yaml:"expandAll"` } type KeybindingBranchesConfig struct { @@ -895,6 +897,8 @@ func GetDefaultConfig() *UserConfig { OpenStatusFilter: "", ConfirmDiscard: "x", CopyFileInfoToClipboard: "y", + CollapseAll: "-", + ExpandAll: "=", }, Branches: KeybindingBranchesConfig{ CopyPullRequestURL: "", diff --git a/pkg/gui/controllers/commits_files_controller.go b/pkg/gui/controllers/commits_files_controller.go index 462b9c3ee5a..5f7b55fcea3 100644 --- a/pkg/gui/controllers/commits_files_controller.go +++ b/pkg/gui/controllers/commits_files_controller.go @@ -109,6 +109,18 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) [] Description: self.c.Tr.ToggleTreeView, Tooltip: self.c.Tr.ToggleTreeViewTooltip, }, + { + Key: opts.GetKey(opts.Config.Files.CollapseAll), + Handler: self.collapseAll, + Description: self.c.Tr.CollapseAll, + Tooltip: self.c.Tr.CollapseAllTooltip, + }, + { + Key: opts.GetKey(opts.Config.Files.ExpandAll), + Handler: self.expandAll, + Description: self.c.Tr.ExpandAll, + Tooltip: self.c.Tr.ExpandAllTooltip, + }, } return bindings @@ -401,6 +413,34 @@ func (self *CommitFilesController) toggleTreeView() error { return nil } +func (self *CommitFilesController) collapseAll() error { + nodes := self.context().GetAllItems() + + dirPaths := lo.FilterMap(nodes, func(file *filetree.CommitFileNode, index int) (string, bool) { + return file.Path, !file.IsFile() + }) + + self.context().CommitFileTreeViewModel.CollapseAll(dirPaths) + + self.c.PostRefreshUpdate(self.context()) + + return nil +} + +func (self *CommitFilesController) expandAll() error { + files := self.context().GetAllFiles() + + filePaths := lo.Map(files, func(file *models.CommitFile, index int) string { + return file.GetPath() + }) + + self.context().CommitFileTreeViewModel.UncollapseAll(filePaths) + + self.c.PostRefreshUpdate(self.context()) + + return nil +} + // NOTE: these functions are identical to those in files_controller.go (except for types) and // could also be cleaned up with some generics func normalisedSelectedCommitFileNodes(selectedNodes []*filetree.CommitFileNode) []*filetree.CommitFileNode { diff --git a/pkg/gui/controllers/files_controller.go b/pkg/gui/controllers/files_controller.go index baacc8061f9..c7240ed9a09 100644 --- a/pkg/gui/controllers/files_controller.go +++ b/pkg/gui/controllers/files_controller.go @@ -186,6 +186,18 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types Description: self.c.Tr.Fetch, Tooltip: self.c.Tr.FetchTooltip, }, + { + Key: opts.GetKey(opts.Config.Files.CollapseAll), + Handler: self.collapseAll, + Description: self.c.Tr.CollapseAll, + Tooltip: self.c.Tr.CollapseAllTooltip, + }, + { + Key: opts.GetKey(opts.Config.Files.ExpandAll), + Handler: self.expandAll, + Description: self.c.Tr.ExpandAll, + Tooltip: self.c.Tr.ExpandAllTooltip, + }, } } @@ -478,6 +490,34 @@ func (self *FilesController) enter() error { return self.EnterFile(types.OnFocusOpts{ClickedWindowName: "", ClickedViewLineIdx: -1}) } +func (self *FilesController) collapseAll() error { + nodes := self.context().GetAllItems() + + dirPaths := lo.FilterMap(nodes, func(file *filetree.FileNode, index int) (string, bool) { + return file.Path, !file.IsFile() + }) + + self.context().FileTreeViewModel.CollapseAll(dirPaths) + + self.c.PostRefreshUpdate(self.context()) + + return nil +} + +func (self *FilesController) expandAll() error { + files := self.context().GetAllFiles() + + filePaths := lo.Map(files, func(file *models.File, index int) string { + return file.GetPath() + }) + + self.context().FileTreeViewModel.UncollapseAll(filePaths) + + self.c.PostRefreshUpdate(self.context()) + + return nil +} + func (self *FilesController) EnterFile(opts types.OnFocusOpts) error { node := self.context().GetSelected() if node == nil { diff --git a/pkg/gui/filetree/commit_file_tree.go b/pkg/gui/filetree/commit_file_tree.go index 2593828eea3..911d16c8926 100644 --- a/pkg/gui/filetree/commit_file_tree.go +++ b/pkg/gui/filetree/commit_file_tree.go @@ -25,6 +25,18 @@ type CommitFileTree struct { collapsedPaths *CollapsedPaths } +func (self *CommitFileTree) CollapseAll(paths []string) { + for _, path := range paths { + self.collapsedPaths.Collapse(path) + } +} + +func (self *CommitFileTree) UncollapseAll(paths []string) { + for _, path := range paths { + self.collapsedPaths.ExpandToPath(path) + } +} + var _ ICommitFileTree = &CommitFileTree{} func NewCommitFileTree(getFiles func() []*models.CommitFile, log *logrus.Entry, showTree bool) *CommitFileTree { diff --git a/pkg/gui/filetree/file_tree.go b/pkg/gui/filetree/file_tree.go index 12780e3ed3d..70fe3e56e79 100644 --- a/pkg/gui/filetree/file_tree.go +++ b/pkg/gui/filetree/file_tree.go @@ -31,6 +31,8 @@ type ITree[T any] interface { IsCollapsed(path string) bool ToggleCollapsed(path string) CollapsedPaths() *CollapsedPaths + CollapseAll(paths []string) + UncollapseAll(paths []string) } type IFileTree interface { @@ -171,6 +173,18 @@ func (self *FileTree) ToggleCollapsed(path string) { self.collapsedPaths.ToggleCollapsed(path) } +func (self *FileTree) CollapseAll(paths []string) { + for _, path := range paths { + self.collapsedPaths.Collapse(path) + } +} + +func (self *FileTree) UncollapseAll(paths []string) { + for _, path := range paths { + self.collapsedPaths.ExpandToPath(path) + } +} + func (self *FileTree) Tree() *FileNode { return NewFileNode(self.tree) } diff --git a/pkg/i18n/english.go b/pkg/i18n/english.go index e777bdc9619..399ccafc0aa 100644 --- a/pkg/i18n/english.go +++ b/pkg/i18n/english.go @@ -256,6 +256,10 @@ type TranslationSet struct { NoBranchOnRemote string Fetch string FetchTooltip string + CollapseAll string + CollapseAllTooltip string + ExpandAll string + ExpandAllTooltip string FileEnter string FileEnterTooltip string FileStagingRequirements string @@ -1258,6 +1262,10 @@ func EnglishTranslationSet() *TranslationSet { NoBranchOnRemote: `This branch doesn't exist on remote. You need to push it to remote first.`, Fetch: `Fetch`, FetchTooltip: "Fetch changes from remote.", + CollapseAll: "Collapse all files", + CollapseAllTooltip: "Collapse all entries in the files tree", + ExpandAll: "Expand all files", + ExpandAllTooltip: "Expand all entries in the file tree", FileEnter: `Stage lines / Collapse directory`, FileEnterTooltip: "If the selected item is a file, focus the staging view so you can stage individual hunks/lines. If the selected item is a directory, collapse/expand it.", FileStagingRequirements: `Can only stage individual lines for tracked files`, diff --git a/schema/config.json b/schema/config.json index ee6f37ca508..69a7a504c6a 100644 --- a/schema/config.json +++ b/schema/config.json @@ -1456,6 +1456,14 @@ "copyFileInfoToClipboard": { "type": "string", "default": "y" + }, + "collapseAll": { + "type": "string", + "default": "-" + }, + "expandAll": { + "type": "string", + "default": "=" } }, "additionalProperties": false,