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

View File

@ -1,7 +1,6 @@
package filetree
import (
"errors"
"fmt"
"sort"
"strings"
@ -88,8 +87,10 @@ func (tree *FileTree) String() string {
func (tree *FileTree) Copy() *FileTree {
newTree := NewFileTree()
*newTree = *tree
newTree.Root = tree.Root.Copy()
newTree.Size = tree.Size
newTree.Root = tree.Root.Copy(newTree.Root)
// update the tree pointers
newTree.VisitDepthChildFirst(func(node *FileNode) error {
node.Tree = newTree
return nil
@ -116,12 +117,12 @@ func (tree *FileTree) Stack(upper *FileTree) error {
if node.IsWhiteout() {
err := tree.RemovePath(node.Path())
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 {
newNode, err := tree.AddPath(node.Path(), node.Data.FileInfo)
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
@ -137,7 +138,7 @@ func (tree *FileTree) GetNode(path string) (*FileNode, error) {
continue
}
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]
}
@ -183,7 +184,7 @@ func (tree *FileTree) Compare(upper *FileTree) error {
if upperNode.IsWhiteout() {
err := tree.MarkRemoved(upperNode.Path())
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 {
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)
// fmt.Printf("added new upperNode at %s\n", newNode.Path())
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)
} else {

View File

@ -432,3 +432,41 @@ func TestStackRange(t *testing.T) {
trees := []*FileTree{lowerTree, upperTree, tree}
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
view *gocui.View
TreeIndex int
Tree *filetree.FileTree
ModelTree *filetree.FileTree
ViewTree *filetree.FileTree
RefTrees []*filetree.FileTree
HiddenDiffTypes []bool
}
@ -24,7 +25,7 @@ func NewFileTreeView(name string, gui *gocui.Gui, tree *filetree.FileTree, refTr
// populate main fields
treeview.Name = name
treeview.gui = gui
treeview.Tree = tree
treeview.ModelTree = tree
treeview.RefTrees = refTrees
treeview.HiddenDiffTypes = make([]bool, 4)
@ -65,6 +66,7 @@ func (view *FileTreeView) Setup(v *gocui.View) error {
return err
}
view.updateViewTree()
view.Render()
return nil
@ -81,14 +83,11 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
visitor := func(node *filetree.FileNode) error {
newNode, err := newTree.GetNode(node.Path())
if err == nil {
newNode.Data.ViewInfo.Collapsed = node.Data.ViewInfo.Collapsed
newNode.Data.ViewInfo = node.Data.ViewInfo
}
return nil
}
view.Tree.VisitDepthChildFirst(visitor, nil)
// now that the tree has been rebuilt, keep the view seleciton in parity with the previous selection
view.setHiddenFromDiffTypes()
view.ModelTree.VisitDepthChildFirst(visitor, nil)
if debug {
v, _ := view.gui.View("debug")
@ -96,9 +95,11 @@ func (view *FileTreeView) setLayer(layerIndex int) error {
_, _ = fmt.Fprintln(v, view.RefTrees[layerIndex])
}
view.view.SetCursor(0, 0)
view.TreeIndex = 0
view.Tree = newTree
view.ModelTree = newTree
view.updateViewTree()
return view.Render()
}
@ -115,6 +116,8 @@ func (view *FileTreeView) CursorUp() error {
if err == nil {
view.TreeIndex--
}
// tmp tmp tmp
view.getAbsPositionNode()
return nil
}
@ -123,21 +126,26 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
var evaluator func(*filetree.FileNode) bool
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 {
dfsCounter++
if dfsCounter == view.TreeIndex {
node = curNode
}
dfsCounter++
return nil
}
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 {
// todo: you guessed it, check errors
panic(err)
}
return node
@ -146,25 +154,39 @@ func (view *FileTreeView) getAbsPositionNode() (node *filetree.FileNode) {
func (view *FileTreeView) toggleCollapse() error {
node := view.getAbsPositionNode()
node.Data.ViewInfo.Collapsed = !node.Data.ViewInfo.Collapsed
return view.Render()
}
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)
view.updateViewTree()
return view.Render()
}
func (view *FileTreeView) toggleShowDiffType(diffType filetree.DiffType) error {
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 {
renderString := view.Tree.String()
// print the tree to the view
renderString := view.ViewTree.String()
view.gui.Update(func(g *gocui.Gui) error {
view.view.Clear()
_, err := fmt.Fprintln(view.view, renderString)