dont traverse the tree root node

This commit is contained in:
Alex Goodman 2018-06-20 17:07:13 -04:00
parent 598d95f5f7
commit fd397ac932
No known key found for this signature in database
GPG Key ID: 05328C611D8A520E
4 changed files with 123 additions and 48 deletions

View File

@ -5,6 +5,7 @@ import (
"strings" "strings"
"github.com/fatih/color" "github.com/fatih/color"
"fmt"
) )
type FileNode struct { type FileNode struct {
@ -30,10 +31,12 @@ func NewNode(parent *FileNode, name string, data *FileInfo) (node *FileNode) {
return node return node
} }
func (node *FileNode) Copy() *FileNode { func (node *FileNode) Copy(parent *FileNode) *FileNode {
newNode := NewNode(node.Parent, node.Name, node.Data.FileInfo) newNode := NewNode(parent, node.Name, node.Data.FileInfo)
newNode.Data.ViewInfo = node.Data.ViewInfo
newNode.Data.DiffType = node.Data.DiffType
for name, child := range node.Children { for name, child := range node.Children {
newNode.Children[name] = child.Copy() newNode.Children[name] = child.Copy(newNode)
child.Parent = newNode child.Parent = newNode
} }
return newNode return newNode
@ -52,6 +55,9 @@ func (node *FileNode) AddChild(name string, data *FileInfo) (child *FileNode) {
} }
func (node *FileNode) Remove() error { func (node *FileNode) Remove() error {
if node == node.Tree.Root {
return fmt.Errorf("cannot remove the tree root")
}
for _, child := range node.Children { for _, child := range node.Children {
child.Remove() child.Remove()
} }
@ -87,24 +93,37 @@ func (node *FileNode) VisitDepthChildFirst(visiter Visiter, evaluator VisitEvalu
} }
sort.Strings(keys) sort.Strings(keys)
for _, name := range keys { for _, name := range keys {
if evaluator != nil {
if !evaluator(node) {
continue
}
}
child := node.Children[name] child := node.Children[name]
err := child.VisitDepthChildFirst(visiter, evaluator) err := child.VisitDepthChildFirst(visiter, evaluator)
if err != nil { if err != nil {
return err return err
} }
} }
return visiter(node) // never visit the root node
if node == node.Tree.Root {
return nil
} else if evaluator != nil && evaluator(node) || evaluator == nil {
return visiter(node)
}
return nil
} }
func (node *FileNode) VisitDepthParentFirst(visiter Visiter, evaluator VisitEvaluator) error { func (node *FileNode) VisitDepthParentFirst(visiter Visiter, evaluator VisitEvaluator) error {
err := visiter(node) var err error
if err != nil {
return err doVisit := evaluator != nil && evaluator(node) || evaluator == nil
if !doVisit {
return nil
}
// never visit the root node
if node != node.Tree.Root{
err = visiter(node)
if err != nil {
return err
}
} }
var keys []string var keys []string
@ -113,11 +132,6 @@ func (node *FileNode) VisitDepthParentFirst(visiter Visiter, evaluator VisitEval
} }
sort.Strings(keys) sort.Strings(keys)
for _, name := range keys { for _, name := range keys {
if evaluator != nil {
if !evaluator(node) {
continue
}
}
child := node.Children[name] child := node.Children[name]
err = child.VisitDepthParentFirst(visiter, evaluator) err = child.VisitDepthParentFirst(visiter, evaluator)
if err != nil { if err != nil {

View File

@ -1,7 +1,6 @@
package filetree package filetree
import ( import (
"errors"
"fmt" "fmt"
"sort" "sort"
"strings" "strings"
@ -88,8 +87,10 @@ func (tree *FileTree) String() string {
func (tree *FileTree) Copy() *FileTree { func (tree *FileTree) Copy() *FileTree {
newTree := NewFileTree() newTree := NewFileTree()
*newTree = *tree newTree.Size = tree.Size
newTree.Root = tree.Root.Copy() newTree.Root = tree.Root.Copy(newTree.Root)
// update the tree pointers
newTree.VisitDepthChildFirst(func(node *FileNode) error { newTree.VisitDepthChildFirst(func(node *FileNode) error {
node.Tree = newTree node.Tree = newTree
return nil return nil
@ -116,12 +117,12 @@ func (tree *FileTree) Stack(upper *FileTree) error {
if node.IsWhiteout() { if node.IsWhiteout() {
err := tree.RemovePath(node.Path()) err := tree.RemovePath(node.Path())
if err != nil { if err != nil {
return fmt.Errorf("Cannot remove node %s: %v", node.Path(), err.Error()) return fmt.Errorf("cannot remove node %s: %v", node.Path(), err.Error())
} }
} else { } else {
newNode, err := tree.AddPath(node.Path(), node.Data.FileInfo) newNode, err := tree.AddPath(node.Path(), node.Data.FileInfo)
if err != nil { if err != nil {
return fmt.Errorf("Cannot add node %s: %v", newNode.Path(), err.Error()) return fmt.Errorf("cannot add node %s: %v", newNode.Path(), err.Error())
} }
} }
return nil return nil
@ -137,7 +138,7 @@ func (tree *FileTree) GetNode(path string) (*FileNode, error) {
continue continue
} }
if node.Children[name] == nil { if node.Children[name] == nil {
return nil, errors.New("Path does not exist") return nil, fmt.Errorf("path does not exist: %s", path)
} }
node = node.Children[name] node = node.Children[name]
} }
@ -183,7 +184,7 @@ func (tree *FileTree) Compare(upper *FileTree) error {
if upperNode.IsWhiteout() { if upperNode.IsWhiteout() {
err := tree.MarkRemoved(upperNode.Path()) err := tree.MarkRemoved(upperNode.Path())
if err != nil { if err != nil {
return fmt.Errorf("Cannot remove upperNode %s: %v", upperNode.Path(), err.Error()) return fmt.Errorf("cannot remove upperNode %s: %v", upperNode.Path(), err.Error())
} }
} else { } else {
lowerNode, _ := tree.GetNode(upperNode.Path()) lowerNode, _ := tree.GetNode(upperNode.Path())
@ -191,7 +192,7 @@ func (tree *FileTree) Compare(upper *FileTree) error {
newNode, err := tree.AddPath(upperNode.Path(), upperNode.Data.FileInfo) newNode, err := tree.AddPath(upperNode.Path(), upperNode.Data.FileInfo)
// fmt.Printf("added new upperNode at %s\n", newNode.Path()) // fmt.Printf("added new upperNode at %s\n", newNode.Path())
if err != nil { if err != nil {
return fmt.Errorf("Cannot add new upperNode %s: %v", upperNode.Path(), err.Error()) return fmt.Errorf("cannot add new upperNode %s: %v", upperNode.Path(), err.Error())
} }
newNode.AssignDiffType(Added) newNode.AssignDiffType(Added)
} else { } else {

View File

@ -432,3 +432,41 @@ func TestStackRange(t *testing.T) {
trees := []*FileTree{lowerTree, upperTree, tree} trees := []*FileTree{lowerTree, upperTree, tree}
StackRange(trees, 2) StackRange(trees, 2)
} }
func TestRemoveOnIterate(t *testing.T) {
tree := NewFileTree()
paths := [...]string{"/etc", "/usr", "/etc/hosts", "/etc/sudoers", "/usr/bin", "/usr/something"}
for _, value := range paths {
fakeData := FileInfo{
Path: value,
Typeflag: 1,
MD5sum: [16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}
node, err := tree.AddPath(value, &fakeData)
if err == nil && stringInSlice(node.Path(), []string{"/etc"}) {
node.Data.ViewInfo.Hidden = true
}
}
tree.VisitDepthChildFirst(func(node *FileNode) error {
if node.Data.ViewInfo.Hidden {
tree.RemovePath(node.Path())
}
return nil
}, nil)
expected := `.
usr
bin
something
`
actual := tree.String()
if expected != actual {
t.Errorf("Expected tree string:\n--->%s<---\nGot:\n--->%s<---", expected, actual)
}
}

View File

@ -13,7 +13,8 @@ type FileTreeView struct {
gui *gocui.Gui gui *gocui.Gui
view *gocui.View view *gocui.View
TreeIndex int TreeIndex int
Tree *filetree.FileTree ModelTree *filetree.FileTree
ViewTree *filetree.FileTree
RefTrees []*filetree.FileTree RefTrees []*filetree.FileTree
HiddenDiffTypes []bool HiddenDiffTypes []bool
} }
@ -24,7 +25,7 @@ func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTr
// populate main fields // populate main fields
treeview.Name = name treeview.Name = name
treeview.gui = gui treeview.gui = gui
treeview.Tree = tree treeview.ModelTree = tree
treeview.RefTrees = refTrees treeview.RefTrees = refTrees
treeview.HiddenDiffTypes = make([]bool, 4) treeview.HiddenDiffTypes = make([]bool, 4)
@ -65,6 +66,7 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
return err return err
} }
view.updateViewTree()
view.Render() view.Render()
return nil return nil
@ -81,14 +83,11 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
visitor := func(node *filetree.FileNode) error { visitor := func(node *filetree.FileNode) error {
newNode, err := newTree.GetNode(node.Path()) newNode, err := newTree.GetNode(node.Path())
if err == nil { if err == nil {
newNode.Data.ViewInfo.Collapsed = node.Data.ViewInfo.Collapsed newNode.Data.ViewInfo = node.Data.ViewInfo
} }
return nil return nil
} }
view.Tree.VisitDepthChildFirst(visitor, nil) view.ModelTree.VisitDepthChildFirst(visitor, nil)
// now that the tree has been rebuilt, keep the view seleciton in parity with the previous selection
view.setHiddenFromDiffTypes()
if debug { if debug {
v, _ := view.gui.View("debug") v, _ := view.gui.View("debug")
@ -96,9 +95,11 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
_, _ = fmt.Fprintln(v, view.RefTrees[layerIndex]) _, _ = fmt.Fprintln(v, view.RefTrees[layerIndex])
} }
view.view.SetCursor(0, 0) view.view.SetCursor(0, 0)
view.TreeIndex = 0 view.TreeIndex = 0
view.Tree = newTree view.ModelTree = newTree
view.updateViewTree()
return view.Render() return view.Render()
} }
@ -115,6 +116,8 @@ func (view *FileTreeView) CursorUp() error {
if err == nil { if err == nil {
view.TreeIndex-- view.TreeIndex--
} }
// tmp tmp tmp
view.getAbsPositionNode()
return nil return nil
} }
@ -123,21 +126,26 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
var evaluator func(*filetree.FileNode) bool var evaluator func(*filetree.FileNode) bool
var dfsCounter int var dfsCounter int
// special case: the root node is never visited
if view.TreeIndex == 0 {
return view.ModelTree.Root
}
visiter = func(curNode *filetree.FileNode) error { visiter = func(curNode *filetree.FileNode) error {
dfsCounter++
if dfsCounter == view.TreeIndex { if dfsCounter == view.TreeIndex {
node = curNode node = curNode
} }
dfsCounter++
return nil return nil
} }
evaluator = func(curNode *filetree.FileNode) bool { evaluator = func(curNode *filetree.FileNode) bool {
return !curNode.Data.ViewInfo.Collapsed && !curNode.Data.ViewInfo.Hidden return !curNode.Parent.Data.ViewInfo.Collapsed && !curNode.Data.ViewInfo.Hidden
} }
err := view.Tree.VisitDepthParentFirst(visiter, evaluator) err := view.ModelTree.VisitDepthParentFirst(visiter, evaluator)
if err != nil { if err != nil {
// todo: you guessed it, check errors panic(err)
} }
return node return node
@ -146,25 +154,39 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
func (view *FileTreeView) toggleCollapse() error { func (view *FileTreeView) toggleCollapse() error {
node := view.getAbsPositionNode() node := view.getAbsPositionNode()
node.Data.ViewInfo.Collapsed = !node.Data.ViewInfo.Collapsed node.Data.ViewInfo.Collapsed = !node.Data.ViewInfo.Collapsed
return view.Render() view.updateViewTree()
}
func (view *FileTreeView) setHiddenFromDiffTypes() error {
visitor := func(node *filetree.FileNode) error {
node.Data.ViewInfo.Hidden = view.HiddenDiffTypes[node.Data.DiffType]
return nil
}
view.Tree.VisitDepthChildFirst(visitor, nil)
return view.Render() return view.Render()
} }
func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error { func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error {
view.HiddenDiffTypes[diffType] = !view.HiddenDiffTypes[diffType] view.HiddenDiffTypes[diffType] = !view.HiddenDiffTypes[diffType]
return view.setHiddenFromDiffTypes()
view.view.SetCursor(0, 0)
view.TreeIndex = 0
view.updateViewTree()
return view.Render()
}
func (view *FileTreeView) updateViewTree() {
// keep the view selection in parity with the current DiffType selection
view.ModelTree.VisitDepthChildFirst(func(node *filetree.FileNode) error {
node.Data.ViewInfo.Hidden = view.HiddenDiffTypes[node.Data.DiffType]
return nil
}, nil)
// make a new tree with only visible nodes
view.ViewTree = view.ModelTree.Copy()
view.ViewTree.VisitDepthParentFirst(func(node *filetree.FileNode) error {
if node.Data.ViewInfo.Hidden {
view.ViewTree.RemovePath(node.Path())
}
return nil
}, nil)
} }
func (view *FileTreeView) Render() error { func (view *FileTreeView) Render() error {
renderString := view.Tree.String() // print the tree to the view
renderString := view.ViewTree.String()
view.gui.Update(func(g *gocui.Gui) error { view.gui.Update(func(g *gocui.Gui) error {
view.view.Clear() view.view.Clear()
_, err := fmt.Fprintln(view.view, renderString) _, err := fmt.Fprintln(view.view, renderString)