mirror of
https://github.com/charmbracelet/lipgloss.git
synced 2024-09-11 14:07:21 +03:00
* feat: implement list renderer * feat: data model for list * feat: add Offset, Height, Indent * feat: tree renderer * fix: list example * test: last tree node is a subtree Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: tree Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: lint Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * docs: example Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * test: inc cov Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: multiline items Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * feat: one approach to sublists Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: tree improvements * wip Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * wip Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * wip Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * wip Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: prevent having to pass renderer to all trees * test: force linux line endings * fix: windows * fix: lint issues * fix: ignore lint issue failing forever * fix: renames * fix: renderer * ci: fix coveralls hopefully * wip Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: style * docs: update Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: renderer Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * test: cover with tests Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * feat: more list enumerators * fix: drop renderer api Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * feat: improve api Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: prealloc * fix: clean up * fix: list setup * fix: improve sublist tests * fix: improve sublists * refactor: simplify * docs: more examples, readme updates Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * docs: readme * fix: set item style * fix(tree): do not use golden files * test: do not use golden files * refactor: remove unused code * feat: hide nodes * feat: filter * fix: allow fmt.Stringer * test: table within a tree * docs: update * fix: rename atter to data * test: test public api only * docs: sublist with table example * docs: update example * fix: lint issues Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * docs: update examples * docs: update * feat: offset * feat: offsetstart && offsetend * fix: remove last from enumerator * refactor: make tree more explicit * `New()` now take no args * added a new `Root(string)` method * added a new `Items(...any)` method * refactor: improve list * docs: fix examples * test: ensure embed lists in trees * docs: sublist example with list as tree node Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * refactor: first pass at examples cleanup * refactor: first pass at examples cleanup * fix: support multi-line prefixes * test: more glow-style lists within trees * docs: simplify examples * fix: improve handling of different types * chore: go mod tidy Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: code review suggestions Co-authored-by: Ayman Bagabas <ayman.bagabas@gmail.com> Co-authored-by: bashbunni <15822994+bashbunni@users.noreply.github.com> * fix: apply code review suggestion * feat(tree): rounded enumerator (#280) * chore(examples): go mod tidy * feat(tree): add rounded corner enumerator * test: rounded enumerator test * docs: readme updates Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * docs: readme updates Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * chore: fmt Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * docs: godoc * docs: godoc * fix: bash's issue * fix: bad example Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * docs: improve godoc for trees and lists (#296) * docs(godoc): add overview * docs(godoc): include examples in godoc * docs(godoc): fix roman numerals example * docs(godoc): fix tree examples * docs(godoc): attempt to fix list Enumerator examples * docs: added some examples to godoc * fix(list): New(items...) with sublist * docs: update examples * chore(tree): improve var names in rounded example * fix: use padding instead of margin to better styles * fix: lint --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Maas Lalani <maas@lalani.dev> Co-authored-by: Ayman Bagabas <ayman.bagabas@gmail.com> Co-authored-by: bashbunni <15822994+bashbunni@users.noreply.github.com> Co-authored-by: Christian Rocha <christian@rocha.is>
134 lines
3.1 KiB
Go
134 lines
3.1 KiB
Go
package tree
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
)
|
|
|
|
// StyleFunc allows the list to be styled per item.
|
|
type StyleFunc func(data Data, i int) lipgloss.Style
|
|
|
|
// Style is the styling applied to the list.
|
|
type Style struct {
|
|
enumeratorFunc StyleFunc
|
|
itemFunc StyleFunc
|
|
}
|
|
|
|
// newRenderer returns the renderer used to render a tree.
|
|
func newRenderer() *renderer {
|
|
return &renderer{
|
|
style: Style{
|
|
enumeratorFunc: func(Data, int) lipgloss.Style {
|
|
return lipgloss.NewStyle().PaddingRight(1)
|
|
},
|
|
itemFunc: func(Data, int) lipgloss.Style {
|
|
return lipgloss.NewStyle()
|
|
},
|
|
},
|
|
enumerator: DefaultEnumerator,
|
|
}
|
|
}
|
|
|
|
type renderer struct {
|
|
style Style
|
|
enumerator Enumerator
|
|
}
|
|
|
|
// render is responsible for actually rendering the tree.
|
|
func (r *renderer) render(node Node, root bool, prefix string) string {
|
|
if node.Hidden() {
|
|
return ""
|
|
}
|
|
var strs []string
|
|
var maxLen int
|
|
children := node.Children()
|
|
enumerator := r.enumerator
|
|
|
|
// print the root node name if its not empty.
|
|
if name := node.Name(); name != "" && root {
|
|
strs = append(strs, r.style.itemFunc(children, -1).Render(name))
|
|
}
|
|
|
|
for i := 0; i < children.Length(); i++ {
|
|
_, prefix := enumerator(children, i)
|
|
prefix = r.style.enumeratorFunc(children, i).Render(prefix)
|
|
maxLen = max(lipgloss.Width(prefix), maxLen)
|
|
}
|
|
|
|
for i := 0; i < children.Length(); i++ {
|
|
child := children.At(i)
|
|
if child.Hidden() {
|
|
continue
|
|
}
|
|
indent, nodePrefix := enumerator(children, i)
|
|
enumStyle := r.style.enumeratorFunc(children, i)
|
|
itemStyle := r.style.itemFunc(children, i)
|
|
|
|
nodePrefix = enumStyle.Render(nodePrefix)
|
|
if l := maxLen - lipgloss.Width(nodePrefix); l > 0 {
|
|
nodePrefix = strings.Repeat(" ", l) + nodePrefix
|
|
}
|
|
|
|
item := itemStyle.Render(child.Name())
|
|
multineLinePrefix := prefix
|
|
|
|
// This dance below is to account for multiline prefixes, e.g. "|\n|".
|
|
// In that case, we need to make sure that both the parent prefix and
|
|
// the current node's prefix have the same height.
|
|
for lipgloss.Height(item) > lipgloss.Height(nodePrefix) {
|
|
nodePrefix = lipgloss.JoinVertical(
|
|
lipgloss.Top,
|
|
nodePrefix,
|
|
enumStyle.Render(indent),
|
|
)
|
|
}
|
|
for lipgloss.Height(nodePrefix) > lipgloss.Height(multineLinePrefix) {
|
|
multineLinePrefix = lipgloss.JoinVertical(
|
|
lipgloss.Top,
|
|
multineLinePrefix,
|
|
prefix,
|
|
)
|
|
}
|
|
|
|
strs = append(
|
|
strs,
|
|
lipgloss.JoinHorizontal(
|
|
lipgloss.Left,
|
|
multineLinePrefix,
|
|
nodePrefix,
|
|
item,
|
|
),
|
|
)
|
|
|
|
if children.Length() > 0 {
|
|
// here we see if the child has a custom renderer, which means the
|
|
// user set a custom enumerator, style, etc.
|
|
// if it has one, we'll use it to render itself.
|
|
// otherwise, we keep using the current renderer.
|
|
renderer := r
|
|
switch child := child.(type) {
|
|
case *Tree:
|
|
if child.renderer != nil {
|
|
renderer = child.renderer
|
|
}
|
|
}
|
|
if s := renderer.render(
|
|
child,
|
|
false,
|
|
prefix+enumStyle.Render(indent),
|
|
); s != "" {
|
|
strs = append(strs, s)
|
|
}
|
|
}
|
|
}
|
|
return lipgloss.JoinVertical(lipgloss.Top, strs...)
|
|
}
|
|
|
|
func max(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|