Codebase list golang-github-xlab-treeprint / d235141
Add support for values with multiple lines Grzegorz Dlugoszewski authored 3 years ago Maxim committed 3 years ago
2 changed file(s) with 100 addition(s) and 3 deletion(s). Raw diff Collapse all Expand all
138138 edge = EdgeTypeEnd
139139 levelsEnded = append(levelsEnded, level)
140140 }
141 printValues(buf, 0, levelsEnded, edge, n.Meta, n.Value)
141 printValues(buf, 0, levelsEnded, edge, n)
142142 }
143143 if len(n.Nodes) > 0 {
144144 printNodes(buf, level, levelsEnded, n.Nodes)
167167 levelsEnded = append(levelsEnded, level)
168168 edge = EdgeTypeEnd
169169 }
170 printValues(wr, level, levelsEnded, edge, node.Meta, node.Value)
170 printValues(wr, level, levelsEnded, edge, node)
171171 if len(node.Nodes) > 0 {
172172 printNodes(wr, level+1, levelsEnded, node.Nodes)
173173 }
175175 }
176176
177177 func printValues(wr io.Writer,
178 level int, levelsEnded []int, edge EdgeType, meta MetaValue, val Value) {
178 level int, levelsEnded []int, edge EdgeType, node *node) {
179179
180180 for i := 0; i < level; i++ {
181181 if isEnded(levelsEnded, i) {
184184 }
185185 fmt.Fprintf(wr, "%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
186186 }
187
188 val := renderValue(level, node)
189 meta := node.Meta
190
187191 if meta != nil {
188192 fmt.Fprintf(wr, "%s [%v] %v\n", edge, meta, val)
189193 return
198202 }
199203 }
200204 return false
205 }
206
207 func renderValue(level int, node *node) Value {
208 lines := strings.Split(fmt.Sprintf("%v", node.Value), "\n")
209
210 // If value does not contain multiple lines, return itself.
211 if len(lines) < 2 {
212 return node.Value
213 }
214
215 // If value contains multiple lines,
216 // generate a padding and prefix each line with it.
217 pad := padding(level, node)
218
219 for i := 1; i < len(lines); i++ {
220 lines[i] = fmt.Sprintf("%s%s", pad, lines[i])
221 }
222
223 return strings.Join(lines, "\n")
224 }
225
226 // padding returns a padding for the multiline values with correctly placed link edges.
227 // It is generated by traversing the tree upwards (from leaf to the root of the tree)
228 // and, on each level, checking if the node the last one of its siblings.
229 // If a node is the last one, the padding on that level should be empty (there's nothing to link to below it).
230 // If a node is not the last one, the padding on that level should be the link edge so the sibling below is correctly connected.
231 func padding(level int, node *node) string {
232 links := make([]string, level+1)
233
234 for node.Root != nil {
235 if isLast(node) {
236 links[level] = strings.Repeat(" ", IndentSize+1)
237 } else {
238 links[level] = fmt.Sprintf("%s%s", EdgeTypeLink, strings.Repeat(" ", IndentSize))
239 }
240 level--
241 node = node.Root
242 }
243
244 return strings.Join(links, "")
245 }
246
247 // isLast checks if the node is the last one in the slice of its parent children
248 func isLast(n *node) bool {
249 return n == n.Root.FindLastNode()
201250 }
202251
203252 type EdgeType string
213213 assert.Equal(treeNode, treeNode.Nodes[0].Root)
214214 assert.Equal(treeNode.Nodes[0], treeNode.Nodes[0].Nodes[0].Root)
215215 }
216
217 func TestMultiline(t *testing.T) {
218 assert := assert.New(t)
219
220 multi1 := `I am
221 a multiline
222 value`
223
224 multi2 := `I have
225 many
226
227
228 empty lines`
229
230 multi3 := `I am another
231 multiple
232 lines value`
233
234 tree := New()
235 tree.AddBranch("one").AddMetaNode("meta", multi1)
236 tree.AddBranch("two")
237 foo := tree.AddBranch("foo")
238 foo.AddBranch("bar").AddNode("a").AddNode(multi2).AddNode("c")
239 foo.AddBranch(multi3)
240
241 actual := tree.String()
242 expected := `.
243 ├── one
244 │ └── [meta] I am
245 │ a multiline
246 │ value
247 ├── two
248 └── foo
249 ├── bar
250 │ ├── a
251 │ ├── I have
252 │ │ many
253 │ │
254 │ │
255 │ │ empty lines
256 │ └── c
257 └── I am another
258 multiple
259 lines value
260 `
261
262 assert.Equal(expected, actual)
263 }