add wrap logic

pull/268/head
Anton Medvedev 9 months ago
parent 13300f9fbd
commit a93c9cbc3f
No known key found for this signature in database

@ -67,6 +67,9 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case tea.WindowSizeMsg:
m.termWidth = msg.Width
m.termHeight = msg.Height
if m.termWidth > 0 {
wrapAll(m.top, m.termWidth)
}
case tea.MouseMsg:
switch msg.Type {
@ -235,11 +238,7 @@ func (m *model) View() string {
var screen []byte
n := m.head
colon := currentTheme.Syntax([]byte{':', ' '})
comma := currentTheme.Syntax([]byte{','})
empty := currentTheme.Preview([]byte{'~'})
printedLines := 0
for i := 0; i < m.viewHeight(); i++ {
if n == nil {
break
@ -247,6 +246,13 @@ func (m *model) View() string {
for ident := 0; ident < int(n.depth); ident++ {
screen = append(screen, ' ', ' ')
}
valueOrChunk := n.value
if n.chunk != nil {
valueOrChunk = n.chunk
}
selected := m.cursor == i
if n.key != nil {
keyColor := currentTheme.Key
if m.cursor == i {
@ -254,20 +260,34 @@ func (m *model) View() string {
}
screen = append(screen, keyColor(n.key)...)
screen = append(screen, colon...)
screen = append(screen, colorForValue(n.value)(n.value)...)
} else {
colorize := colorForValue(n.value)
if m.cursor == i {
colorize = currentTheme.Cursor
}
screen = append(screen, colorize(n.value)...)
selected = false // don't highlight the key's value
}
if n.isCollapsed() {
screen = append(screen, currentTheme.Preview([]byte("…"))...)
if !n.isCollapsed() {
screen = append(screen, prettyPrint(valueOrChunk, selected, n.chunk != nil)...)
} else {
if n.value[0] == '{' {
screen = append(screen, currentTheme.Syntax([]byte{'}'})...)
screen = append(screen, prettyPrint(valueOrChunk, selected, n.chunk != nil)...)
screen = append(screen, dot3...)
screen = append(screen, closeCurlyBracket...)
} else if n.value[0] == '[' {
screen = append(screen, currentTheme.Syntax([]byte{']'})...)
screen = append(screen, prettyPrint(valueOrChunk, selected, n.chunk != nil)...)
screen = append(screen, dot3...)
screen = append(screen, closeSquareBracket...)
} else if n.value[0] == '"' {
suffix := 2
if n.comma {
suffix++
}
offset := len(n.chunk) - suffix
if offset < 0 {
offset = 0
}
screen = append(screen, prettyPrint(valueOrChunk[:offset], selected, n.chunk != nil)...)
screen = append(screen, dot3...)
screen = append(screen, doubleQuotationMark...)
} else {
screen = append(screen, dot3...)
}
}
if n.comma {

@ -8,6 +8,7 @@ type node struct {
depth uint8
key []byte
value []byte
chunk []byte
comma bool
}
@ -24,6 +25,43 @@ func (n *node) append(child *node) {
}
}
func (n *node) insertChild(child *node) {
if n.end == nil {
n.insertAfter(child)
} else {
n.end.insertAfter(child)
}
n.end = child
}
func (n *node) insertAfter(child *node) {
if n.next == nil {
n.next = child
child.prev = n
} else {
old := n.next
n.next = child
child.prev = n
child.next = old
old.prev = child
}
}
func (n *node) dropChunks() {
if n.end == nil {
return
}
n.chunk = nil
n.next = n.end.next
if n.next != nil {
n.next.prev = n
}
n.end = nil
}
func (n *node) hasChildren() bool {
return n.end != nil
}

@ -29,6 +29,14 @@ func init() {
themeId = "1"
}
currentTheme = themes[themeId]
colon = currentTheme.Syntax([]byte{':', ' '})
comma = currentTheme.Syntax([]byte{','})
empty = currentTheme.Preview([]byte{'~'})
dot3 = currentTheme.Preview([]byte("…"))
closeCurlyBracket = currentTheme.Syntax([]byte{'}'})
closeSquareBracket = currentTheme.Syntax([]byte{']'})
doubleQuotationMark = currentTheme.String([]byte{'"'})
}
var (
@ -40,6 +48,16 @@ var (
defaultNull = fg("8")
)
var (
colon []byte
comma []byte
empty []byte
dot3 []byte
closeCurlyBracket []byte
closeSquareBracket []byte
doubleQuotationMark []byte
)
var themes = map[string]theme{
"0": {
Cursor: defaultCursor,

@ -1,9 +1,5 @@
package main
import (
"github.com/charmbracelet/lipgloss"
)
func isHexDigit(ch byte) bool {
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
}
@ -12,28 +8,30 @@ func isDigit(ch byte) bool {
return ch >= '0' && ch <= '9'
}
func colorForValue(b []byte) color {
func prettyPrint(b []byte, selected bool, isChunk bool) []byte {
if len(b) == 0 {
return noColor
return b
}
switch b[0] {
case '"':
return currentTheme.String
case 't', 'f':
return currentTheme.Boolean
case 'n':
return currentTheme.Null
case '{', '[', '}', ']':
return currentTheme.Syntax
default:
if isDigit(b[0]) || b[0] == '-' {
return currentTheme.Number
if selected {
return currentTheme.Cursor(b)
} else {
if isChunk {
return currentTheme.String(b)
}
switch b[0] {
case '"':
return currentTheme.String(b)
case 't', 'f':
return currentTheme.Boolean(b)
case 'n':
return currentTheme.Null(b)
case '{', '[', '}', ']':
return currentTheme.Syntax(b)
default:
if isDigit(b[0]) || b[0] == '-' {
return currentTheme.Number(b)
}
return noColor(b)
}
return noColor
}
}
func width(s string) int {
return lipgloss.Width(s)
}

@ -0,0 +1,80 @@
package main
import (
"unicode/utf8"
"github.com/mattn/go-runewidth"
)
func wrapAll(n *node, termWidth int) {
for n != nil {
if n.value != nil && n.value[0] == '"' {
collapsed := n.isCollapsed()
if collapsed {
n.collapsed = nil
}
n.dropChunks()
lines, count := doWrap(n, termWidth)
if count > 1 {
n.chunk = lines[0]
for i := 1; i < count; i++ {
child := &node{
directParent: n,
depth: n.depth,
chunk: lines[i],
}
if n.comma && i == count-1 {
child.comma = true
}
n.insertChild(child)
}
}
if collapsed {
n.collapse()
}
}
n = n.next
}
}
func doWrap(n *node, termWidth int) ([][]byte, int) {
lines := make([][]byte, 0, 1)
width := int(n.depth) * 2
if n.key != nil {
for _, ch := range string(n.key) {
width += runewidth.RuneWidth(ch)
}
width += 2 // for ": "
}
linesCount := 0
start, end := 0, 0
b := n.value
for len(b) > 0 {
r, size := utf8.DecodeRune(b)
w := runewidth.RuneWidth(r)
if width+w > termWidth {
if linesCount == 0 {
lines = append(lines, n.value[start:end])
} else {
lines = append(lines, n.value[start:end])
}
start = end
width = int(n.depth) * 2
linesCount++
} else {
width += w
}
end += size
b = b[size:]
}
if start < end {
lines = append(lines, n.value[start:])
linesCount++
}
return lines, linesCount
}
Loading…
Cancel
Save