diff --git a/pkg/commands/files.go b/pkg/commands/files.go index 82d3ae244..328853a79 100644 --- a/pkg/commands/files.go +++ b/pkg/commands/files.go @@ -9,6 +9,7 @@ import ( "github.com/go-errors/errors" "github.com/jesseduffield/lazygit/pkg/commands/models" + "github.com/jesseduffield/lazygit/pkg/gui/filetree" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/mgutz/str" ) @@ -137,12 +138,12 @@ func (c *GitCommand) DiscardAllFileChanges(file *models.File) error { return c.DiscardUnstagedFileChanges(file) } -func (c *GitCommand) DiscardAllDirChanges(node *models.FileChangeNode) error { +func (c *GitCommand) DiscardAllDirChanges(node *filetree.FileChangeNode) error { // this could be more efficient but we would need to handle all the edge cases return node.ForEachFile(c.DiscardAllFileChanges) } -func (c *GitCommand) DiscardUnstagedDirChanges(node *models.FileChangeNode) error { +func (c *GitCommand) DiscardUnstagedDirChanges(node *filetree.FileChangeNode) error { if err := c.RemoveUntrackedDirFiles(node); err != nil { return err } @@ -155,9 +156,9 @@ func (c *GitCommand) DiscardUnstagedDirChanges(node *models.FileChangeNode) erro return nil } -func (c *GitCommand) RemoveUntrackedDirFiles(node *models.FileChangeNode) error { +func (c *GitCommand) RemoveUntrackedDirFiles(node *filetree.FileChangeNode) error { untrackedFilePaths := node.GetPathsMatching( - func(n *models.FileChangeNode) bool { return n.File != nil && !n.File.GetIsTracked() }, + func(n *filetree.FileChangeNode) bool { return n.File != nil && !n.File.GetIsTracked() }, ) for _, path := range untrackedFilePaths { diff --git a/pkg/commands/models/file.go b/pkg/commands/models/file.go index 3e28ca46f..a02beae63 100644 --- a/pkg/commands/models/file.go +++ b/pkg/commands/models/file.go @@ -21,6 +21,14 @@ type File struct { ShortStatus string // e.g. 'AD', ' A', 'M ', '??' } +// sometimes we need to deal with either a node (which contains a file) or an actual file +type IFileChange interface { + GetHasUnstagedChanges() bool + GetHasStagedChanges() bool + GetIsTracked() bool + GetPath() string +} + const RENAME_SEPARATOR = " -> " func (f *File) IsRename() bool { diff --git a/pkg/commands/models/file_change_interface.go b/pkg/commands/models/file_change_interface.go deleted file mode 100644 index 76d313d1a..000000000 --- a/pkg/commands/models/file_change_interface.go +++ /dev/null @@ -1,9 +0,0 @@ -package models - -// sometimes we need to deal with either a node (which contains a file) or an actual file -type IFileChange interface { - GetHasUnstagedChanges() bool - GetHasStagedChanges() bool - GetIsTracked() bool - GetPath() string -} diff --git a/pkg/gui/files_panel.go b/pkg/gui/files_panel.go index f1fbc4325..5c0ef9c64 100644 --- a/pkg/gui/files_panel.go +++ b/pkg/gui/files_panel.go @@ -15,13 +15,14 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands" "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/gui/filetree" "github.com/jesseduffield/lazygit/pkg/utils" "github.com/mgutz/str" ) // list panel functions -func (gui *Gui) getSelectedFileChangeNode() *models.FileChangeNode { +func (gui *Gui) getSelectedFileChangeNode() *filetree.FileChangeNode { selectedLine := gui.State.Panels.Files.SelectedLineIdx if selectedLine == -1 { return nil @@ -557,8 +558,8 @@ func (gui *Gui) refreshStateFiles() error { // nodes until we find one that exists in the new set of nodes, then move the cursor // to that. // prevNodes starts from our previously selected node because we don't need to consider anything above that -func (gui *Gui) findNewSelectedIdx(prevNodes []*models.FileChangeNode, currNodes []*models.FileChangeNode) int { - getPaths := func(node *models.FileChangeNode) []string { +func (gui *Gui) findNewSelectedIdx(prevNodes []*filetree.FileChangeNode, currNodes []*filetree.FileChangeNode) int { + getPaths := func(node *filetree.FileChangeNode) []string { if node == nil { return nil } diff --git a/pkg/gui/filetree/build_tree.go b/pkg/gui/filetree/build_tree.go index e8da844e0..267bb22dd 100644 --- a/pkg/gui/filetree/build_tree.go +++ b/pkg/gui/filetree/build_tree.go @@ -9,10 +9,10 @@ import ( "github.com/jesseduffield/lazygit/pkg/commands/models" ) -func BuildTreeFromFiles(files []*models.File) *models.FileChangeNode { - root := &models.FileChangeNode{} +func BuildTreeFromFiles(files []*models.File) *FileChangeNode { + root := &FileChangeNode{} - var curr *models.FileChangeNode + var curr *FileChangeNode for _, file := range files { split := strings.Split(file.Name, string(os.PathSeparator)) curr = root @@ -33,7 +33,7 @@ func BuildTreeFromFiles(files []*models.File) *models.FileChangeNode { } } - newChild := &models.FileChangeNode{ + newChild := &FileChangeNode{ Path: path, File: setFile, } @@ -49,7 +49,7 @@ func BuildTreeFromFiles(files []*models.File) *models.FileChangeNode { return root } -func BuildFlatTreeFromFiles(files []*models.File) *models.FileChangeNode { +func BuildFlatTreeFromFiles(files []*models.File) *FileChangeNode { rootAux := BuildTreeFromFiles(files) sortedFiles := rootAux.GetLeaves() @@ -59,5 +59,5 @@ func BuildFlatTreeFromFiles(files []*models.File) *models.FileChangeNode { return sortedFiles[i].File != nil && sortedFiles[i].File.HasMergeConflicts && !(sortedFiles[j].File != nil && sortedFiles[j].File.HasMergeConflicts) }) - return &models.FileChangeNode{Children: sortedFiles} + return &FileChangeNode{Children: sortedFiles} } diff --git a/pkg/gui/filetree/file_change_manager.go b/pkg/gui/filetree/file_change_manager.go index 09e2e7316..fa68458af 100644 --- a/pkg/gui/filetree/file_change_manager.go +++ b/pkg/gui/filetree/file_change_manager.go @@ -19,7 +19,7 @@ const NOTHING = " " type FileChangeManager struct { files []*models.File - tree *models.FileChangeNode + tree *FileChangeNode showTree bool log *logrus.Entry collapsedPaths map[string]bool @@ -39,7 +39,7 @@ func (m *FileChangeManager) ToggleShowTree() { m.SetTree() } -func (m *FileChangeManager) GetItemAtIndex(index int) *models.FileChangeNode { +func (m *FileChangeManager) GetItemAtIndex(index int) *FileChangeNode { // need to traverse the three depth first until we get to the index. return m.tree.GetNodeAtIndex(index+1, m.collapsedPaths) // ignoring root } @@ -49,7 +49,7 @@ func (m *FileChangeManager) GetIndexForPath(path string) (int, bool) { return index - 1, found } -func (m *FileChangeManager) GetAllItems() []*models.FileChangeNode { +func (m *FileChangeManager) GetAllItems() []*FileChangeNode { if m.tree == nil { return nil } @@ -83,15 +83,15 @@ func (m *FileChangeManager) Render(diffName string, submoduleConfigs []*models.S return m.renderAux(m.tree, "", -1, diffName, submoduleConfigs) } -func (m *FileChangeManager) IsCollapsed(s *models.FileChangeNode) bool { +func (m *FileChangeManager) IsCollapsed(s *FileChangeNode) bool { return m.collapsedPaths[s.GetPath()] } -func (m *FileChangeManager) ToggleCollapsed(s *models.FileChangeNode) { +func (m *FileChangeManager) ToggleCollapsed(s *FileChangeNode) { m.collapsedPaths[s.GetPath()] = !m.collapsedPaths[s.GetPath()] } -func (m *FileChangeManager) renderAux(s *models.FileChangeNode, prefix string, depth int, diffName string, submoduleConfigs []*models.SubmoduleConfig) []string { +func (m *FileChangeManager) renderAux(s *FileChangeNode, prefix string, depth int, diffName string, submoduleConfigs []*models.SubmoduleConfig) []string { isRoot := depth == -1 if s == nil { return []string{} diff --git a/pkg/gui/filetree/file_change_manager_test.go b/pkg/gui/filetree/file_change_manager_test.go index be59aeccd..fb9022fec 100644 --- a/pkg/gui/filetree/file_change_manager_test.go +++ b/pkg/gui/filetree/file_change_manager_test.go @@ -10,7 +10,7 @@ import ( func TestRender(t *testing.T) { scenarios := []struct { name string - root *models.FileChangeNode + root *FileChangeNode collapsedPaths map[string]bool expected []string }{ @@ -21,9 +21,9 @@ func TestRender(t *testing.T) { }, { name: "leaf node", - root: &models.FileChangeNode{ + root: &FileChangeNode{ Path: "", - Children: []*models.FileChangeNode{ + Children: []*FileChangeNode{ {File: &models.File{Name: "test", ShortStatus: " M", HasStagedChanges: true}, Path: "test"}, }, }, @@ -31,12 +31,12 @@ func TestRender(t *testing.T) { }, { name: "big example", - root: &models.FileChangeNode{ + root: &FileChangeNode{ Path: "", - Children: []*models.FileChangeNode{ + Children: []*FileChangeNode{ { Path: "dir1", - Children: []*models.FileChangeNode{ + Children: []*FileChangeNode{ { File: &models.File{Name: "dir1/file2", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "dir1/file2", @@ -49,10 +49,10 @@ func TestRender(t *testing.T) { }, { Path: "dir2", - Children: []*models.FileChangeNode{ + Children: []*FileChangeNode{ { Path: "dir2/dir2", - Children: []*models.FileChangeNode{ + Children: []*FileChangeNode{ { File: &models.File{Name: "dir2/dir2/file3", ShortStatus: " M", HasStagedChanges: true}, Path: "dir2/dir2/file3", diff --git a/pkg/commands/models/file_change_node.go b/pkg/gui/filetree/file_change_node.go similarity index 91% rename from pkg/commands/models/file_change_node.go rename to pkg/gui/filetree/file_change_node.go index c70db2dfa..240525cba 100644 --- a/pkg/commands/models/file_change_node.go +++ b/pkg/gui/filetree/file_change_node.go @@ -1,4 +1,4 @@ -package models +package filetree import ( "fmt" @@ -6,28 +6,30 @@ import ( "path/filepath" "sort" "strings" + + "github.com/jesseduffield/lazygit/pkg/commands/models" ) type FileChangeNode struct { Children []*FileChangeNode - File *File + File *models.File Path string // e.g. '/path/to/mydir' CompressionLevel int // equal to the number of forward slashes you'll see in the path when it's rendered in tree mode } func (s *FileChangeNode) GetHasUnstagedChanges() bool { - return s.AnyFile(func(file *File) bool { return file.HasUnstagedChanges }) + return s.AnyFile(func(file *models.File) bool { return file.HasUnstagedChanges }) } func (s *FileChangeNode) GetHasStagedChanges() bool { - return s.AnyFile(func(file *File) bool { return file.HasStagedChanges }) + return s.AnyFile(func(file *models.File) bool { return file.HasStagedChanges }) } func (s *FileChangeNode) GetHasInlineMergeConflicts() bool { - return s.AnyFile(func(file *File) bool { return file.HasInlineMergeConflicts }) + return s.AnyFile(func(file *models.File) bool { return file.HasInlineMergeConflicts }) } -func (s *FileChangeNode) AnyFile(test func(file *File) bool) bool { +func (s *FileChangeNode) AnyFile(test func(file *models.File) bool) bool { return s.Any(func(node *FileChangeNode) bool { return node.IsLeaf() && test(node.File) }) @@ -39,7 +41,7 @@ func (s *FileChangeNode) Any(test func(node *FileChangeNode) bool) bool { } for _, child := range s.Children { - if test(child) { + if child.Any(test) { return true } } @@ -231,7 +233,7 @@ func (s *FileChangeNode) Description() string { return s.GetPath() } -func (s *FileChangeNode) ForEachFile(cb func(*File) error) error { +func (s *FileChangeNode) ForEachFile(cb func(*models.File) error) error { if s.File != nil { if err := cb(s.File); err != nil { return err diff --git a/pkg/commands/models/file_change_node_test.go b/pkg/gui/filetree/file_change_node_test.go similarity index 60% rename from pkg/commands/models/file_change_node_test.go rename to pkg/gui/filetree/file_change_node_test.go index e4ec59edf..dbb21a6cd 100644 --- a/pkg/commands/models/file_change_node_test.go +++ b/pkg/gui/filetree/file_change_node_test.go @@ -1,8 +1,9 @@ -package models +package filetree import ( "testing" + "github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/stretchr/testify/assert" ) @@ -22,13 +23,13 @@ func TestCompress(t *testing.T) { root: &FileChangeNode{ Path: "", Children: []*FileChangeNode{ - {File: &File{Name: "test", ShortStatus: " M", HasStagedChanges: true}, Path: "test"}, + {File: &models.File{Name: "test", ShortStatus: " M", HasStagedChanges: true}, Path: "test"}, }, }, expected: &FileChangeNode{ Path: "", Children: []*FileChangeNode{ - {File: &File{Name: "test", ShortStatus: " M", HasStagedChanges: true}, Path: "test"}, + {File: &models.File{Name: "test", ShortStatus: " M", HasStagedChanges: true}, Path: "test"}, }, }, }, @@ -41,7 +42,7 @@ func TestCompress(t *testing.T) { Path: "dir1", Children: []*FileChangeNode{ { - File: &File{Name: "file2", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file2", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "dir1/file2", }, }, @@ -50,11 +51,11 @@ func TestCompress(t *testing.T) { Path: "dir2", Children: []*FileChangeNode{ { - File: &File{Name: "file3", ShortStatus: " M", HasStagedChanges: true}, + File: &models.File{Name: "file3", ShortStatus: " M", HasStagedChanges: true}, Path: "dir2/file3", }, { - File: &File{Name: "file4", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file4", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "dir2/file4", }, }, @@ -66,7 +67,7 @@ func TestCompress(t *testing.T) { Path: "dir3/dir3-1", Children: []*FileChangeNode{ { - File: &File{Name: "file5", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file5", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "dir3/dir3-1/file5", }, }, @@ -74,7 +75,7 @@ func TestCompress(t *testing.T) { }, }, { - File: &File{Name: "file1", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file1", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "file1", }, }, @@ -84,29 +85,29 @@ func TestCompress(t *testing.T) { Children: []*FileChangeNode{ { Path: "dir1/file2", - File: &File{Name: "file2", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file2", ShortStatus: "M ", HasUnstagedChanges: true}, CompressionLevel: 1, }, { Path: "dir2", Children: []*FileChangeNode{ { - File: &File{Name: "file3", ShortStatus: " M", HasStagedChanges: true}, + File: &models.File{Name: "file3", ShortStatus: " M", HasStagedChanges: true}, Path: "dir2/file3", }, { - File: &File{Name: "file4", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file4", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "dir2/file4", }, }, }, { Path: "dir3/dir3-1/file5", - File: &File{Name: "file5", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file5", ShortStatus: "M ", HasUnstagedChanges: true}, CompressionLevel: 2, }, { - File: &File{Name: "file1", ShortStatus: "M ", HasUnstagedChanges: true}, + File: &models.File{Name: "file1", ShortStatus: "M ", HasUnstagedChanges: true}, Path: "file1", }, },