fx/print.go

278 lines
7.6 KiB
Go
Raw Normal View History

2022-03-11 15:15:24 +00:00
package main
import (
"fmt"
"strings"
)
2022-04-02 20:59:36 +00:00
func (m *model) connect(path string, lineNumber int) {
if _, exist := m.pathToLineNumber[path]; exist {
return
}
m.paths = append(m.paths, path)
m.pathToIndex[path] = len(m.paths) - 1
m.pathToLineNumber[path] = lineNumber
m.lineNumberToPath[lineNumber] = path
}
2022-03-29 16:13:41 +00:00
func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path string, selectableValues bool) []string {
2022-04-02 20:59:36 +00:00
m.connect(path, lineNumber)
2022-03-28 20:13:48 +00:00
ident := strings.Repeat(" ", level)
subident := strings.Repeat(" ", level-1)
2022-04-07 08:32:34 +00:00
highlight := m.highlightIndex[path]
var searchValue []*foundRange
if highlight != nil {
searchValue = highlight.value
}
2022-03-29 16:13:41 +00:00
2022-03-28 20:13:48 +00:00
switch v.(type) {
case nil:
2022-04-16 19:50:46 +00:00
return []string{merge(m.explode("null", searchValue, m.theme.null, path, selectableValues))}
2022-03-28 20:13:48 +00:00
case bool:
if v.(bool) {
2022-04-16 19:50:46 +00:00
return []string{merge(m.explode("true", searchValue, m.theme.boolean, path, selectableValues))}
2022-03-28 20:13:48 +00:00
} else {
2022-04-16 19:50:46 +00:00
return []string{merge(m.explode("false", searchValue, m.theme.boolean, path, selectableValues))}
2022-03-28 20:13:48 +00:00
}
2022-03-29 16:13:41 +00:00
case number:
2022-04-16 19:50:46 +00:00
return []string{merge(m.explode(v.(number).String(), searchValue, m.theme.number, path, selectableValues))}
2022-03-28 20:13:48 +00:00
case string:
2022-03-11 15:15:24 +00:00
line := fmt.Sprintf("%q", v)
2022-04-16 19:50:46 +00:00
chunks := m.explode(line, searchValue, m.theme.string, path, selectableValues)
2022-03-11 15:15:24 +00:00
if m.wrap && keyEndPos+width(line) > m.width {
2022-03-29 16:13:41 +00:00
return wrapLines(chunks, keyEndPos, m.width, subident)
2022-03-11 15:15:24 +00:00
}
2022-03-29 16:13:41 +00:00
// No wrap
return []string{merge(chunks)}
2022-03-11 15:15:24 +00:00
case *dict:
if !m.expandedPaths[path] {
2022-03-29 16:13:41 +00:00
return []string{m.preview(v, path, selectableValues)}
2022-03-11 15:15:24 +00:00
}
2022-04-07 08:32:34 +00:00
output := []string{m.printOpenBracket("{", highlight, path, selectableValues)}
2022-03-11 15:15:24 +00:00
lineNumber++ // bracket is on separate line
keys := v.(*dict).keys
for i, k := range keys {
subpath := path + "." + k
2022-04-07 08:32:34 +00:00
highlight := m.highlightIndex[subpath]
2022-04-14 12:30:36 +00:00
var keyRanges, delimRanges []*foundRange
2022-04-07 08:32:34 +00:00
if highlight != nil {
2022-04-14 12:30:36 +00:00
keyRanges = highlight.key
delimRanges = highlight.delim
2022-04-07 08:32:34 +00:00
}
2022-04-02 20:59:36 +00:00
m.connect(subpath, lineNumber)
2022-03-11 15:15:24 +00:00
key := fmt.Sprintf("%q", k)
2022-04-16 19:50:46 +00:00
keyTheme := m.theme.key(i, len(keys))
key = merge(m.explode(key, keyRanges, keyTheme, subpath, true))
2022-03-11 15:15:24 +00:00
value, _ := v.(*dict).get(k)
2022-04-16 19:50:46 +00:00
delim := merge(m.explode(": ", delimRanges, m.theme.syntax, subpath, false))
2022-03-11 15:15:24 +00:00
keyEndPos := width(ident) + width(key) + width(delim)
2022-03-29 16:13:41 +00:00
lines := m.print(value, level+1, lineNumber, keyEndPos, subpath, false)
2022-03-11 15:15:24 +00:00
lines[0] = ident + key + delim + lines[0]
if i < len(keys)-1 {
2022-04-07 08:32:34 +00:00
lines[len(lines)-1] += m.printComma(",", highlight)
2022-03-11 15:15:24 +00:00
}
output = append(output, lines...)
lineNumber += len(lines)
}
2022-04-07 08:32:34 +00:00
output = append(output, subident+m.printCloseBracket("}", highlight, path, false))
2022-03-11 15:15:24 +00:00
return output
case array:
if !m.expandedPaths[path] {
2022-03-29 16:13:41 +00:00
return []string{m.preview(v, path, selectableValues)}
2022-03-11 15:15:24 +00:00
}
2022-04-07 08:32:34 +00:00
output := []string{m.printOpenBracket("[", highlight, path, selectableValues)}
2022-03-11 15:15:24 +00:00
lineNumber++ // bracket is on separate line
slice := v.(array)
for i, value := range slice {
subpath := fmt.Sprintf("%v[%v]", path, i)
2022-04-07 08:32:34 +00:00
s := m.highlightIndex[subpath]
2022-04-02 20:59:36 +00:00
m.connect(subpath, lineNumber)
2022-03-29 16:13:41 +00:00
lines := m.print(value, level+1, lineNumber, width(ident), subpath, true)
2022-03-11 15:15:24 +00:00
lines[0] = ident + lines[0]
if i < len(slice)-1 {
2022-03-29 16:13:41 +00:00
lines[len(lines)-1] += m.printComma(",", s)
2022-03-11 15:15:24 +00:00
}
lineNumber += len(lines)
output = append(output, lines...)
}
2022-04-07 08:32:34 +00:00
output = append(output, subident+m.printCloseBracket("]", highlight, path, false))
2022-03-11 15:15:24 +00:00
return output
default:
return []string{"unknown type"}
}
}
2022-03-29 16:13:41 +00:00
func (m *model) preview(v interface{}, path string, selectableValues bool) string {
2022-04-07 08:32:34 +00:00
searchResult := m.highlightIndex[path]
2022-04-16 19:50:46 +00:00
previewStyle := m.theme.preview
2022-03-29 16:13:41 +00:00
if selectableValues && m.cursorPath() == path {
2022-04-16 19:50:46 +00:00
previewStyle = m.theme.cursor
2022-03-11 15:15:24 +00:00
}
printValue := func(value interface{}) string {
switch value.(type) {
2022-03-29 16:13:41 +00:00
case nil, bool, number:
2022-04-16 19:50:46 +00:00
return previewStyle(fmt.Sprintf("%v", value))
2022-03-11 15:15:24 +00:00
case string:
2022-04-16 19:50:46 +00:00
return previewStyle(fmt.Sprintf("%q", value))
2022-03-11 15:15:24 +00:00
case *dict:
2022-04-16 19:50:46 +00:00
return previewStyle("{\u2026}")
2022-03-11 15:15:24 +00:00
case array:
2022-04-16 19:50:46 +00:00
return previewStyle("[\u2026]")
2022-03-11 15:15:24 +00:00
}
return "..."
}
switch v.(type) {
case *dict:
2022-03-29 16:13:41 +00:00
output := m.printOpenBracket("{", searchResult, path, selectableValues)
2022-03-11 15:15:24 +00:00
keys := v.(*dict).keys
for _, k := range keys {
key := fmt.Sprintf("%q", k)
2022-04-16 19:50:46 +00:00
output += previewStyle(key + ": ")
2022-03-11 15:15:24 +00:00
value, _ := v.(*dict).get(k)
output += printValue(value)
break
}
2022-03-29 16:13:41 +00:00
if len(keys) > 1 {
2022-04-16 19:50:46 +00:00
output += previewStyle(", \u2026")
2022-03-11 15:15:24 +00:00
}
2022-03-29 16:13:41 +00:00
output += m.printCloseBracket("}", searchResult, path, selectableValues)
2022-03-11 15:15:24 +00:00
return output
case array:
2022-03-29 16:13:41 +00:00
output := m.printOpenBracket("[", searchResult, path, selectableValues)
2022-03-11 15:15:24 +00:00
slice := v.(array)
for _, value := range slice {
output += printValue(value)
break
}
2022-03-29 16:13:41 +00:00
if len(slice) > 1 {
2022-04-16 19:50:46 +00:00
output += previewStyle(", \u2026")
2022-03-11 15:15:24 +00:00
}
2022-03-29 16:13:41 +00:00
output += m.printCloseBracket("]", searchResult, path, selectableValues)
2022-03-11 15:15:24 +00:00
return output
}
return "?"
}
2022-03-29 16:13:41 +00:00
func wrapLines(chunks []withStyle, keyEndPos, mWidth int, subident string) []string {
2022-03-11 15:15:24 +00:00
wrappedLines := make([]string, 0)
currentLine := ""
ident := "" // First line stays on the same line with a "key",
pos := keyEndPos // so no ident is needed. Start counting from the "key" offset.
2022-03-29 16:13:41 +00:00
for _, chunk := range chunks {
2022-03-11 15:15:24 +00:00
buffer := ""
2022-03-29 16:13:41 +00:00
for _, ch := range chunk.value {
2022-03-11 15:15:24 +00:00
buffer += string(ch)
if pos == mWidth-1 {
2022-03-29 16:13:41 +00:00
wrappedLines = append(wrappedLines, ident+currentLine+chunk.Render(buffer))
2022-03-11 15:15:24 +00:00
currentLine = ""
buffer = ""
pos = width(subident) // Start counting from ident.
ident = subident // After first line, add ident to all.
} else {
pos++
}
}
2022-03-29 16:13:41 +00:00
currentLine += chunk.Render(buffer)
2022-03-11 15:15:24 +00:00
}
if width(currentLine) > 0 {
wrappedLines = append(wrappedLines, subident+currentLine)
}
return wrappedLines
}
2022-03-29 16:13:41 +00:00
func (w withStyle) Render(s string) string {
2022-04-16 19:50:46 +00:00
return w.style(s)
2022-03-29 16:13:41 +00:00
}
2022-04-07 08:32:34 +00:00
func (m *model) printOpenBracket(line string, s *rangeGroup, path string, selectableValues bool) string {
2022-03-29 16:13:41 +00:00
if selectableValues && m.cursorPath() == path {
2022-04-16 19:50:46 +00:00
return m.theme.cursor(line)
2022-03-29 16:13:41 +00:00
}
2022-04-07 08:32:34 +00:00
if s != nil && s.openBracket != nil {
if s.openBracket.parent.index == m.searchResultsCursor {
2022-04-16 19:50:46 +00:00
return m.theme.cursor(line)
2022-03-29 16:13:41 +00:00
} else {
2022-04-16 19:50:46 +00:00
return m.theme.search(line)
2022-03-11 15:15:24 +00:00
}
2022-03-29 16:13:41 +00:00
} else {
2022-04-16 19:50:46 +00:00
return m.theme.syntax(line)
2022-03-11 15:15:24 +00:00
}
}
2022-04-07 08:32:34 +00:00
func (m *model) printCloseBracket(line string, s *rangeGroup, path string, selectableValues bool) string {
2022-03-29 16:13:41 +00:00
if selectableValues && m.cursorPath() == path {
2022-04-16 19:50:46 +00:00
return m.theme.cursor(line)
2022-03-29 16:13:41 +00:00
}
2022-04-07 08:32:34 +00:00
if s != nil && s.closeBracket != nil {
if s.closeBracket.parent.index == m.searchResultsCursor {
2022-04-16 19:50:46 +00:00
return m.theme.cursor(line)
2022-03-29 16:13:41 +00:00
} else {
2022-04-16 19:50:46 +00:00
return m.theme.search(line)
2022-03-29 16:13:41 +00:00
}
} else {
2022-04-16 19:50:46 +00:00
return m.theme.syntax(line)
2022-03-11 15:15:24 +00:00
}
}
2022-04-07 08:32:34 +00:00
func (m *model) printComma(line string, s *rangeGroup) string {
if s != nil && s.comma != nil {
if s.comma.parent.index == m.searchResultsCursor {
2022-04-16 19:50:46 +00:00
return m.theme.cursor(line)
2022-03-29 16:13:41 +00:00
} else {
2022-04-16 19:50:46 +00:00
return m.theme.search(line)
2022-03-29 16:13:41 +00:00
}
} else {
2022-04-16 19:50:46 +00:00
return m.theme.syntax(line)
2022-03-29 16:13:41 +00:00
}
}
type withStyle struct {
value string
2022-04-16 19:50:46 +00:00
style Color
2022-03-29 16:13:41 +00:00
}
2022-04-16 19:50:46 +00:00
func (m *model) explode(line string, highlightRanges []*foundRange, defaultStyle Color, path string, selectable bool) []withStyle {
2022-04-14 12:30:36 +00:00
if selectable && m.cursorPath() == path && m.showCursor {
2022-04-16 19:50:46 +00:00
return []withStyle{{line, m.theme.cursor}}
2022-03-29 16:13:41 +00:00
}
out := make([]withStyle, 0, 1)
pos := 0
2022-04-07 08:32:34 +00:00
for _, r := range highlightRanges {
2022-04-16 19:50:46 +00:00
style := m.theme.search
2022-04-07 08:32:34 +00:00
if r.parent.index == m.searchResultsCursor {
2022-04-16 19:50:46 +00:00
style = m.theme.cursor
2022-03-29 16:13:41 +00:00
}
out = append(out, withStyle{
2022-04-07 08:32:34 +00:00
value: line[pos:r.start],
2022-03-29 16:13:41 +00:00
style: defaultStyle,
})
out = append(out, withStyle{
2022-04-07 08:32:34 +00:00
value: line[r.start:r.end],
2022-03-29 16:13:41 +00:00
style: style,
})
2022-04-07 08:32:34 +00:00
pos = r.end
2022-03-29 16:13:41 +00:00
}
out = append(out, withStyle{
value: line[pos:],
style: defaultStyle,
})
return out
}
func merge(chunks []withStyle) string {
out := ""
for _, chunk := range chunks {
out += chunk.Render(chunk.value)
}
return out
2022-03-11 15:15:24 +00:00
}