summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2022-01-22 00:13:51 +1100
committerJesse Duffield <jessedduffield@gmail.com>2022-01-22 10:48:51 +1100
commit5b7dd9e43ccd2b82f07f0f0ff0ee8d54d0ecba11 (patch)
tree2afa1c595dad6ae8c0503217eac3f40e2a925924
parent4ab5e5413944a92699a9540148666f0de26aa44b (diff)
properly resolve cyclic dependency
-rw-r--r--pkg/commands/git_commands/working_tree.go17
-rw-r--r--pkg/gui/commit_files_panel.go25
-rw-r--r--pkg/gui/files_panel.go46
-rw-r--r--pkg/gui/filetree/README.md22
-rw-r--r--pkg/gui/filetree/commit_file_manager.go118
-rw-r--r--pkg/gui/filetree/commit_file_node.go13
-rw-r--r--pkg/gui/filetree/commit_file_tree_view_model.go101
-rw-r--r--pkg/gui/filetree/constants.go9
-rw-r--r--pkg/gui/filetree/file_manager.go140
-rw-r--r--pkg/gui/filetree/file_manager_test.go156
-rw-r--r--pkg/gui/filetree/file_node.go17
-rw-r--r--pkg/gui/filetree/file_tree_view_model.go139
-rw-r--r--pkg/gui/filetree/file_tree_view_model_test.go67
-rw-r--r--pkg/gui/filetree/inode.go51
-rw-r--r--pkg/gui/filetree/presentation.go96
-rw-r--r--pkg/gui/gui.go26
-rw-r--r--pkg/gui/line_by_line_panel.go2
-rw-r--r--pkg/gui/list_context_config.go10
-rw-r--r--pkg/gui/patch_building_panel.go2
-rw-r--r--pkg/gui/presentation/files.go204
-rw-r--r--pkg/gui/presentation/files_test.go146
-rw-r--r--pkg/gui/submodules_panel.go2
22 files changed, 768 insertions, 641 deletions
diff --git a/pkg/commands/git_commands/working_tree.go b/pkg/commands/git_commands/working_tree.go
index c40f75693..4e97913fb 100644
--- a/pkg/commands/git_commands/working_tree.go
+++ b/pkg/commands/git_commands/working_tree.go
@@ -10,7 +10,6 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/loaders"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
- "github.com/jesseduffield/lazygit/pkg/gui/filetree"
"github.com/jesseduffield/lazygit/pkg/utils"
)
@@ -160,12 +159,18 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
return self.DiscardUnstagedFileChanges(file)
}
-func (self *WorkingTreeCommands) DiscardAllDirChanges(node *filetree.FileNode) error {
+type IFileNode interface {
+ ForEachFile(cb func(*models.File) error) error
+ GetFilePathsMatching(test func(*models.File) bool) []string
+ GetPath() string
+}
+
+func (self *WorkingTreeCommands) DiscardAllDirChanges(node IFileNode) error {
// this could be more efficient but we would need to handle all the edge cases
return node.ForEachFile(self.DiscardAllFileChanges)
}
-func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node *filetree.FileNode) error {
+func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error {
if err := self.RemoveUntrackedDirFiles(node); err != nil {
return err
}
@@ -178,9 +183,9 @@ func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node *filetree.FileNo
return nil
}
-func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node *filetree.FileNode) error {
- untrackedFilePaths := node.GetPathsMatching(
- func(n *filetree.FileNode) bool { return n.File != nil && !n.File.GetIsTracked() },
+func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
+ untrackedFilePaths := node.GetFilePathsMatching(
+ func(file *models.File) bool { return !file.GetIsTracked() },
)
for _, path := range untrackedFilePaths {
diff --git a/pkg/gui/commit_files_panel.go b/pkg/gui/commit_files_panel.go
index 7e1ecd130..61f3b72b8 100644
--- a/pkg/gui/commit_files_panel.go
+++ b/pkg/gui/commit_files_panel.go
@@ -8,11 +8,11 @@ import (
func (gui *Gui) getSelectedCommitFileNode() *filetree.CommitFileNode {
selectedLine := gui.State.Panels.CommitFiles.SelectedLineIdx
- if selectedLine == -1 || selectedLine > gui.State.CommitFileManager.GetItemsLength()-1 {
+ if selectedLine == -1 || selectedLine > gui.State.CommitFileTreeViewModel.GetItemsLength()-1 {
return nil
}
- return gui.State.CommitFileManager.GetItemAtIndex(selectedLine)
+ return gui.State.CommitFileTreeViewModel.GetItemAtIndex(selectedLine)
}
func (gui *Gui) getSelectedCommitFile() *models.CommitFile {
@@ -42,7 +42,7 @@ func (gui *Gui) commitFilesRenderToMain() error {
return nil
}
- to := gui.State.CommitFileManager.GetParent()
+ to := gui.State.CommitFileTreeViewModel.GetParent()
from, reverse := gui.getFromAndReverseArgsForDiff(to)
cmdObj := gui.Git.WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false)
@@ -64,7 +64,7 @@ func (gui *Gui) handleCheckoutCommitFile() error {
}
gui.logAction(gui.Tr.Actions.CheckoutFile)
- if err := gui.Git.WorkingTree.CheckoutFile(gui.State.CommitFileManager.GetParent(), node.GetPath()); err != nil {
+ if err := gui.Git.WorkingTree.CheckoutFile(gui.State.CommitFileTreeViewModel.GetParent(), node.GetPath()); err != nil {
return gui.surfaceError(err)
}
@@ -111,7 +111,8 @@ func (gui *Gui) refreshCommitFilesView() error {
if err != nil {
return gui.surfaceError(err)
}
- gui.State.CommitFileManager.SetFiles(files, to)
+ gui.State.CommitFileTreeViewModel.SetParent(to)
+ gui.State.CommitFileTreeViewModel.SetFiles(files)
return gui.postRefreshUpdate(gui.State.Contexts.CommitFiles)
}
@@ -154,7 +155,7 @@ func (gui *Gui) handleToggleFileForPatch() error {
// if there is any file that hasn't been fully added we'll fully add everything,
// otherwise we'll remove everything
adding := node.AnyFile(func(file *models.CommitFile) bool {
- return gui.Git.Patch.PatchManager.GetFileStatus(file.Name, gui.State.CommitFileManager.GetParent()) != patch.WHOLE
+ return gui.Git.Patch.PatchManager.GetFileStatus(file.Name, gui.State.CommitFileTreeViewModel.GetParent()) != patch.WHOLE
})
err := node.ForEachFile(func(file *models.CommitFile) error {
@@ -176,7 +177,7 @@ func (gui *Gui) handleToggleFileForPatch() error {
return gui.postRefreshUpdate(gui.State.Contexts.CommitFiles)
}
- if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileManager.GetParent() {
+ if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileTreeViewModel.GetParent() {
return gui.ask(askOpts{
title: gui.Tr.DiscardPatch,
prompt: gui.Tr.DiscardPatchConfirm,
@@ -224,7 +225,7 @@ func (gui *Gui) enterCommitFile(opts OnFocusOpts) error {
return gui.pushContext(gui.State.Contexts.PatchBuilding, opts)
}
- if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileManager.GetParent() {
+ if gui.Git.Patch.PatchManager.Active() && gui.Git.Patch.PatchManager.To != gui.State.CommitFileTreeViewModel.GetParent() {
return gui.ask(askOpts{
title: gui.Tr.DiscardPatch,
prompt: gui.Tr.DiscardPatchConfirm,
@@ -244,7 +245,7 @@ func (gui *Gui) handleToggleCommitFileDirCollapsed() error {
return nil
}
- gui.State.CommitFileManager.ToggleCollapsed(node.GetPath())
+ gui.State.CommitFileTreeViewModel.ToggleCollapsed(node.GetPath())
if err := gui.postRefreshUpdate(gui.State.Contexts.CommitFiles); err != nil {
gui.Log.Error(err)
@@ -275,12 +276,12 @@ func (gui *Gui) switchToCommitFilesContext(refName string, canRebase bool, conte
func (gui *Gui) handleToggleCommitFileTreeView() error {
path := gui.getSelectedCommitFilePath()
- gui.State.CommitFileManager.ToggleShowTree()
+ gui.State.CommitFileTreeViewModel.ToggleShowTree()
// find that same node in the new format and move the cursor to it
if path != "" {
- gui.State.CommitFileManager.ExpandToPath(path)
- index, found := gui.State.CommitFileManager.GetIndexForPath(path)
+ gui.State.CommitFileTreeViewModel.ExpandToPath(path)
+ index, found := gui.State.CommitFileTreeViewModel.GetIndexForPath(path)
if found {
gui.State.Contexts.CommitFiles.GetPanelState().SetSelectedLineIdx(index)
}
diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go
index 4201afb11..c62da8e21 100644
--- a/pkg/gui/files_panel.go
+++ b/pkg/gui/files_panel.go
@@ -21,7 +21,7 @@ func (gui *Gui) getSelectedFileNode() *filetree.FileNode {
return nil
}
- return gui.State.FileManager.GetItemAtIndex(selectedLine)
+ return gui.State.FileTreeViewModel.GetItemAtIndex(selectedLine)
}
func (gui *Gui) getSelectedFile() *models.File {
@@ -129,7 +129,7 @@ func (gui *Gui) refreshFilesAndSubmodules() error {
// specific functions
func (gui *Gui) stagedFiles() []*models.File {
- files := gui.State.FileManager.GetAllFiles()
+ files := gui.State.FileTreeViewModel.GetAllFiles()
result := make([]*models.File, 0)
for _, file := range files {
if file.HasStagedChanges {
@@ -140,7 +140,7 @@ func (gui *Gui) stagedFiles() []*models.File {
}
func (gui *Gui) trackedFiles() []*models.File {
- files := gui.State.FileManager.GetAllFiles()
+ files := gui.State.FileTreeViewModel.GetAllFiles()
result := make([]*models.File, 0, len(files))
for _, file := range files {
if file.Tracked {
@@ -244,7 +244,7 @@ func (gui *Gui) handleFilePress() error {
}
func (gui *Gui) allFilesStaged() bool {
- for _, file := range gui.State.FileManager.GetAllFiles() {
+ for _, file := range gui.State.FileTreeViewModel.GetAllFiles() {
if file.HasUnstagedChanges {
return false
}
@@ -378,7 +378,7 @@ func (gui *Gui) handleCommitPress() error {
return gui.surfaceError(err)
}
- if gui.State.FileManager.GetItemsLength() == 0 {
+ if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
}
@@ -433,7 +433,7 @@ func (gui *Gui) promptToStageAllAndRetry(retry func() error) error {
}
func (gui *Gui) handleAmendCommitPress() error {
- if gui.State.FileManager.GetItemsLength() == 0 {
+ if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
}
@@ -459,7 +459,7 @@ func (gui *Gui) handleAmendCommitPress() error {
// handleCommitEditorPress - handle when the user wants to commit changes via
// their editor rather than via the popup panel
func (gui *Gui) handleCommitEditorPress() error {
- if gui.State.FileManager.GetItemsLength() == 0 {
+ if gui.State.FileTreeViewModel.GetItemsLength() == 0 {
return gui.createErrorPanel(gui.Tr.NoFilesStagedTitle)
}
@@ -498,9 +498,9 @@ func (gui *Gui) handleStatusFilterPressed() error {
return gui.createMenu(gui.Tr.FilteringMenuTitle, menuItems, createMenuOptions{showCancel: true})
}
-func (gui *Gui) setStatusFiltering(filter filetree.FileManagerDisplayFilter) error {
+func (gui *Gui) setStatusFiltering(filter filetree.FileTreeDisplayFilter) error {
state := gui.State
- state.FileManager.SetDisplayFilter(filter)
+ state.FileTreeViewModel.SetDisplayFilter(filter)
return gui.handleRefreshFiles()
}
@@ -555,31 +555,31 @@ func (gui *Gui) refreshStateFiles() error {
selectedNode := gui.getSelectedFileNode()
- prevNodes := gui.State.FileManager.GetAllItems()
+ prevNodes := gui.State.FileTreeViewModel.GetAllItems()
prevSelectedLineIdx := gui.State.Panels.Files.SelectedLineIdx
files := gui.Git.Loaders.Files.
GetStatusFiles(loaders.GetStatusFileOptions{})
// for when you stage the old file of a rename and the new file is in a collapsed dir
- state.FileManager.RWMutex.Lock()
+ state.FileTreeViewModel.RWMutex.Lock()
for _, file := range files {
if selectedNode != nil && selectedNode.Path != "" && file.PreviousName == selectedNode.Path {
- state.FileManager.ExpandToPath(file.Name)
+ state.FileTreeViewModel.ExpandToPath(file.Name)
}
}
- state.FileManager.SetFiles(files)
- state.FileManager.RWMutex.Unlock()
+ state.FileTreeViewModel.SetFiles(files)
+ state.FileTreeViewModel.RWMutex.Unlock()
if err := gui.fileWatcher.addFilesToFileWatcher(files); err != nil {
return err
}
if selectedNode != nil {
- newIdx := gui.findNewSelectedIdx(prevNodes[prevSelectedLineIdx:], state.FileManager.GetAllItems())
+ newIdx := gui.findNewSelectedIdx(prevNodes[prevSelectedLineIdx:], state.FileTreeViewModel.GetAllItems())
if newIdx != -1 && newIdx != prevSelectedLineIdx {
- newNode := state.FileManager.GetItemAtIndex(newIdx)
+ newNode := state.FileTreeViewModel.GetItemAtIndex(newIdx)
// when not in tree mode, we show merge conflict files at the top, so you
// can work through them one by one without having to sift through a large
// set of files. If you have just fixed the merge conflicts of a file, we
@@ -588,7 +588,7 @@ func (gui *Gui) refreshStateFiles() error {
// conflicts: the user in this case would rather work on the next file
// with merge conflicts, which will have moved up to fill the gap left by
// the last file, meaning the cursor doesn't need to move at all.
- leaveCursor := !state.FileManager.InTreeMode() && newNode != nil &&
+ leaveCursor := !state.FileTreeViewModel.InTreeMode() && newNode != nil &&
selectedNode.File != nil && selectedNode.File.HasMergeConflicts &&
newNode.File != nil && !newNode.File.HasMergeConflicts
@@ -598,7 +598,7 @@ func (gui *Gui) refreshStateFiles() error {
}
}
- gui.refreshSelectedLine(state.Panels.Files, state.FileManager.GetItemsLength())
+ gui.refreshSelectedLine(state.Panels.Files, state.FileTreeViewModel.GetItemsLength())
return nil
}
@@ -871,7 +871,7 @@ func (gui *Gui) openFile(filename string) error {
}
func (gui *Gui) anyFilesWithMergeConflicts() bool {
- for _, file := range gui.State.FileManager.GetAllFiles() {
+ for _, file := range gui.State.FileTreeViewModel.GetAllFiles() {
if file.HasMergeConflicts {
return true
}
@@ -939,7 +939,7 @@ func (gui *Gui) handleToggleDirCollapsed() error {
return nil
}
- gui.State.FileManager.ToggleCollapsed(node.GetPath())
+ gui.State.FileTreeViewModel.ToggleCollapsed(node.GetPath())
if err := gui.postRefreshUpdate(gui.State.Contexts.Files); err != nil {
gui.Log.Error(err)
@@ -952,12 +952,12 @@ func (gui *Gui) handleToggleFileTreeView() error {
// get path of currently selected file
path := gui.getSelectedPath()
- gui.State.FileManager.ToggleShowTree()
+ gui.State.FileTreeViewModel.ToggleShowTree()
// find that same node in the new format and move the cursor to it
if path != "" {
- gui.State.FileManager.ExpandToPath(path)
- index, found := gui.State.FileManager.GetIndexForPath(path)
+ gui.State.FileTreeViewModel.ExpandToPath(path)
+ index, found := gui.State.FileTreeViewModel.GetIndexForPath(path)
if found {
gui.filesListContext().GetPanelState().SetSelectedLineIdx(index)
}
diff --git a/pkg/gui/filetree/README.md b/pkg/gui/filetree/README.md
new file mode 100644
index 000000000..d2d16ace6
--- /dev/null
+++ b/pkg/gui/filetree/README.md
@@ -0,0 +1,22 @@
+## FileTree Package
+
+This package handles the representation of file trees. There are two ways to render files: one is to render them flat, so something like this:
+
+```
+dir1/file1
+dir1/file2
+file3
+```
+
+And the other is to render them as a tree
+
+```
+dir1/
+ file1
+ file2
+file3
+```
+
+Internally we represent each of the above as a tree, but with the flat approach there's just a single root node and every path is a direct child of that root. Viewing in 'tree' mode (as opposed to 'flat' mode) allows for collapsing and expanding directories, and lets you perform actions on directories e.g. staging a whole directory. But it takes up more vertical space and sometimes you just want to have a flat view where you can go flick through your files one by one to see the diff.
+
+This package is not concerned about rendering the tree: only representing its internal state.
diff --git a/pkg/gui/filetree/commit_file_manager.go b/pkg/gui/filetree/commit_file_manager.go
deleted file mode 100644
index 852a67b09..000000000
--- a/pkg/gui/filetree/commit_file_manager.go
+++ /dev/null
@@ -1,118 +0,0 @@
-package filetree
-
-import (
- "github.com/jesseduffield/lazygit/pkg/commands/models"
- "github.com/jesseduffield/lazygit/pkg/commands/patch"
- "github.com/sirupsen/logrus"
-)
-
-type CommitFileManager struct {
- files []*models.CommitFile
- tree *CommitFileNode
- showTree bool
- log *logrus.Entry
- collapsedPaths CollapsedPaths
- // parent is the identifier of the parent object e.g. a commit SHA if this commit file is for a commit, or a stash entry ref like 'stash@{1}'
- parent string
-}
-
-func (m *CommitFileManager) GetParent() string {
- return m.parent
-}
-
-func NewCommitFileManager(files []*models.CommitFile, log *logrus.Entry, showTree bool) *CommitFileManager {
- return &CommitFileManager{
- files: files,
- log: log,
- showTree: showTree,
- collapsedPaths: CollapsedPaths{},
- }
-}
-
-func (m *CommitFileManager) ExpandToPath(path string) {
- m.collapsedPaths.ExpandToPath(path)
-}
-
-func (m *CommitFileManager) ToggleShowTree() {
- m.showTree = !m.showTree
- m.SetTree()
-}
-
-func (m *CommitFileManager) GetItemAtIndex(index int) *CommitFileNode {
- // need to traverse the three depth first until we get to the index.
- return m.tree.GetNodeAtIndex(index+1, m.collapsedPaths) // ignoring root
-}
-
-func (m *CommitFileManager) GetIndexForPath(path string) (int, bool) {
- index, found := m.tree.GetIndexForPath(path, m.collapsedPaths)
- return index - 1, found
-}
-
-func (m *CommitFileManager) GetAllItems() []*CommitFileNode {
- if m.tree == nil {
- return nil
- }
-
- return m.tree.Flatten(m.collapsedPaths)[1:] // ignoring root
-}
-
-func (m *CommitFileManager) GetItemsLength() int {
- return m.tree.Size(m.collapsedPaths) - 1 // ignoring root
-}
-
-func (m *CommitFileManager) GetAllFiles() []*models.CommitFile {
- return m.files
-}
-
-func (m *CommitFileManager) SetFiles(files []*models.CommitFile, parent string) {
- m.files = files
- m.parent = parent
-
- m.SetTree()
-}
-
-func (m *CommitFileManager) SetTree() {
- if m.showTree {
- m.tree = BuildTreeFromCommitFiles(m.files)
- } else {
- m.tree = BuildFlatTreeFromCommitFiles(m.files)
- }
-}
-
-func (m *CommitFileManager) IsCollapsed(path string) bool {
- return m.collapsedPaths.IsCollapsed(path)
-}
-
-func (m *CommitFileManager) ToggleCollapsed(path string) {
- m.collapsedPaths.ToggleCollapsed(path)
-}
-
-func (m *CommitFileManager) Render(diffName string, patchManager *patch.PatchManager) []string {
- // can't rely on renderAux to check for nil because an interface won't be nil if its concrete value is nil
- if m.tree == nil {
- return []string{}
- }
-
- return renderAux(m.tree, m.collapsedPaths, "", -1, func(n INode, depth int) string {
- castN := n.(*CommitFileNode)
-
- // This is a little convoluted because we're dealing with either a leaf or a non-leaf.
- // But this code actually applies to both. If it's a leaf, the status will just
- // be whatever status it is, but if it's a non-leaf it will determine its status
- // based on the leaves of that subtree
- var status patch.PatchStatus
- if castN.EveryFile(func(file *models.CommitFile) bool {
- return patchManager.GetFileStatus(file.Name, m.parent) == patch.WHOLE
- }) {
- status = patch.WHOLE
- } else if castN.EveryFile(func(file *models.CommitFile) bool {
- return patchManager.GetFileStatus(file.Name, m.parent) == patch.UNSELECTED
- }) {
- status = patch.UNSELECTED
- } else {
- status = patch.PART
- }
-
- return getCommitFileLine(castN.NameAtDepth(depth), diffName, castN.File, status)
- })
-}
diff --git a/pkg/gui/filetree/commit_file_node.go b/pkg/gui/filetree/commit_file_node.go
index 173281035..14960ee30 100644
--- a/pkg/gui/filetree/commit_file_node.go
+++ b/pkg/gui/filetree/commit_file_node.go
@@ -11,6 +11,8 @@ type CommitFileNode struct {
CompressionLevel int // equal to the number of forward slashes you'll see in the path when it's rendered in tree mode
}
+var _ INode = &CommitFileNode{}
+
// methods satisfying ListItem interface
func (s *CommitFileNode) ID() string {
@@ -23,6 +25,10 @@ func (s *CommitFileNode) Description() string {
// methods satisfying INode interface
+func (s *CommitFileNode) IsNil() bool {
+ return s == nil
+}
+
func (s *CommitFileNode) IsLeaf() bool {
return s.File != nil
}
@@ -139,13 +145,6 @@ func (s *CommitFileNode) Compress() {
compressAux(s)
}
-// This ignores the root
-func (node *CommitFileNode) GetPathsMatching(test func(*CommitFileNode) bool) []string {
- return getPathsMatching(node, func(n INode) bool {
- return test(n.(*CommitFileNode))
- })
-}
-
func (s *CommitFileNode) GetLeaves() []*CommitFileNode {
leaves := getLeaves(s)
castLeaves := make([]*CommitFileNode, len(leaves))
diff --git a/pkg/gui/filetree/commit_file_tree_view_model.go b/pkg/gui/filetree/commit_file_tree_view_model.go
new file mode 100644
index 000000000..301396462
--- /dev/null
+++ b/pkg/gui/filetree/commit_file_tree_view_model.go
@@ -0,0 +1,101 @@
+package filetree
+
+import (
+ "github.com/jesseduffield/lazygit/pkg/commands/models"
+ "github.com/sirupsen/logrus"
+)
+
+type CommitFileTreeViewModel struct {
+ files []*models.CommitFile
+ tree *CommitFileNode
+ showTree bool
+ log *logrus.Entry
+ collapsedPaths CollapsedPaths
+ // parent is the identifier of the parent object e.g. a commit SHA if this commit file is for a commit, or a stash entry ref like 'stash@{1}'
+ parent string
+}
+
+func (self *CommitFileTreeViewModel) GetParent() string {
+ return self.parent
+}
+
+func (self *CommitFileTreeViewModel) SetParent(parent string) {
+ self.parent = parent
+}
+
+func NewCommitFileTreeViewModel(files []*models.CommitFile, log *logrus.Entry, showTree bool) *CommitFileTreeViewModel {
+ viewModel := &CommitFileTreeViewModel{
+ log: log,
+ showTree: showTree,
+ collapsedPaths: CollapsedPaths{},
+ }
+
+ viewModel.SetFiles(files)
+
+ return viewModel
+}
+
+func (self *CommitFileTreeViewModel) ExpandToPath(path string) {
+ self.collapsedPaths.ExpandToPath(path)
+}
+
+func (self *CommitFileTreeViewModel) ToggleShowTree() {
+ self.showTree = !self.showTree
+ self.SetTree()
+}
+
+func (self *CommitFileTreeViewModel) GetItemAtIndex(index int) *CommitFileNode {
+ // need to traverse the three depth first until we get to the index.
+ return self.tree.GetNodeAtIndex(index+1, self.collapsedPaths) // ignoring root
+}
+
+func (self *CommitFileTreeViewModel) GetIndexForPath(path string) (int, bool) {
+ index, found := self.tree.GetIndexForPath(path, self.collapsedPaths)
+ return index - 1, found
+}
+
+func (self *CommitFileTreeViewModel) GetAllItems() []*CommitFileNode {
+ if self.tree == nil {
+ return nil
+ }
+
+ return self.tree.Flatten(self.collapsedPaths)[1:] // ignoring root
+}
+
+func (self *CommitFileTreeViewModel) GetItemsLength() int {
+ return self.tree.Size(self.collapsedPaths) - 1 // ignoring root
+}
+
+func (self *CommitFileTreeViewModel) GetAllFiles() []*models.CommitFile {
+ return self.files
+}
+
+func (self *CommitFileTreeViewModel) SetFiles(files []*models.CommitFile) {
+ self.files = files
+
+ self.SetTree()
+}
+
+func (self *CommitFileTreeViewModel) SetTree() {
+ if self.showTree {
+ self.tree = BuildTreeFromCommitFiles(self.files)
+ } else {
+ self.tree = BuildFlatTreeFromCommitFiles(self.files)
+ }
+}
+
+func (self *CommitFileTreeViewModel) IsCollapsed(path string) bool {
+ return self.collapsedPaths.IsCollapsed(path)
+}
+
+func (self *CommitFileTreeViewModel) ToggleCollapsed(path string) {
+ self.collapsedPaths.ToggleCollapsed(path)
+}
+
+func (self *CommitFileTreeViewModel) Tree() INode {
+ return self.tree
+}
+
+func (self *CommitFileTreeViewModel) CollapsedPaths() CollapsedPaths {
+ return self.collapsedPaths
+}
diff --git a/pkg/gui/filetree/constants.go b/pkg/gui/filetree/constants.go
deleted file mode 100644
index d510650e2..000000000
--- a/pkg/gui/filetree/constants.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package filetree
-
-const EXPANDED_ARROW = "▼"
-const COLLAPSED_ARROW = "►"
-
-const INNER_ITEM = "├─ "
-const LAST_ITEM = "└─ "
-const NESTED = "│ "
-const NOTHING = " "
diff --git a/pkg/gui/filetree/file_manager.go b/pkg/gui/filetree/file_manager.go
deleted file mode 100644
index b028ef961..000000000
--- a/pkg/gui/filetree/file_manager.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package filetree
-
-import (
- "sync"
-
- "github.com/jesseduffield/lazygit/pkg/commands/models"
- "github.com/sirupsen/logrus"
-)
-
-type FileManagerDisplayFilter int
-
-const (
- DisplayAll FileManagerDisplayFilter = iota
- DisplayStaged
- DisplayUnstaged
-)
-
-type FileManager struct {
- files []*models.File
- tree *FileNode
- showTree bool
- log *logrus.Entry
- filter FileManagerDisplayFilter
- collapsedPaths CollapsedPaths
- sync.RWMutex
-}
-
-func NewFileManager(files []*models.File, log *logrus.Entry, showTree bool) *FileManager {
- return &FileManager{
- files: files,
- log: log,
- showTree: showTree,
- filter: DisplayAll,
- collapsedPaths: CollapsedPaths{},
- RWMutex: sync.RWMutex{},
- }
-}
-
-func (m *FileManager) InTreeMode() bool {
- return m.showTree
-}
-
-func (m *FileManager) ExpandToPath(path string) {
- m.collapsedPaths.ExpandToPath(path)
-}
-
-func (m *FileManager) GetFilesForDisplay() []*models.File {
- files := m.files
- if m.filter == DisplayAll {
- return files
- }
-
- result := make([]*models.File, 0)
- if m.filter == DisplayStaged {
- for _, file := range files {
- if file.HasStagedChanges {
- result = append(result, file)
- }
- }
- } else {
- for _, file := range files {
- if !file.HasStagedChanges {
- result = append(result, file)
- }
- }
- }
-
- return result
-}
-
-func (m *FileManager) SetDisplayFilter(filter FileManagerDisplayFilter) {
- m.filter = filter
- m.SetTree()
-}
-
-func (m *FileManager) ToggleShowTree() {
- m.showTree = !m.showTree
- m.SetTree()
-}
-
-func (m *FileManager) GetItemAtIndex(index int) *FileNode {
- // need to traverse the three depth first until we get to the index.
- return m.tree.GetNodeAtIndex(index+1, m.collapsedPaths) // ignoring root
-}
-
-func (m *FileManager) GetIndexForPath(path string) (int, bool) {
- index, found := m.tree.GetIndexForPath(path, m.collapsedPaths)
- return index - 1, found
-}
-
-func (m *FileManager) GetAllItems() []*FileNode {
- if m.tree == nil {
- return nil
- }
-
- return m.tree.Flatten(m.collapsedPaths)[1:] // ignoring root
-}
-
-func (m *FileManager) GetItemsLength() int {
- return m.tree.Size(m.collapsedPaths) - 1 // ignoring root
-}
-
-func (m *FileManager) GetAllFiles() []*models.File {
- return m.files
-}
-
-func (m *FileManager) SetFiles(files []*models.File) {
- m.files = files
-
- m.SetTree()
-}
-
-func (m *FileManager) SetTree() {
- filesForDisplay := m.GetFilesForDisplay()
- if m.showTree {
- m.tree = BuildTreeFromFiles(filesForDisplay)
- } else {
- m.tree = BuildFlatTreeFromFiles(filesForDisplay)
- }
-}
-
-func (m *FileManager) IsCollapsed(path string) bool {
- return m.collapsedPaths.IsCollapsed(path)
-}
-
-func (m *FileManager) ToggleCollapsed(path string) {
- m.collapsedPaths.ToggleCollapsed(path)
-}
-
-func (m *FileManager) Render(diffName string, submoduleConfigs []*models.SubmoduleConfig) []string {
- // can't rely on renderAux to check for nil because an interface won't be nil if its concrete value is nil
- if m.tree == nil {
- return []string{}
- }
-
- return renderAux(m.tree, m.collapsedPaths, "", -1, func(n INode, depth int) string {
- castN := n.(*FileNode)
- return getFileLine(castN.GetHasUnstagedChanges(), castN.GetHasStagedChanges(), castN.NameAtDepth(depth), diffName, submoduleConfigs, castN.File)
- })
-}
diff --git a/pkg/gui/filetree/file_manager_test.go b/pkg/gui/filetree/file_manager_test.go
deleted file mode 100644
index 83ba5b086..000000000
--- a/pkg/gui/filetree/file_manager_test.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package filetree
-
-import (
- "testing"
-
- "github.com/gookit/color"
- "github.com/jesseduffield/lazygit/pkg/commands/models"
- "github.com/stretchr/testify/assert"
- "github.com/xo/terminfo"
-)
-
-func init() {
- color.ForceSetColorLevel(terminfo.ColorLevelNone)
-}
-
-func TestRender(t *testing.T) {
- scenarios := []struct {
- name string
- root *FileNode
- collapsedPaths map[string]bool
- expected []string
- }{
- {
- name: "nil node",
- root: nil,
- expected: []string{},