diff options
Diffstat (limited to 'pkg/gui/filetree/file_node.go')
-rw-r--r-- | pkg/gui/filetree/file_node.go | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/pkg/gui/filetree/file_node.go b/pkg/gui/filetree/file_node.go new file mode 100644 index 000000000..cf3e69aa8 --- /dev/null +++ b/pkg/gui/filetree/file_node.go @@ -0,0 +1,178 @@ +package filetree + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/jesseduffield/lazygit/pkg/commands/models" +) + +type FileNode struct { + Children []*FileNode + 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 +} + +// methods satisfying ListItem interface + +func (s *FileNode) ID() string { + return s.GetPath() +} + +func (s *FileNode) Description() string { + return s.GetPath() +} + +// methods satisfying INode interface + +func (s *FileNode) IsLeaf() bool { + return s.File != nil +} + +func (s *FileNode) GetPath() string { + return s.Path +} + +func (s *FileNode) GetChildren() []INode { + result := make([]INode, len(s.Children)) + for i, child := range s.Children { + result[i] = child + } + + return result +} + +func (s *FileNode) SetChildren(children []INode) { + castChildren := make([]*FileNode, len(children)) + for i, child := range children { + castChildren[i] = child.(*FileNode) + } + + s.Children = castChildren +} + +func (s *FileNode) GetCompressionLevel() int { + return s.CompressionLevel +} + +func (s *FileNode) SetCompressionLevel(level int) { + s.CompressionLevel = level +} + +// methods utilising generic functions for INodes + +func (s *FileNode) Sort() { + sortNode(s) +} + +func (s *FileNode) ForEachFile(cb func(*models.File) error) error { + return forEachLeaf(s, func(n INode) error { + castNode := n.(*FileNode) + return cb(castNode.File) + }) +} + +func (s *FileNode) Any(test func(node *FileNode) bool) bool { + return any(s, func(n INode) bool { + castNode := n.(*FileNode) + return test(castNode) + }) +} + +func (n *FileNode) Flatten(collapsedPaths map[string]bool) []*FileNode { + results := flatten(n, collapsedPaths) + nodes := make([]*FileNode, len(results)) + for i, result := range results { + nodes[i] = result.(*FileNode) + } + + return nodes +} + +func (node *FileNode) GetNodeAtIndex(index int, collapsedPaths map[string]bool) *FileNode { + return getNodeAtIndex(node, index, collapsedPaths).(*FileNode) +} + +func (node *FileNode) GetIndexForPath(path string, collapsedPaths map[string]bool) (int, bool) { + return getIndexForPath(node, path, collapsedPaths) +} + +func (node *FileNode) Size(collapsedPaths map[string]bool) int { + return size(node, collapsedPaths) +} + +func (s *FileNode) Compress() { + // with these functions I try to only have type conversion code on the actual struct, + // but comparing interface values to nil is fraught with danger so I'm duplicating + // that code here. + if s == nil { + return + } + + compressAux(s) +} + +// This ignores the root +func (node *FileNode) GetPathsMatching(test func(*FileNode) bool) []string { + return getPathsMatching(node, func(n INode) bool { + return test(n.(*FileNode)) + }) +} + +func (s *FileNode) GetLeaves() []*FileNode { + leaves := getLeaves(s) + castLeaves := make([]*FileNode, len(leaves)) + for i := range leaves { + castLeaves[i] = leaves[i].(*FileNode) + } + + return castLeaves +} + +// extra methods + +func (s *FileNode) GetHasUnstagedChanges() bool { + return s.AnyFile(func(file *models.File) bool { return file.HasUnstagedChanges }) +} + +func (s *FileNode) GetHasStagedChanges() bool { + return s.AnyFile(func(file *models.File) bool { return file.HasStagedChanges }) +} + +func (s *FileNode) GetHasInlineMergeConflicts() bool { + return s.AnyFile(func(file *models.File) bool { return file.HasInlineMergeConflicts }) +} + +func (s *FileNode) GetIsTracked() bool { + return s.AnyFile(func(file *models.File) bool { return file.Tracked }) +} + +func (s *FileNode) AnyFile(test func(file *models.File) bool) bool { + return s.Any(func(node *FileNode) bool { + return node.IsLeaf() && test(node.File) + }) +} + +func (s *FileNode) NameAtDepth(depth int) string { + splitName := strings.Split(s.Path, string(os.PathSeparator)) + name := filepath.Join(splitName[depth:]...) + + if s.File != nil && s.File.IsRename() { + splitPrevName := strings.Split(s.File.PreviousName, string(os.PathSeparator)) + + prevName := s.File.PreviousName + // if the file has just been renamed inside the same directory, we can shave off + // the prefix for the previous path too. Otherwise we'll keep it unchanged + sameParentDir := len(splitName) == len(splitPrevName) && filepath.Join(splitName[0:depth]...) == filepath.Join(splitPrevName[0:depth]...) + if sameParentDir { + prevName = filepath.Join(splitPrevName[depth:]...) + } + + return fmt.Sprintf("%s%s%s", prevName, " → ", name) + } + + return name +} |