cat / 012
clone

a spreadsheet for the terminal: lotus 1-2-3 looks, google sheets keys and formulas, one go binary

Grid: borders drawn from a frame's cache; a laid-out stress shape

Dami committed 2def060
Borders draw the same few strings in the same roles over and over, so a
frame keeps what it has drawn in each role (Model.paint) instead of
rendering a glyph per cell, and a line down a cell's padding replaces
the space without cutting the styled text. stress.Laidout is a report
of 8192 rows with every cell bordered, a column of notes wrapping over
three lines and a merged title: BenchmarkView and BenchmarkFrame draw it
as laidout-8192x26.
commit
2def060eab32372cf7300c568f0cb9bb539ff7cb
parent
b771362b1a76ea0d4d9e4a8c68cced977821fac3
author
Dami <dami.etoile@gmail.com>
5 files changed, +66 -13
Minternal/stress/gen.go26++++++++++++++++++
Minternal/ui/gridlines.go43+++++++++++++++++++++++-------
Minternal/ui/model.go8+++--
Minternal/ui/panel.go1+
Minternal/ui/stress_test.go1+
M internal/stress/gen.go +26 -0
M internal/ui/gridlines.go +33 -10
@@ -3,6 +3,7 @@ package ui
33 import (44 "strings"55 6+ "charm.land/lipgloss/v2"67 "github.com/charmbracelet/x/ansi"78 89 "github.com/FelineStateMachine/012/internal/sheet"
@@ -156,7 +157,31 @@ func (m *Model) leftEdge(a sheet.Addr, text string) string {
156157 if e == sheet.LineNone {157158 return text158159 }159- return m.th.CellBorder.Render(theme.Junction(e, e, sheet.LineNone, sheet.LineNone)) + ansi.Cut(text, 1, m.sheet.ColWidth(a.Col))160+ glyph := m.paint(&m.th.CellBorder, theme.Junction(e, e, sheet.LineNone, sheet.LineNone))161+ if strings.HasPrefix(text, " ") { // padding, as it mostly is162+ return glyph + text[1:]163+ }164+ return glyph + ansi.Cut(text, 1, m.sheet.ColWidth(a.Col))165+}166+167+// paintKey is a text drawn in a role.168+type paintKey struct {169+ role *lipgloss.Style170+ text string171+}172+173+// paint draws text in role, as role.Render does, keeping what it drew174+// until the next frame: borders draw the same few strings over and over.175+func (m *Model) paint(role *lipgloss.Style, text string) string {176+ if s, ok := m.painted[paintKey{role, text}]; ok {177+ return s178+ }179+ if m.painted == nil {180+ m.painted = map[paintKey]string{}181+ }182+ s := role.Render(text)183+ m.painted[paintKey{role, text}] = s184+ return s160185 }161186 162187 // ruleLine draws the border line above row: each column's top edge,
@@ -180,35 +205,33 @@ func (m *Model) ruleLine(row int) string {
180205 181206 // ruleCols draws the rule line of ncols columns from first.182207 func (m *Model) ruleCols(b *strings.Builder, lc *lineCtx, above, first, ncols int) {208+ left := sheet.LineNone // the edge of the column before, on the screen183209 for c := first; c < first+ncols; c++ {184210 a := sheet.Addr{Col: c, Row: lc.row}185211 w := m.sheet.ColWidth(c)186- style, colored := m.th.CellBorder, false212+ style, colored := &m.th.CellBorder, false187213 if lc.selecting && lc.sel.Contains(a) && lc.sel.Contains(sheet.Addr{Col: c, Row: above}) {188- style, colored = m.th.Selection, true214+ style, colored = &m.th.Selection, true189215 }190216 mg, inMerge := lc.mergeOf(c)191217 inside := inMerge && lc.row > mg.From.Row192218 if inside && mg.From == lc.focus {193- style, colored = m.th.Pointer, true219+ style, colored = &m.th.Pointer, true194220 }195221 h := m.hEdge(a)196- left := sheet.LineNone197- if c > first {198- left = m.hEdge(sheet.Addr{Col: c - 1, Row: lc.row})199- }200222 joint := theme.Junction(m.vEdge(sheet.Addr{Col: c, Row: above}), m.vEdge(a), left, h)201223 if inside && c > mg.From.Col {202224 joint = " "203225 }226+ left = h204227 run := strings.Repeat(theme.Junction(sheet.LineNone, sheet.LineNone, h, h), w-1)205228 switch {206229 case !colored && joint == " " && h == sheet.LineNone:207230 b.WriteString(strings.Repeat(" ", w))208231 case joint == " ":209- b.WriteString(style.Render(joint + run))232+ b.WriteString(m.paint(style, joint+run))210233 default: // the joint in the border's role, as leftEdge draws the lines through it211- b.WriteString(m.th.CellBorder.Render(joint) + style.Render(run))234+ b.WriteString(m.paint(&m.th.CellBorder, joint) + m.paint(style, run))212235 }213236 }214237 }
M internal/ui/model.go +5 -3
M internal/ui/panel.go +1 -0
M internal/ui/stress_test.go +1 -0