diff --git a/README.md b/README.md
index 4f214341..e8b39325 100644
--- a/README.md
+++ b/README.md
@@ -562,6 +562,100 @@ for i := 0; i < repeat; i++ {
}
```
+## Rendering Trees
+
+Lip Gloss ships with a tree rendering sub-package.
+
+```go
+import "github.com/charmbracelet/lipgloss/tree"
+```
+
+Define a new tree.
+
+```go
+t := tree.Root(".").
+ Child("A", "B", "C")
+```
+
+Print the tree.
+
+```go
+fmt.Println(t)
+
+// .
+// ├── A
+// ├── B
+// └── C
+```
+
+Trees have the ability to nest.
+
+```go
+t := tree.Root(".").
+ Child("Item 1").
+ Child(
+ tree.Root("Item 2").
+ Child("Item 2.1").
+ Child("Item 2.2").
+ Child("Item 2.3"),
+ ).
+ Child(
+ tree.Root("Item 3").
+ Child("Item 3.1").
+ Child("Item 3.2"),
+ )
+```
+
+Print the tree.
+
+```go
+fmt.Println(t)
+```
+
+
+
+
+
+Trees can be customized via their enumeration function as well as using
+`lipgloss.Style`s.
+
+```go
+enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
+itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)
+
+t := tree.
+ Root("Makeup").
+ Child(
+ "Glossier",
+ "Claire’s Boutique",
+ "Nyx",
+ "Mac",
+ "Milk",
+ ).
+ Enumerator(tree.RoundedEnumerator).
+ EnumeratorStyle(enumeratorStyle).
+ ItemStyle(itemStyle)
+
+```
+
+Print the tree.
+
+
+
+
+
+The predefined enumerators for trees are `DefaultEnumerator` and `RoundedEnumerator`.
+
+If you need, you can also build trees incrementally:
+
+```go
+t := tree.New()
+
+for i := 0; i < repeat; i++ {
+ t.Child("Lip Gloss")
+}
+```
+
---
## FAQ
diff --git a/examples/go.mod b/examples/go.mod
index 64150bcf..15b2609c 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -22,7 +22,7 @@ require (
github.com/charmbracelet/bubbletea v0.25.0 // indirect
github.com/charmbracelet/keygen v0.5.0 // indirect
github.com/charmbracelet/log v0.4.0 // indirect
- github.com/charmbracelet/x/ansi v0.1.3 // indirect
+ github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 // indirect
github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
diff --git a/examples/go.sum b/examples/go.sum
index 9a640cb8..72eaeba8 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -13,8 +13,8 @@ github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 h1:NZKjJ7d/pzk/A
github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917/go.mod h1:8/Ve8iGRRIGFM1kepYfRF2pEOF5Y3TEZYoJaA54228U=
github.com/charmbracelet/wish v1.4.0 h1:pL1uVP/YuYgJheHEj98teZ/n6pMYnmlZq/fcHvomrfc=
github.com/charmbracelet/wish v1.4.0/go.mod h1:ew4/MjJVfW/akEO9KmrQHQv1F7bQRGscRMrA+KtovTk=
-github.com/charmbracelet/x/ansi v0.1.3 h1:RBh/eleNWML5R524mjUF0yVRePTwqN9tPtV+DPgO5Lw=
-github.com/charmbracelet/x/ansi v0.1.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
+github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
+github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI=
github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
github.com/charmbracelet/x/exp/term v0.0.0-20240328150354-ab9afc214dfd h1:HqBjkSFXXfW4IgX3TMKipWoPEN08T3Pi4SA/3DLss/U=
diff --git a/examples/tree/background/main.go b/examples/tree/background/main.go
new file mode 100644
index 00000000..a38ca626
--- /dev/null
+++ b/examples/tree/background/main.go
@@ -0,0 +1,39 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+func main() {
+ enumeratorStyle := lipgloss.NewStyle().
+ Background(lipgloss.Color("0")).
+ Padding(0, 1)
+
+ headerItemStyle := lipgloss.NewStyle().
+ Background(lipgloss.Color("#ee6ff8")).
+ Foreground(lipgloss.Color("#ecfe65")).
+ Bold(true).
+ Padding(0, 1)
+
+ itemStyle := headerItemStyle.Background(lipgloss.Color("0"))
+
+ t := tree.Root("# Table of Contents").
+ RootStyle(itemStyle).
+ ItemStyle(itemStyle).
+ EnumeratorStyle(enumeratorStyle).
+ Child(
+ tree.Root("## Chapter 1").
+ Child("Chapter 1.1").
+ Child("Chapter 1.2"),
+ ).
+ Child(
+ tree.Root("## Chapter 2").
+ Child("Chapter 2.1").
+ Child("Chapter 2.2"),
+ )
+
+ fmt.Println(t)
+}
diff --git a/examples/tree/files/main.go b/examples/tree/files/main.go
new file mode 100644
index 00000000..9e6250b8
--- /dev/null
+++ b/examples/tree/files/main.go
@@ -0,0 +1,28 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+func main() {
+ enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240")).PaddingRight(1)
+ itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).Bold(true).PaddingRight(1)
+
+ t := tree.Root(".").EnumeratorStyle(enumeratorStyle).ItemStyle(itemStyle)
+ _ = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ if info.IsDir() {
+ t.Child(tree.Root(path))
+ }
+ return nil
+ })
+
+ fmt.Println(t)
+}
diff --git a/examples/tree/makeup/main.go b/examples/tree/makeup/main.go
new file mode 100644
index 00000000..2619cf81
--- /dev/null
+++ b/examples/tree/makeup/main.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+func main() {
+ enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
+ itemStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)
+
+ t := tree.
+ Root("Makeup").
+ Child(
+ "Glossier",
+ "Claire’s Boutique",
+ "Nyx",
+ "Mac",
+ "Milk",
+ ).
+ Enumerator(tree.RoundedEnumerator).
+ EnumeratorStyle(enumeratorStyle).
+ ItemStyle(itemStyle).
+ RootStyle(lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")))
+
+ fmt.Println(t)
+}
diff --git a/examples/tree/rounded/main.go b/examples/tree/rounded/main.go
new file mode 100644
index 00000000..c32dae3a
--- /dev/null
+++ b/examples/tree/rounded/main.go
@@ -0,0 +1,37 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+func main() {
+ itemStyle := lipgloss.NewStyle().MarginRight(1)
+ enumeratorStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("8")).MarginRight(1)
+
+ t := tree.Root("Groceries").
+ Child(
+ tree.Root("Fruits").
+ Child(
+ "Blood Orange",
+ "Papaya",
+ "Dragonfruit",
+ "Yuzu",
+ ),
+ tree.Root("Items").
+ Child(
+ "Cat Food",
+ "Nutella",
+ "Powdered Sugar",
+ ),
+ tree.Root("Veggies").
+ Child(
+ "Leek",
+ "Artichoke",
+ ),
+ ).ItemStyle(itemStyle).EnumeratorStyle(enumeratorStyle).Enumerator(tree.RoundedEnumerator)
+
+ fmt.Println(t)
+}
diff --git a/examples/tree/simple/main.go b/examples/tree/simple/main.go
new file mode 100644
index 00000000..718022ed
--- /dev/null
+++ b/examples/tree/simple/main.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+func main() {
+ t := tree.Root(".").
+ Child("Item 1").
+ Child(
+ tree.New().
+ Root("Item 2").
+ Child("Item 2.1").
+ Child("Item 2.2").
+ Child("Item 2.3"),
+ ).
+ Child(
+ tree.New().
+ Root("Item 3").
+ Child("Item 3.1").
+ Child("Item 3.2"),
+ )
+
+ fmt.Println(t)
+}
diff --git a/examples/tree/styles/main.go b/examples/tree/styles/main.go
new file mode 100644
index 00000000..9950b1f4
--- /dev/null
+++ b/examples/tree/styles/main.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+func main() {
+ purple := lipgloss.NewStyle().Foreground(lipgloss.Color("99")).MarginRight(1)
+ pink := lipgloss.NewStyle().Foreground(lipgloss.Color("212")).MarginRight(1)
+
+ t := tree.New().
+ Child(
+ "Glossier",
+ "Claire’s Boutique",
+ tree.Root("Nyx").
+ Child("Lip Gloss", "Foundation").
+ EnumeratorStyle(pink),
+ "Mac",
+ "Milk",
+ ).
+ EnumeratorStyle(purple)
+ fmt.Println(t)
+}
diff --git a/examples/tree/toggle/main.go b/examples/tree/toggle/main.go
new file mode 100644
index 00000000..b914c2f5
--- /dev/null
+++ b/examples/tree/toggle/main.go
@@ -0,0 +1,92 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/charmbracelet/lipgloss"
+ "github.com/charmbracelet/lipgloss/tree"
+)
+
+type styles struct {
+ base,
+ block,
+ enumerator,
+ dir,
+ toggle,
+ file lipgloss.Style
+}
+
+func defaultStyles() styles {
+ var s styles
+ s.base = lipgloss.NewStyle().
+ Background(lipgloss.Color("57")).
+ Foreground(lipgloss.Color("225"))
+ s.block = s.base.
+ Padding(1, 3).
+ Margin(1, 3).
+ Width(40)
+ s.enumerator = s.base.
+ Foreground(lipgloss.Color("212")).
+ PaddingRight(1)
+ s.dir = s.base.
+ Inline(true)
+ s.toggle = s.base.
+ Foreground(lipgloss.Color("207")).
+ PaddingRight(1)
+ s.file = s.base
+ return s
+}
+
+type dir struct {
+ name string
+ open bool
+ styles styles
+}
+
+func (d dir) String() string {
+ t := d.styles.toggle.Render
+ n := d.styles.dir.Render
+ if d.open {
+ return t("▼") + n(d.name)
+ }
+ return t("▶") + n(d.name)
+}
+
+type file struct {
+ name string
+ styles styles
+}
+
+func (s file) String() string {
+ return s.styles.file.Render(s.name)
+}
+
+func main() {
+ s := defaultStyles()
+
+ t := tree.Root(dir{"~", true, s}).
+ Enumerator(tree.RoundedEnumerator).
+ EnumeratorStyle(s.enumerator).
+ Child(
+ dir{"ayman", false, s},
+ tree.Root(dir{"bash", false, s}).
+ Child(
+ tree.Root(dir{"tools", true, s}).
+ Child(
+ file{"zsh", s},
+ file{"doom-emacs", s},
+ ),
+ ),
+ tree.Root(dir{"carlos", true, s}).
+ Child(
+ tree.Root(dir{"emotes", true, s}).
+ Child(
+ file{"chefkiss.png", s},
+ file{"kekw.png", s},
+ ),
+ ),
+ dir{"maas", false, s},
+ )
+
+ fmt.Println(s.block.Render(t.String()))
+}
diff --git a/go.mod b/go.mod
index de9c8269..7df3fefa 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ go 1.18
require (
github.com/aymanbagabas/go-udiff v0.2.0
github.com/charmbracelet/x/ansi v0.1.4
+ github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a
github.com/muesli/termenv v0.15.2
github.com/rivo/uniseg v0.4.7
)
diff --git a/go.sum b/go.sum
index 1b8d1dc9..2a55532a 100644
--- a/go.sum
+++ b/go.sum
@@ -2,12 +2,10 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
-github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
-github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
-github.com/charmbracelet/x/ansi v0.1.3 h1:RBh/eleNWML5R524mjUF0yVRePTwqN9tPtV+DPgO5Lw=
-github.com/charmbracelet/x/ansi v0.1.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
+github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
+github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
diff --git a/list/enumerator.go b/list/enumerator.go
index ce55b1e7..1fe327b8 100644
--- a/list/enumerator.go
+++ b/list/enumerator.go
@@ -120,33 +120,3 @@ func Asterisk(Items, int) string {
func Dash(Items, int) string {
return "-"
}
-
-// Tree enumerates a tree.
-//
-// ├── Foo
-// ├── Bar
-// ├── Baz
-// └── Qux.
-func Tree(items Items, index int) string {
- if items.Length()-1 == index {
- return "└──"
- }
- return "├──"
-}
-
-// DefaultIndenter indents a tree for nested trees and multiline content.
-//
-// ├── Foo
-// ├── Bar
-// │ ├── Qux
-// │ ├── Quux
-// │ │ ├── Foo
-// │ │ └── Bar
-// │ └── Quuux
-// └── Baz.
-func TreeIndenter(items Items, index int) string {
- if items.Length()-1 == index {
- return " "
- }
- return "│ "
-}
diff --git a/list/list.go b/list/list.go
index 30aa6ed8..dc38e3bc 100644
--- a/list/list.go
+++ b/list/list.go
@@ -24,7 +24,7 @@ package list
import (
"github.com/charmbracelet/lipgloss"
- "github.com/charmbracelet/lipgloss/internal/tree"
+ "github.com/charmbracelet/lipgloss/tree"
)
// List represents a list of items that can be displayed. Lists can contain
diff --git a/list/list_test.go b/list/list_test.go
index d745f480..b5a0a947 100644
--- a/list/list_test.go
+++ b/list/list_test.go
@@ -7,8 +7,8 @@ import (
"github.com/aymanbagabas/go-udiff"
"github.com/charmbracelet/lipgloss"
- "github.com/charmbracelet/lipgloss/internal/tree"
"github.com/charmbracelet/lipgloss/list"
+ "github.com/charmbracelet/lipgloss/tree"
)
// XXX: can't write multi-line examples if the underlying string uses
diff --git a/internal/tree/children.go b/tree/children.go
similarity index 91%
rename from internal/tree/children.go
rename to tree/children.go
index 9cd4db8e..6727092a 100644
--- a/internal/tree/children.go
+++ b/tree/children.go
@@ -1,11 +1,11 @@
package tree
-// Children is the interface that wraps the basic methods of a list model.
+// Children is the interface that wraps the basic methods of a tree model.
type Children interface {
// At returns the content item of the given index.
At(index int) Node
- // Length returns the number of items in the list.
+ // Length returns the number of children in the tree.
Length() int
}
@@ -86,7 +86,7 @@ func (m *Filter) Filter(f func(index int) bool) *Filter {
return m
}
-// Length returns the number of items in the list.
+// Length returns the number of children in the tree.
func (m *Filter) Length() int {
j := 0
for i := 0; i < m.data.Length(); i++ {
diff --git a/internal/tree/enumerator.go b/tree/enumerator.go
similarity index 100%
rename from internal/tree/enumerator.go
rename to tree/enumerator.go
diff --git a/internal/tree/renderer.go b/tree/renderer.go
similarity index 92%
rename from internal/tree/renderer.go
rename to tree/renderer.go
index 3cca00bb..8fd86930 100644
--- a/internal/tree/renderer.go
+++ b/tree/renderer.go
@@ -6,13 +6,14 @@ import (
"github.com/charmbracelet/lipgloss"
)
-// StyleFunc allows the list to be styled per item.
+// StyleFunc allows the tree to be styled per item.
type StyleFunc func(children Children, i int) lipgloss.Style
-// Style is the styling applied to the list.
+// Style is the styling applied to the tree.
type Style struct {
enumeratorFunc StyleFunc
itemFunc StyleFunc
+ root lipgloss.Style
}
// newRenderer returns the renderer used to render a tree.
@@ -50,7 +51,7 @@ func (r *renderer) render(node Node, root bool, prefix string) string {
// print the root node name if its not empty.
if name := node.Value(); name != "" && root {
- strs = append(strs, r.style.itemFunc(children, -1).Render(name))
+ strs = append(strs, r.style.root.Render(name))
}
for i := 0; i < children.Length(); i++ {
@@ -82,14 +83,14 @@ func (r *renderer) render(node Node, root bool, prefix string) string {
// the current node's prefix have the same height.
for lipgloss.Height(item) > lipgloss.Height(nodePrefix) {
nodePrefix = lipgloss.JoinVertical(
- lipgloss.Top,
+ lipgloss.Left,
nodePrefix,
enumStyle.Render(indent),
)
}
for lipgloss.Height(nodePrefix) > lipgloss.Height(multineLinePrefix) {
multineLinePrefix = lipgloss.JoinVertical(
- lipgloss.Top,
+ lipgloss.Left,
multineLinePrefix,
prefix,
)
@@ -98,7 +99,7 @@ func (r *renderer) render(node Node, root bool, prefix string) string {
strs = append(
strs,
lipgloss.JoinHorizontal(
- lipgloss.Left,
+ lipgloss.Top,
multineLinePrefix,
nodePrefix,
item,
@@ -126,7 +127,7 @@ func (r *renderer) render(node Node, root bool, prefix string) string {
}
}
}
- return lipgloss.JoinVertical(lipgloss.Top, strs...)
+ return strings.Join(strs, "\n")
}
func max(a, b int) int {
diff --git a/tree/testdata/TestRootStyle.golden b/tree/testdata/TestRootStyle.golden
new file mode 100644
index 00000000..feacf4cd
--- /dev/null
+++ b/tree/testdata/TestRootStyle.golden
@@ -0,0 +1,3 @@
+[48;2;89;86;224mRoot[0m
+├── [48;2;4;181;117mFoo[0m
+└── [48;2;4;181;117mBaz[0m
\ No newline at end of file
diff --git a/internal/tree/tree.go b/tree/tree.go
similarity index 90%
rename from internal/tree/tree.go
rename to tree/tree.go
index 5c629af3..94f5dd08 100644
--- a/internal/tree/tree.go
+++ b/tree/tree.go
@@ -223,6 +223,12 @@ func (t *Tree) EnumeratorStyleFunc(fn StyleFunc) *Tree {
return t
}
+// RootStyle sets a style for the root element.
+func (t *Tree) RootStyle(style lipgloss.Style) *Tree {
+ t.ensureRenderer().style.root = style
+ return t
+}
+
// ItemStyle sets a static style for all items.
//
// Use ItemStyleFunc to conditionally set styles based on the tree node.
@@ -249,12 +255,12 @@ func (t *Tree) ItemStyleFunc(fn StyleFunc) *Tree {
return t
}
-// Enumerator sets the enumerator implementation. This can be used to change the way the branches indicators look.
-// Lipgloss includes predefined enumerators including bullets, roman numerals, and more. For
-// example, you can have a numbered list:
+// Enumerator sets the enumerator implementation. This can be used to change the
+// way the branches indicators look. Lipgloss includes predefined enumerators
+// for a classic or rounded tree. For example, you can have a rounded tree:
//
// tree.New().
-// Enumerator(Arabic)
+// Enumerator(RoundedEnumerator)
func (t *Tree) Enumerator(enum Enumerator) *Tree {
t.ensureRenderer().enumerator = enum
return t
@@ -302,13 +308,25 @@ func (t *Tree) Children() Children {
// It is a shorthand for:
//
// tree.New().Root(root)
-func Root(root string) *Tree {
- return New().Root(root)
+func Root(root any) *Tree {
+ t := New()
+ return t.Root(root)
}
// Root sets the root value of this tree.
-func (t *Tree) Root(root string) *Tree {
- t.value = root
+func (t *Tree) Root(root any) *Tree {
+ // root is a tree or string
+ switch item := root.(type) {
+ case *Tree:
+ t.value = item.value
+ t = t.Child(item.children)
+ case Node, fmt.Stringer:
+ t.value = item.(fmt.Stringer).String()
+ case string, nil:
+ t.value = item.(string)
+ default:
+ t.value = fmt.Sprintf("%v", item)
+ }
return t
}
diff --git a/internal/tree/tree_test.go b/tree/tree_test.go
similarity index 96%
rename from internal/tree/tree_test.go
rename to tree/tree_test.go
index 654b5b4f..3ee1fd34 100644
--- a/internal/tree/tree_test.go
+++ b/tree/tree_test.go
@@ -7,9 +7,11 @@ import (
"github.com/aymanbagabas/go-udiff"
"github.com/charmbracelet/lipgloss"
- "github.com/charmbracelet/lipgloss/internal/tree"
"github.com/charmbracelet/lipgloss/list"
"github.com/charmbracelet/lipgloss/table"
+ "github.com/charmbracelet/lipgloss/tree"
+ "github.com/charmbracelet/x/exp/golden"
+ "github.com/muesli/termenv"
)
func TestTree(t *testing.T) {
@@ -410,6 +412,20 @@ Root
assertEqual(t, want, tree.String())
}
+func TestRootStyle(t *testing.T) {
+ lipgloss.SetColorProfile(termenv.TrueColor)
+ tree := tree.New().
+ Root("Root").
+ Child(
+ "Foo",
+ "Baz",
+ ).
+ RootStyle(lipgloss.NewStyle().Background(lipgloss.Color("#5A56E0"))).
+ ItemStyle(lipgloss.NewStyle().Background(lipgloss.Color("#04B575")))
+
+ golden.RequireEqual(t, []byte(tree.String()))
+}
+
func TestAt(t *testing.T) {
data := tree.NewStringData("Foo", "Bar")
@@ -607,7 +623,7 @@ func TestMultilinePrefixSubtree(t *testing.T) {
├── Foo
├── Bar
├── Baz
-│ Foo Document
+│ Foo Document
│ The Foo Files
│
│ │ Bar Document