mirror of
https://github.com/grdl/git-get.git
synced 2026-02-11 22:39:05 +00:00
Cleanup the print package
- Use strings.Builder instead of concatenation - Move smart tree to a separate file - Add missing docstrings
This commit is contained in:
@@ -45,9 +45,9 @@ func List(c *ListCfg) error {
|
|||||||
case cfg.OutFlat:
|
case cfg.OutFlat:
|
||||||
printer = &print.FlatPrinter{}
|
printer = &print.FlatPrinter{}
|
||||||
case cfg.OutTree:
|
case cfg.OutTree:
|
||||||
printer = &print.SimpleTreePrinter{}
|
printer = &print.TreePrinter{}
|
||||||
case cfg.OutSmart:
|
case cfg.OutSmart:
|
||||||
printer = &print.SmartTreePrinter{}
|
printer = &print.SmartPrinter{}
|
||||||
case cfg.OutDump:
|
case cfg.OutDump:
|
||||||
printer = &print.DumpPrinter{}
|
printer = &print.DumpPrinter{}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DumpPrinter implements Printer interface and provides method for printing list of repos in dump file format.
|
||||||
type DumpPrinter struct{}
|
type DumpPrinter struct{}
|
||||||
|
|
||||||
// Print generates a list of repos URLs. Each line contains a URL and, if applicable, a currently checked out branch name.
|
// Print generates a list of repos URLs. Each line contains a URL and, if applicable, a currently checked out branch name.
|
||||||
|
|||||||
@@ -7,16 +7,18 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FlatPrinter implements Printer interface and provides method for printing list of repos in flat format.
|
||||||
type FlatPrinter struct{}
|
type FlatPrinter struct{}
|
||||||
|
|
||||||
|
// Print generates a flat list of repositories and their statuses - each repo in new line with full path.
|
||||||
func (p *FlatPrinter) Print(root string, repos []*repo.Repo) string {
|
func (p *FlatPrinter) Print(root string, repos []*repo.Repo) string {
|
||||||
val := root
|
var str strings.Builder
|
||||||
|
|
||||||
for _, r := range repos {
|
for _, r := range repos {
|
||||||
path := strings.TrimPrefix(r.Path, root)
|
path := strings.TrimPrefix(r.Path, root)
|
||||||
path = strings.Trim(path, string(filepath.Separator))
|
path = strings.Trim(path, string(filepath.Separator))
|
||||||
|
|
||||||
val += fmt.Sprintf("\n%s %s", path, printWorktreeStatus(r))
|
str.WriteString(fmt.Sprintf("\n%s %s", path, printWorktreeStatus(r)))
|
||||||
|
|
||||||
for _, branch := range r.Status.Branches {
|
for _, branch := range r.Status.Branches {
|
||||||
// Don't print the status of the current branch. It was already printed above.
|
// Don't print the status of the current branch. It was already printed above.
|
||||||
@@ -25,9 +27,9 @@ func (p *FlatPrinter) Print(root string, repos []*repo.Repo) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
indent := strings.Repeat(" ", len(path))
|
indent := strings.Repeat(" ", len(path))
|
||||||
val += fmt.Sprintf("\n%s %s", indent, printBranchStatus(branch))
|
str.WriteString(fmt.Sprintf("\n%s %s", indent, printBranchStatus(branch)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return val
|
return str.String()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Printer prints list of repos and their statuses
|
||||||
type Printer interface {
|
type Printer interface {
|
||||||
Print(root string, repos []*repo.Repo) string
|
Print(root string, repos []*repo.Repo) string
|
||||||
}
|
}
|
||||||
|
|||||||
98
pkg/print/smart.go
Normal file
98
pkg/print/smart.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package print
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git-get/pkg/repo"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SmartPrinter implements Printer interface and provides methods for printing repos and their statuses.
|
||||||
|
// It's "smart" because it automatically folds branches which only have a single child and indents branches with many children.
|
||||||
|
type SmartPrinter struct {
|
||||||
|
// length is the size (number of chars) of the currently processed line.
|
||||||
|
// It's used to correctly indent the lines with branches status.
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print generates a list of repositories and their statuses.
|
||||||
|
func (p *SmartPrinter) Print(root string, repos []*repo.Repo) string {
|
||||||
|
tree := buildTree(root, repos)
|
||||||
|
|
||||||
|
return p.printSmartTree(tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
// printSmartTree 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 only a single child, it is printed in the same line using path separator.
|
||||||
|
// For better readability the first level (repos hosts) is not indented.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// Following paths:
|
||||||
|
// /repos/github.com/user/repo1
|
||||||
|
// /repos/github.com/user/repo2
|
||||||
|
// /repos/github.com/another/repo
|
||||||
|
//
|
||||||
|
// will render a tree:
|
||||||
|
// /repos/
|
||||||
|
// github.com/
|
||||||
|
// user/
|
||||||
|
// repo1
|
||||||
|
// repo2
|
||||||
|
// another/repo
|
||||||
|
//
|
||||||
|
func (p *SmartPrinter) printSmartTree(node *Node) string {
|
||||||
|
if node.children == nil {
|
||||||
|
// If node is a leaf, print repo name and its status and finish processing this node.
|
||||||
|
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.Repository == nil {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
value += " " + printWorktreeStatus(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(" ", p.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 + printBranchStatus(branch)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
val := node.val + string(filepath.Separator)
|
||||||
|
|
||||||
|
shift := ""
|
||||||
|
if node.parent == nil {
|
||||||
|
// If node is a root, print its children on a new line without indentation.
|
||||||
|
shift = "\n"
|
||||||
|
} else if len(node.children) == 1 {
|
||||||
|
// If node has only a single child, print it on the same line as its parent.
|
||||||
|
// 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.depth = node.parent.depth
|
||||||
|
|
||||||
|
p.length += len(val)
|
||||||
|
} else {
|
||||||
|
// If node has multiple children, print each of them on a new line
|
||||||
|
// and indent them once relative to the parent
|
||||||
|
node.depth = node.parent.depth + 1
|
||||||
|
shift = "\n" + strings.Repeat(" ", node.depth)
|
||||||
|
p.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range node.children {
|
||||||
|
p.length += len(shift)
|
||||||
|
val += shift + p.printSmartTree(child)
|
||||||
|
p.length = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return val
|
||||||
|
}
|
||||||
@@ -8,27 +8,17 @@ import (
|
|||||||
"github.com/xlab/treeprint"
|
"github.com/xlab/treeprint"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SimpleTreePrinter struct{}
|
// TreePrinter implements Printer interface and provides methods for printing repos and their statuses.
|
||||||
|
type TreePrinter struct{}
|
||||||
|
|
||||||
type SmartTreePrinter struct {
|
// Print generates a tree view of repos and their statuses.
|
||||||
// length is the size (number of chars) of the currently processed line.
|
func (p *TreePrinter) Print(root string, repos []*repo.Repo) string {
|
||||||
// It's used to correctly indent the lines with branches status.
|
tree := buildTree(root, repos)
|
||||||
length int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SmartTreePrinter) Print(root string, repos []*repo.Repo) string {
|
|
||||||
tree := BuildTree(root, repos)
|
|
||||||
|
|
||||||
return p.PrintSmartTree(tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimpleTreePrinter) Print(root string, repos []*repo.Repo) string {
|
|
||||||
tree := BuildTree(root, repos)
|
|
||||||
|
|
||||||
tp := treeprint.New()
|
tp := treeprint.New()
|
||||||
tp.SetValue(root)
|
tp.SetValue(root)
|
||||||
|
|
||||||
p.PrintSimpleTree(tree, tp)
|
p.printTree(tree, tp)
|
||||||
|
|
||||||
return tp.String()
|
return tp.String()
|
||||||
}
|
}
|
||||||
@@ -80,10 +70,10 @@ func (n *Node) GetChild(val string) *Node {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildTree builds a directory tree of paths to repositories.
|
// buildTree builds a directory tree of paths to repositories.
|
||||||
// Each node represents a directory in the repo path.
|
// Each node represents a directory in the repo path.
|
||||||
// Each leaf (final node) contains a pointer to the repo.
|
// Each leaf (final node) contains a pointer to the repo.
|
||||||
func BuildTree(root string, repos []*repo.Repo) *Node {
|
func buildTree(root string, repos []*repo.Repo) *Node {
|
||||||
tree := Root(root)
|
tree := Root(root)
|
||||||
|
|
||||||
for _, r := range repos {
|
for _, r := range repos {
|
||||||
@@ -114,86 +104,7 @@ func BuildTree(root string, repos []*repo.Repo) *Node {
|
|||||||
return tree
|
return tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrintSmartTree returns a string representation of repos tree.
|
func (p *TreePrinter) printTree(node *Node, tp treeprint.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.
|
|
||||||
// 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.
|
|
||||||
// For better readability the first level (repos hosts) is not indented.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// Following paths:
|
|
||||||
// /repos/github.com/user/repo1
|
|
||||||
// /repos/github.com/user/repo2
|
|
||||||
// /repos/github.com/another/repo
|
|
||||||
//
|
|
||||||
// will render a tree:
|
|
||||||
// /repos/
|
|
||||||
// github.com/
|
|
||||||
// user/
|
|
||||||
// repo1
|
|
||||||
// repo2
|
|
||||||
// another/repo
|
|
||||||
//
|
|
||||||
func (p *SmartTreePrinter) PrintSmartTree(node *Node) string {
|
|
||||||
if node.children == nil {
|
|
||||||
// If node is a leaf, print repo name and its status and finish processing this node.
|
|
||||||
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.Repository == nil {
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
value += " " + printWorktreeStatus(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(" ", p.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 + printBranchStatus(branch)
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
val := node.val + string(filepath.Separator)
|
|
||||||
|
|
||||||
shift := ""
|
|
||||||
if node.parent == nil {
|
|
||||||
// If node is a root, print its children on a new line without indentation.
|
|
||||||
shift = "\n"
|
|
||||||
} else if len(node.children) == 1 {
|
|
||||||
// If node has only a single child, print it on the same line as its parent.
|
|
||||||
// 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.depth = node.parent.depth
|
|
||||||
|
|
||||||
p.length += len(val)
|
|
||||||
} else {
|
|
||||||
// If node has multiple children, print each of them on a new line
|
|
||||||
// and indent them once relative to the parent
|
|
||||||
node.depth = node.parent.depth + 1
|
|
||||||
shift = "\n" + strings.Repeat(" ", node.depth)
|
|
||||||
p.length = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range node.children {
|
|
||||||
p.length += len(shift)
|
|
||||||
val += shift + p.PrintSmartTree(child)
|
|
||||||
p.length = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *SimpleTreePrinter) PrintSimpleTree(node *Node, tp treeprint.Tree) {
|
|
||||||
if node.children == nil {
|
if node.children == nil {
|
||||||
tp.SetValue(node.val + " " + printWorktreeStatus(node.repo))
|
tp.SetValue(node.val + " " + printWorktreeStatus(node.repo))
|
||||||
|
|
||||||
@@ -209,6 +120,6 @@ func (p *SimpleTreePrinter) PrintSimpleTree(node *Node, tp treeprint.Tree) {
|
|||||||
|
|
||||||
for _, child := range node.children {
|
for _, child := range node.children {
|
||||||
branch := tp.AddBranch(child.val)
|
branch := tp.AddBranch(child.val)
|
||||||
p.PrintSimpleTree(child, branch)
|
p.printTree(child, branch)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ gitlab.com/
|
|||||||
repos = append(repos, repo.New(nil, path)) //&Repo{path: path})
|
repos = append(repos, repo.New(nil, path)) //&Repo{path: path})
|
||||||
}
|
}
|
||||||
|
|
||||||
printer := SmartTreePrinter{}
|
printer := SmartPrinter{}
|
||||||
// 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", printer.Print("root", repos))
|
got := fmt.Sprintf("\n%s\n", printer.Print("root", repos))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user