mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-12-14 11:23:09 +02:00
263 lines
5.2 KiB
Go
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
|
|
}
|