Skip to content

Commit

Permalink
feat: add support for alternative ordering strategies (#424)
Browse files Browse the repository at this point in the history
  • Loading branch information
iiian authored Jul 7, 2023
1 parent d5e8a92 commit 6f20438
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 63 deletions.
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func initConfig() {
// keybindings: filetree view
viper.SetDefault("keybinding.toggle-collapse-dir", "space")
viper.SetDefault("keybinding.toggle-collapse-all-dir", "ctrl+space")
viper.SetDefault("keybinding.toggle-sort-order", "ctrl+o")
viper.SetDefault("keybinding.toggle-filetree-attributes", "ctrl+b")
viper.SetDefault("keybinding.toggle-added-files", "ctrl+a")
viper.SetDefault("keybinding.toggle-removed-files", "ctrl+r")
Expand Down
2 changes: 1 addition & 1 deletion dive/filetree/efficiency.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func Efficiency(trees []*FileTree) (float64, EfficiencySlice) {
}

if previousTreeNode.Data.FileInfo.IsDir {
err = previousTreeNode.VisitDepthChildFirst(sizer, nil)
err = previousTreeNode.VisitDepthChildFirst(sizer, nil, nil)
if err != nil {
logrus.Errorf("unable to propagate whiteout dir: %+v", err)
return err
Expand Down
50 changes: 29 additions & 21 deletions dive/filetree/file_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package filetree
import (
"archive/tar"
"fmt"
"sort"
"strings"

"github.com/dustin/go-humanize"
Expand All @@ -27,6 +26,7 @@ var diffTypeColor = map[DiffType]*color.Color{
type FileNode struct {
Tree *FileTree
Parent *FileNode
Size int64 // memoized total size of file or directory
Name string
Data NodeData
Children map[string]*FileNode
Expand All @@ -39,6 +39,7 @@ func NewNode(parent *FileNode, name string, data FileInfo) (node *FileNode) {
node.Name = name
node.Data = *NewNodeData()
node.Data.FileInfo = *data.Copy()
node.Size = -1 // signal lazy load later

node.Children = make(map[string]*FileNode)
node.Parent = parent
Expand Down Expand Up @@ -149,41 +150,49 @@ func (node *FileNode) MetadataString() string {
group := node.Data.FileInfo.Gid
userGroup := fmt.Sprintf("%d:%d", user, group)

// don't include file sizes of children that have been removed (unless the node in question is a removed dir,
// then show the accumulated size of removed files)
sizeBytes := node.GetSize()

size := humanize.Bytes(uint64(sizeBytes))

return diffTypeColor[node.Data.DiffType].Sprint(fmt.Sprintf(AttributeFormat, dir, fileMode, userGroup, size))
}

func (node *FileNode) GetSize() int64 {
if 0 <= node.Size {
return node.Size
}
var sizeBytes int64

if node.IsLeaf() {
sizeBytes = node.Data.FileInfo.Size
} else {
sizer := func(curNode *FileNode) error {
// don't include file sizes of children that have been removed (unless the node in question is a removed dir,
// then show the accumulated size of removed files)

if curNode.Data.DiffType != Removed || node.Data.DiffType == Removed {
sizeBytes += curNode.Data.FileInfo.Size
}
return nil
}

err := node.VisitDepthChildFirst(sizer, nil)
err := node.VisitDepthChildFirst(sizer, nil, nil)
if err != nil {
logrus.Errorf("unable to propagate node for metadata: %+v", err)
}
}

size := humanize.Bytes(uint64(sizeBytes))

return diffTypeColor[node.Data.DiffType].Sprint(fmt.Sprintf(AttributeFormat, dir, fileMode, userGroup, size))
node.Size = sizeBytes
return node.Size
}

// VisitDepthChildFirst iterates a tree depth-first (starting at this FileNode), evaluating the deepest depths first (visit on bubble up)
func (node *FileNode) VisitDepthChildFirst(visitor Visitor, evaluator VisitEvaluator) error {
var keys []string
for key := range node.Children {
keys = append(keys, key)
func (node *FileNode) VisitDepthChildFirst(visitor Visitor, evaluator VisitEvaluator, sorter OrderStrategy) error {
if sorter == nil {
sorter = GetSortOrderStrategy(ByName)
}
sort.Strings(keys)
keys := sorter.orderKeys(node.Children)
for _, name := range keys {
child := node.Children[name]
err := child.VisitDepthChildFirst(visitor, evaluator)
err := child.VisitDepthChildFirst(visitor, evaluator, sorter)
if err != nil {
return err
}
Expand All @@ -199,7 +208,7 @@ func (node *FileNode) VisitDepthChildFirst(visitor Visitor, evaluator VisitEvalu
}

// VisitDepthParentFirst iterates a tree depth-first (starting at this FileNode), evaluating the shallowest depths first (visit while sinking down)
func (node *FileNode) VisitDepthParentFirst(visitor Visitor, evaluator VisitEvaluator) error {
func (node *FileNode) VisitDepthParentFirst(visitor Visitor, evaluator VisitEvaluator, sorter OrderStrategy) error {
var err error

doVisit := evaluator != nil && evaluator(node) || evaluator == nil
Expand All @@ -216,14 +225,13 @@ func (node *FileNode) VisitDepthParentFirst(visitor Visitor, evaluator VisitEval
}
}

var keys []string
for key := range node.Children {
keys = append(keys, key)
if sorter == nil {
sorter = GetSortOrderStrategy(ByName)
}
sort.Strings(keys)
keys := sorter.orderKeys(node.Children)
for _, name := range keys {
child := node.Children[name]
err = child.VisitDepthParentFirst(visitor, evaluator)
err = child.VisitDepthParentFirst(visitor, evaluator, sorter)
if err != nil {
return err
}
Expand Down
28 changes: 14 additions & 14 deletions dive/filetree/file_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package filetree
import (
"fmt"
"path"
"sort"
"strings"

"github.com/google/uuid"
Expand All @@ -24,11 +23,12 @@ const (

// FileTree represents a set of files, directories, and their relations.
type FileTree struct {
Root *FileNode
Size int
FileSize uint64
Name string
Id uuid.UUID
Root *FileNode
Size int
FileSize uint64
Name string
Id uuid.UUID
SortOrder SortOrder
}

// NewFileTree creates an empty FileTree
Expand All @@ -39,6 +39,7 @@ func NewFileTree() (tree *FileTree) {
tree.Root.Tree = tree
tree.Root.Children = make(map[string]*FileNode)
tree.Id = uuid.New()
tree.SortOrder = ByName
return tree
}

Expand Down Expand Up @@ -67,12 +68,8 @@ func (tree *FileTree) renderStringTreeBetween(startRow, stopRow int, showAttribu
currentParams, paramsToVisit = paramsToVisit[0], paramsToVisit[1:]

// take note of the next nodes to visit later
var keys []string
for key := range currentParams.node.Children {
keys = append(keys, key)
}
// we should always visit nodes in order
sort.Strings(keys)
sorter := GetSortOrderStrategy(tree.SortOrder)
keys := sorter.orderKeys(currentParams.node.Children)

var childParams = make([]renderParams, 0)
for idx, name := range keys {
Expand Down Expand Up @@ -174,6 +171,7 @@ func (tree *FileTree) Copy() *FileTree {
newTree.Size = tree.Size
newTree.FileSize = tree.FileSize
newTree.Root = tree.Root.Copy(newTree.Root)
newTree.SortOrder = tree.SortOrder

// update the tree pointers
err := newTree.VisitDepthChildFirst(func(node *FileNode) error {
Expand All @@ -196,12 +194,14 @@ type VisitEvaluator func(*FileNode) bool

// VisitDepthChildFirst iterates the given tree depth-first, evaluating the deepest depths first (visit on bubble up)
func (tree *FileTree) VisitDepthChildFirst(visitor Visitor, evaluator VisitEvaluator) error {
return tree.Root.VisitDepthChildFirst(visitor, evaluator)
sorter := GetSortOrderStrategy(tree.SortOrder)
return tree.Root.VisitDepthChildFirst(visitor, evaluator, sorter)
}

// VisitDepthParentFirst iterates the given tree depth-first, evaluating the shallowest depths first (visit while sinking down)
func (tree *FileTree) VisitDepthParentFirst(visitor Visitor, evaluator VisitEvaluator) error {
return tree.Root.VisitDepthParentFirst(visitor, evaluator)
sorter := GetSortOrderStrategy(tree.SortOrder)
return tree.Root.VisitDepthParentFirst(visitor, evaluator, sorter)
}

// Stack takes two trees and combines them together. This is done by "stacking" the given tree on top of the owning tree.
Expand Down
61 changes: 61 additions & 0 deletions dive/filetree/order_strategy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package filetree

import (
"sort"
)

type SortOrder int

const (
ByName = iota
BySizeDesc

NumSortOrderConventions
)

type OrderStrategy interface {
orderKeys(files map[string]*FileNode) []string
}

func GetSortOrderStrategy(sortOrder SortOrder) OrderStrategy {
switch sortOrder {
case ByName:
return orderByNameStrategy{}
case BySizeDesc:
return orderBySizeDescStrategy{}
}
return orderByNameStrategy{}
}

type orderByNameStrategy struct{}

func (orderByNameStrategy) orderKeys(files map[string]*FileNode) []string {
var keys []string
for key := range files {
keys = append(keys, key)
}

sort.Strings(keys)

return keys
}

type orderBySizeDescStrategy struct{}

func (orderBySizeDescStrategy) orderKeys(files map[string]*FileNode) []string {
var keys []string
for key := range files {
keys = append(keys, key)
}

sort.Slice(keys, func(i, j int) bool {
ki, kj := keys[i], keys[j]
ni, nj := files[ki], files[kj]
if ni.GetSize() == nj.GetSize() {
return ki < kj
}
return ni.GetSize() > nj.GetSize()
})

return keys
}
17 changes: 16 additions & 1 deletion runtime/ui/view/filetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type FileTree struct {
gui *gocui.Gui
view *gocui.View
header *gocui.View
vm *viewmodel.FileTree
vm *viewmodel.FileTreeViewModel
title string

filterRegex *regexp.Regexp
Expand Down Expand Up @@ -98,6 +98,11 @@ func (v *FileTree) Setup(view, header *gocui.View) error {
OnAction: v.toggleCollapseAll,
Display: "Collapse all dir",
},
{
ConfigKeys: []string{"keybinding.toggle-sort-order"},
OnAction: v.toggleSortOrder,
Display: "Toggle sort order",
},
{
ConfigKeys: []string{"keybinding.toggle-added-files"},
OnAction: func() error { return v.toggleShowDiffType(filetree.Added) },
Expand Down Expand Up @@ -288,6 +293,16 @@ func (v *FileTree) toggleCollapseAll() error {
return v.Render()
}

func (v *FileTree) toggleSortOrder() error {
err := v.vm.ToggleSortOrder()
if err != nil {
return err
}
v.resetCursor()
_ = v.Update()
return v.Render()
}

func (v *FileTree) toggleWrapTree() error {
v.view.Wrap = !v.view.Wrap
return nil
Expand Down
Loading

0 comments on commit 6f20438

Please sign in to comment.