File: render.go

package info (click to toggle)
golang-github-jedib0t-go-pretty 6.2.4-1~bpo11%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye-backports
  • size: 1,168 kB
  • sloc: makefile: 31; sh: 14
file content (110 lines) | stat: -rw-r--r-- 3,110 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package list

import (
	"strings"
	"unicode/utf8"
)

// Render renders the List in a human-readable "pretty" format. Example:
//  * Game Of Thrones
//    * Winter
//    * Is
//    * Coming
//      * This
//      * Is
//      * Known
//  * The Dark Tower
//    * The Gunslinger
func (l *List) Render() string {
	l.initForRender()

	var out strings.Builder
	out.Grow(l.approxSize)
	for idx, item := range l.items {
		hint := renderHint{
			isTopItem:    bool(idx == 0),
			isFirstItem:  bool(idx == 0 || item.Level > l.items[idx-1].Level),
			isLastItem:   !l.hasMoreItemsInLevel(item.Level, idx),
			isBottomItem: bool(idx == len(l.items)-1),
		}
		if hint.isFirstItem && hint.isLastItem {
			hint.isOnlyItem = true
		}
		l.renderItem(&out, idx, item, hint)
	}
	return l.render(&out)
}

func (l *List) renderItem(out *strings.Builder, idx int, item *listItem, hint renderHint) {
	// when working on item number 2 or more, render a newline first
	if idx > 0 {
		out.WriteRune('\n')
	}

	// format item.Text as directed in l.style
	itemStr := l.style.Format.Apply(item.Text)

	// convert newlines if newlines are not "\n" in l.style
	if strings.Contains(itemStr, "\n") && l.style.CharNewline != "\n" {
		itemStr = strings.Replace(itemStr, "\n", l.style.CharNewline, -1)
	}

	// render the item.Text line by line
	for lineIdx, lineStr := range strings.Split(itemStr, "\n") {
		if lineIdx > 0 {
			out.WriteRune('\n')
		}

		// render the prefix or the leading text before the actual item
		l.renderItemBulletPrefix(out, idx, item.Level, lineIdx, hint)
		l.renderItemBullet(out, idx, item.Level, lineIdx, hint)

		// render the actual item
		out.WriteString(lineStr)
	}
}

func (l *List) renderItemBullet(out *strings.Builder, itemIdx int, itemLevel int, lineIdx int, hint renderHint) {
	if lineIdx > 0 {
		// multi-line item.Text
		if hint.isLastItem {
			out.WriteString(strings.Repeat(" ", utf8.RuneCountInString(l.style.CharItemVertical)))
		} else {
			out.WriteString(l.style.CharItemVertical)
		}
	} else {
		// single-line item.Text (or first line of a multi-line item.Text)
		if hint.isOnlyItem {
			if hint.isTopItem {
				out.WriteString(l.style.CharItemSingle)
			} else {
				out.WriteString(l.style.CharItemBottom)
			}
		} else if hint.isTopItem {
			out.WriteString(l.style.CharItemTop)
		} else if hint.isFirstItem {
			out.WriteString(l.style.CharItemFirst)
		} else if hint.isBottomItem || hint.isLastItem {
			out.WriteString(l.style.CharItemBottom)
		} else {
			out.WriteString(l.style.CharItemMiddle)
		}
		out.WriteRune(' ')
	}
}

func (l *List) renderItemBulletPrefix(out *strings.Builder, itemIdx int, itemLevel int, lineIdx int, hint renderHint) {
	// write a prefix if one has been set in l.style
	if l.style.LinePrefix != "" {
		out.WriteString(l.style.LinePrefix)
	}

	// render spaces and connectors until the item's position
	for levelIdx := 0; levelIdx < itemLevel; levelIdx++ {
		if l.hasMoreItemsInLevel(levelIdx, itemIdx) {
			out.WriteString(l.style.CharItemVertical)
		} else {
			out.WriteString(strings.Repeat(" ", utf8.RuneCountInString(l.style.CharItemVertical)))
		}
	}
}