133 lines
2.6 KiB
Go
133 lines
2.6 KiB
Go
package dirtree
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type Tree struct {
|
|
Root *Node
|
|
ShowHidden bool
|
|
}
|
|
|
|
type Node struct {
|
|
tree *Tree
|
|
parent *Node
|
|
children []*Node
|
|
depth int
|
|
Path string
|
|
Name string
|
|
Expanded bool
|
|
isDir bool
|
|
selected bool
|
|
}
|
|
|
|
func NewTree(path string) *Tree {
|
|
tree := &Tree{
|
|
ShowHidden: false,
|
|
}
|
|
tree.Root = &Node{
|
|
tree: tree,
|
|
parent: nil,
|
|
Path: path,
|
|
Name: filepath.Base(path),
|
|
isDir: true,
|
|
Expanded: true,
|
|
}
|
|
return tree
|
|
}
|
|
|
|
func (node *Node) IsRoot() bool {
|
|
return node.tree.Root == node
|
|
}
|
|
|
|
func (node *Node) Parent() *Node {
|
|
return node.parent
|
|
}
|
|
|
|
func (node *Node) IsSelected() bool {
|
|
return node.selected
|
|
}
|
|
|
|
func (node *Node) IsDir() bool {
|
|
return node.isDir
|
|
}
|
|
|
|
func (node *Node) Select(recurse bool, maxDepth int) {
|
|
node.selected = true
|
|
if node.isDir && recurse {
|
|
node.LoadChildren(recurse, maxDepth, func (n *Node) {
|
|
n.selected = true
|
|
})
|
|
}
|
|
}
|
|
|
|
func (node *Node) Deselect(recurse bool) {
|
|
node.selected = false
|
|
if node.isDir && recurse {
|
|
for _, child := range node.children {
|
|
child.Deselect(recurse)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (node *Node) LoadChildren(recurse bool, maxDepth int, visitFunc func (node *Node)) error {
|
|
startDepth := node.depth
|
|
var loadChildren func(node *Node) error
|
|
loadChildren = func(node *Node) error {
|
|
// Load new entries
|
|
entries, err := os.ReadDir(node.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Save current children before loading
|
|
oldChildren := make(map[string]*Node)
|
|
for _, child := range node.children {
|
|
oldChildren[child.Path] = child
|
|
}
|
|
|
|
node.children = make([]*Node, 0)
|
|
for _, entry := range entries {
|
|
if !node.tree.ShowHidden && strings.HasPrefix(entry.Name(), ".") {
|
|
continue
|
|
}
|
|
|
|
child := &Node{
|
|
tree: node.tree,
|
|
parent: node,
|
|
depth: node.depth + 1,
|
|
Name: entry.Name(),
|
|
isDir: entry.IsDir(),
|
|
Path: filepath.Join(node.Path, entry.Name()),
|
|
}
|
|
|
|
oldChild, exists := oldChildren[child.Path]
|
|
if child.isDir {
|
|
// Preserve child's grandchildren and expanded/selected state
|
|
if exists && (oldChild.Expanded || len(oldChild.children) > 0) {
|
|
child.Expanded = oldChild.Expanded
|
|
child.selected = oldChild.selected
|
|
child.children = oldChild.children
|
|
for i := range child.children {
|
|
child.children[i].parent = child
|
|
}
|
|
loadChildren(child)
|
|
} else if recurse && (maxDepth <= 0 || node.depth - startDepth < maxDepth) {
|
|
loadChildren(child)
|
|
}
|
|
} else if exists {
|
|
child.selected = oldChild.selected
|
|
}
|
|
|
|
node.children = append(node.children, child)
|
|
if visitFunc != nil {
|
|
visitFunc(child)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
return loadChildren(node)
|
|
}
|