1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-14 11:23:09 +02:00
lazygit/pkg/gui/filetree/inode.go
2021-04-02 11:00:15 +11:00

263 lines
5.2 KiB
Go

package filetree
import (
"fmt"
"sort"
"strings"
)
type INode interface {
IsLeaf() bool
GetPath() string
GetChildren() []INode
SetChildren([]INode)
GetCompressionLevel() int
SetCompressionLevel(int)
}
func sortNode(node INode) {
sortChildren(node)
for _, child := range node.GetChildren() {
sortNode(child)
}
}
func sortChildren(node INode) {
if node.IsLeaf() {
return
}
children := node.GetChildren()
sortedChildren := make([]INode, len(children))
copy(sortedChildren, children)
sort.Slice(sortedChildren, func(i, j int) bool {
if !sortedChildren[i].IsLeaf() && sortedChildren[j].IsLeaf() {
return true
}
if sortedChildren[i].IsLeaf() && !sortedChildren[j].IsLeaf() {
return false
}
return sortedChildren[i].GetPath() < sortedChildren[j].GetPath()
})
// TODO: think about making this in-place
node.SetChildren(sortedChildren)
}
func forEachLeaf(node INode, cb func(INode) error) error {
if node.IsLeaf() {
if err := cb(node); err != nil {
return err
}
}
for _, child := range node.GetChildren() {
if err := forEachLeaf(child, cb); err != nil {
return err
}
}
return nil
}
func any(node INode, test func(INode) bool) bool {
if test(node) {
return true
}
for _, child := range node.GetChildren() {
if any(child, test) {
return true
}
}
return false
}
func every(node INode, test func(INode) bool) bool {
if !test(node) {
return false
}
for _, child := range node.GetChildren() {
if !every(child, test) {
return false
}
}
return true
}
func flatten(node INode, collapsedPaths map[string]bool) []INode {
result := []INode{}
result = append(result, node)
if !collapsedPaths[node.GetPath()] {
for _, child := range node.GetChildren() {
result = append(result, flatten(child, collapsedPaths)...)
}
}
return result
}
func getNodeAtIndex(node INode, index int, collapsedPaths map[string]bool) INode {
foundNode, _ := getNodeAtIndexAux(node, index, collapsedPaths)
return foundNode
}
func getNodeAtIndexAux(node INode, index int, collapsedPaths map[string]bool) (INode, int) {
offset := 1
if index == 0 {
return node, offset
}
if !collapsedPaths[node.GetPath()] {
for _, child := range node.GetChildren() {
foundNode, offsetChange := getNodeAtIndexAux(child, index-offset, collapsedPaths)
offset += offsetChange
if foundNode != nil {
return foundNode, offset
}
}
}
return nil, offset
}
func getIndexForPath(node INode, path string, collapsedPaths map[string]bool) (int, bool) {
offset := 0
if node.GetPath() == path {
return offset, true
}
if !collapsedPaths[node.GetPath()] {
for _, child := range node.GetChildren() {
offsetChange, found := getIndexForPath(child, path, collapsedPaths)
offset += offsetChange + 1
if found {
return offset, true
}
}
}
return offset, false
}
func size(node INode, collapsedPaths map[string]bool) int {
output := 1
if !collapsedPaths[node.GetPath()] {
for _, child := range node.GetChildren() {
output += size(child, collapsedPaths)
}
}
return output
}
func compressAux(node INode) INode {
if node.IsLeaf() {
return node
}
children := node.GetChildren()
for i := range children {
grandchildren := children[i].GetChildren()
for len(grandchildren) == 1 {
grandchildren[0].SetCompressionLevel(children[i].GetCompressionLevel() + 1)
children[i] = grandchildren[0]
grandchildren = children[i].GetChildren()
}
}
for i := range children {
children[i] = compressAux(children[i])
}
node.SetChildren(children)
return node
}
func getPathsMatching(node INode, test func(INode) bool) []string {
paths := []string{}
if test(node) {
paths = append(paths, node.GetPath())
}
for _, child := range node.GetChildren() {
paths = append(paths, getPathsMatching(child, test)...)
}
return paths
}
func getLeaves(node INode) []INode {
if node.IsLeaf() {
return []INode{node}
}
output := []INode{}
for _, child := range node.GetChildren() {
output = append(output, getLeaves(child)...)
}
return output
}
func renderAux(s INode, collapsedPaths CollapsedPaths, prefix string, depth int, renderLine func(INode, int) string) []string {
isRoot := depth == -1
renderLineWithPrefix := func() string {
return prefix + renderLine(s, depth)
}
if s.IsLeaf() {
if isRoot {
return []string{}
}
return []string{renderLineWithPrefix()}
}
if collapsedPaths.IsCollapsed(s.GetPath()) {
return []string{fmt.Sprintf("%s %s", renderLineWithPrefix(), COLLAPSED_ARROW)}
}
arr := []string{}
if !isRoot {
arr = append(arr, fmt.Sprintf("%s %s", renderLineWithPrefix(), EXPANDED_ARROW))
}
newPrefix := prefix
if strings.HasSuffix(prefix, LAST_ITEM) {
newPrefix = strings.TrimSuffix(prefix, LAST_ITEM) + NOTHING
} else if strings.HasSuffix(prefix, INNER_ITEM) {
newPrefix = strings.TrimSuffix(prefix, INNER_ITEM) + NESTED
}
for i, child := range s.GetChildren() {
isLast := i == len(s.GetChildren())-1
var childPrefix string
if isRoot {
childPrefix = newPrefix
} else if isLast {
childPrefix = newPrefix + LAST_ITEM
} else {
childPrefix = newPrefix + INNER_ITEM
}
arr = append(arr, renderAux(child, collapsedPaths, childPrefix, depth+1+s.GetCompressionLevel(), renderLine)...)
}
return arr
}