mirror of
https://github.com/grdl/git-get.git
synced 2026-02-12 12:31:17 +00:00
Improve the implementation of the smart tree printer
It now correctly indents lines with branches status
This commit is contained in:
@@ -106,7 +106,7 @@ func PrintRepos(repos []*Repo) {
|
|||||||
root := viper.GetString(KeyReposRoot)
|
root := viper.GetString(KeyReposRoot)
|
||||||
|
|
||||||
tree := BuildTree(root, repos)
|
tree := BuildTree(root, repos)
|
||||||
fmt.Println(RenderTree(tree))
|
fmt.Println(RenderSmartTree(tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
45
pkg/tree.go
45
pkg/tree.go
@@ -86,7 +86,9 @@ func BuildTree(root string, repos []*Repo) *Node {
|
|||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderTree returns a string representation of repos tree.
|
// RenderSmartTree returns a string representation of repos tree.
|
||||||
|
// It's "smart" because it automatically folds branches which only have a single child and indents branches with many children.
|
||||||
|
//
|
||||||
// It recursively traverses the tree and prints its nodes.
|
// It recursively traverses the tree and prints its nodes.
|
||||||
// If a node contains multiple children, they are be printed in new lines and indented.
|
// If a node contains multiple children, they are be printed in new lines and indented.
|
||||||
// If a node contains only a single child, it is printed in the same line using path separator.
|
// If a node contains only a single child, it is printed in the same line using path separator.
|
||||||
@@ -106,12 +108,35 @@ func BuildTree(root string, repos []*Repo) *Node {
|
|||||||
// repo2
|
// repo2
|
||||||
// another/repo
|
// another/repo
|
||||||
//
|
//
|
||||||
func RenderTree(node *Node) string {
|
func RenderSmartTree(node *Node) string {
|
||||||
if node.children == nil {
|
if node.children == nil {
|
||||||
// If node is a leaf, print repo name and its status and finish processing this node.
|
// If node is a leaf, print repo name and its status and finish processing this node.
|
||||||
return node.val + " " + renderWorktreeStatus(node.repo)
|
value := node.val
|
||||||
|
|
||||||
|
// TODO: Ugly
|
||||||
|
// If this is called from tests the repo will be nil and we should return just the name without the status.
|
||||||
|
if node.repo.repo == nil {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
value += " " + renderWorktreeStatus(node.repo)
|
||||||
|
|
||||||
|
// Print the status of each branch on a new line, indented to match the position of the current branch name.
|
||||||
|
indent := "\n" + strings.Repeat(" ", length+len(node.val))
|
||||||
|
for _, branch := range node.repo.Status.Branches {
|
||||||
|
// Don't print the status of the current branch. It was already printed above.
|
||||||
|
if branch.Name == node.repo.Status.CurrentBranch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value += indent + renderBranchStatus(branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val := node.val + string(filepath.Separator)
|
||||||
|
|
||||||
shift := ""
|
shift := ""
|
||||||
if node.parent == nil {
|
if node.parent == nil {
|
||||||
// If node is a root, print its children on a new line without indentation.
|
// If node is a root, print its children on a new line without indentation.
|
||||||
@@ -121,17 +146,25 @@ func RenderTree(node *Node) string {
|
|||||||
// Setting node's depth to the same as parent's ensures that its children will be indented only once even if
|
// Setting node's depth to the same as parent's ensures that its children will be indented only once even if
|
||||||
// node's path has multiple levels above.
|
// node's path has multiple levels above.
|
||||||
node.depth = node.parent.depth
|
node.depth = node.parent.depth
|
||||||
|
|
||||||
|
length += len(val)
|
||||||
} else {
|
} else {
|
||||||
// If node has multiple children, print each of them on a new line
|
// If node has multiple children, print each of them on a new line
|
||||||
// and indent them once relative to the parent
|
// and indent them once relative to the parent
|
||||||
node.depth = node.parent.depth + 1
|
node.depth = node.parent.depth + 1
|
||||||
shift = "\n" + strings.Repeat("\t", node.depth)
|
shift = "\n" + strings.Repeat(" ", node.depth)
|
||||||
|
length = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
val := node.val + string(filepath.Separator)
|
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
val += shift + RenderTree(child)
|
length += len(shift)
|
||||||
|
val += shift + RenderSmartTree(child)
|
||||||
|
length = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lenght is the size (number of chars) of the currently processed line.
|
||||||
|
// It's used to correctly indent the lines with branches status.
|
||||||
|
var length int
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package pkg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -91,14 +92,15 @@ gitlab.com/
|
|||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
var repos []*Repo
|
var repos []*Repo
|
||||||
for _, path := range test.paths {
|
for _, path := range test.paths {
|
||||||
repos = append(repos, &Repo{path: path})
|
repos = append(repos, newRepo(nil, path)) //&Repo{path: path})
|
||||||
}
|
}
|
||||||
|
|
||||||
tree := BuildTree("root", repos)
|
tree := BuildTree("root", repos)
|
||||||
// Leading and trailing newlines are added to test cases for readability. We also need to add them to the rendering result.
|
// Leading and trailing newlines are added to test cases for readability. We also need to add them to the rendering result.
|
||||||
got := fmt.Sprintf("\n%s\n", RenderTree(tree))
|
got := fmt.Sprintf("\n%s\n", RenderSmartTree(tree))
|
||||||
|
|
||||||
if got != test.want {
|
// Rendered tree uses spaces for indentation but the test cases use tabs.
|
||||||
|
if got != strings.ReplaceAll(test.want, "\t", " ") {
|
||||||
t.Errorf("Failed test case %d, got: %+v; want: %+v", i, got, test.want)
|
t.Errorf("Failed test case %d, got: %+v; want: %+v", i, got, test.want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user