Row numbers widen past row 9999 as Excel's do, to fit the widest number on screen. Stepping over rows the filter hides, and laying out text that runs in from other columns, go through the sheet's indexes instead of testing every row or column between; text runs in from at most 256 columns away. Styles shown come from the cell, its row or its column. The stress shapes keep their 8192 x 256 size (stress.Rows, stress.Cols) so runs compare across the change.
- commit
- 08ee498c8d16e719404833293a7b3023c0e084dd
- parent
- 1c459ce04e8eeda23b0cb4e64fee22ebf4ca5219
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 2 | +- |
| M | internal/ | 29 | +++++++++++------- |
| M | internal/ | 8 | +++--- |
| M | internal/ | 4 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 5 | +--- |
| M | internal/ | 4 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 4 | +- |
| M | internal/ | 4 | +- |
| M | internal/ | 6 | ++-- |
| M | internal/ | 48 | +++++++++++++++++++++--------- |
| M | internal/ | 16 | +++++----- |
| M | internal/ | 2 | + |
| M | internal/ | 27 | +++++++++-------- |
| M | internal/ | 10 | +++--- |
| M | internal/ | 2 | +- |
| M | internal/ | 6 | ++-- |
@@ -46,7 +46,7 @@ var stressSizes = []struct{ w, h int }{{24, 10}, {120, 40}}
4646 4747 // BenchmarkDraw is a chart drawn as text, as every frame showing it does.4848 func BenchmarkDraw(b *testing.B) {49- for _, n := range []int{12, sheet.MaxRows} {49+ for _, n := range []int{12, 8192} {5050 d := stressData(n)5151 for _, ct := range stressTypes {5252 for _, sz := range stressSizes {@@ -23,6 +23,13 @@ type Shape struct {
2323 Input string2424 }2525 26+// Rows and Cols are the size of the shapes: the grid of Lotus 1-2-3 that27+// 012 had before it grew to Excel's, so runs before and after compare.28+const (29+ Rows = 819230+ Cols = 25631+)32+2633 func at(col, row int) sheet.Addr { return sheet.Addr{Col: col, Row: row} }2734 2835 func load(s *sheet.Sheet, a sheet.Addr, input string) {@@ -75,7 +82,7 @@ func FanOut(k int) *sheet.Sheet {
7582 s := sheet.New()7683 load(s, at(0, 0), "1")7784 for i := range k {78- load(s, at(1+i/sheet.MaxRows, i%sheet.MaxRows), fmt.Sprintf("=$A$1*%d", i))85+ load(s, at(1+i/Rows, i%Rows), fmt.Sprintf("=$A$1*%d", i))7986 }8087 s.RecalcAll()8188 return s@@ -102,7 +109,7 @@ func Volatile(k int) *sheet.Sheet {
102109 if i%2 == 1 {103110 f = "=TODAY()"104111 }105- load(s, at(i/sheet.MaxRows, i%sheet.MaxRows), f)112+ load(s, at(i/Rows, i%Rows), f)106113 }107114 s.RecalcAll()108115 return s@@ -148,16 +155,16 @@ func LongText(rows, n int) *sheet.Sheet {
148155 // formula using each: every changed cell is checked against every name.149156 func Names(k int) *sheet.Sheet {150157 s := sheet.New()151- for row := range sheet.MaxRows {158+ for row := range Rows {152159 load(s, at(0, row), fmt.Sprint(row%100))153160 }154161 for i := range k {155- from := (i * 7) % (sheet.MaxRows - 100)162+ from := (i * 7) % (Rows - 100)156163 name := fmt.Sprintf("Region_%d", i)157164 if err := s.DefineName(name, sheet.NewRect(at(0, from), at(0, from+99))); err != nil {158165 panic(err)159166 }160- load(s, at(1+i/sheet.MaxRows, i%sheet.MaxRows), "=SUM("+name+")")167+ load(s, at(1+i/Rows, i%Rows), "=SUM("+name+")")161168 }162169 s.RecalcAll()163170 return s@@ -206,12 +213,12 @@ func Charts(s *sheet.Sheet, k int) {
206213 // Shapes are the edit-latency topologies at their stress sizes.207214 func Shapes() []Shape {208215 return []Shape{209- {"dense-8192x26", func() *sheet.Sheet { return Dense(sheet.MaxRows, 26) }, at(0, 0), "5"},210- {"chain-8192", func() *sheet.Sheet { return Chain(sheet.MaxRows) }, at(0, 0), "2"},211- {"fanin-1000xSUM8192", func() *sheet.Sheet { return FanIn(sheet.MaxRows, 1000) }, at(0, 4000), "7"},212- {"fanout-8192", func() *sheet.Sheet { return FanOut(sheet.MaxRows) }, at(0, 0), "2"},213- {"running-8192", func() *sheet.Sheet { return RunningTotals(sheet.MaxRows) }, at(0, 0), "3"},214- {"volatile-8192", func() *sheet.Sheet { return Volatile(sheet.MaxRows) }, at(200, 0), "1"},216+ {"dense-8192x26", func() *sheet.Sheet { return Dense(Rows, 26) }, at(0, 0), "5"},217+ {"chain-8192", func() *sheet.Sheet { return Chain(Rows) }, at(0, 0), "2"},218+ {"fanin-1000xSUM8192", func() *sheet.Sheet { return FanIn(Rows, 1000) }, at(0, 4000), "7"},219+ {"fanout-8192", func() *sheet.Sheet { return FanOut(Rows) }, at(0, 0), "2"},220+ {"running-8192", func() *sheet.Sheet { return RunningTotals(Rows) }, at(0, 0), "3"},221+ {"volatile-8192", func() *sheet.Sheet { return Volatile(Rows) }, at(200, 0), "1"},215222 {"names-1000", func() *sheet.Sheet { return Names(1000) }, at(0, 50), "9"},216223 }217224 }@@ -94,7 +94,7 @@ func (m *Model) insertChart() tea.Cmd {
9494 }9595 start := m.sheet.StateID()9696 c := m.sheet.GuessChart(r)97- c.W = clamp(newChartW, sheet.MinChartW, max(m.width-rowHdrW-2, sheet.MinChartW))97+ c.W = clamp(newChartW, sheet.MinChartW, max(m.width-m.hdrW()-2, sheet.MinChartW))9898 c.H = clamp(newChartH, sheet.MinChartH, max(m.visibleRows()-1, sheet.MinChartH))9999 // Next to the data when it fits on screen, as Sheets does; otherwise100100 // below it.@@ -164,7 +164,7 @@ func (g *grid) chartScreen(c sheet.Chart) (x, y int) {
164164 165165 // chartAt returns the topmost chart drawn under x, y, or -1.166166 func (m *Model) chartAt(x, y int) int {167- if y < gridTop || y >= gridTop+m.visibleRows() || x < rowHdrW {167+ if y < gridTop || y >= gridTop+m.visibleRows() || x < m.hdrW() {168168 return -1169169 }170170 charts := m.displayCharts()@@ -196,12 +196,12 @@ func (m *Model) chartBoxes() []box {
196196 top, bottom := gridTop, gridTop+m.visibleRows()197197 for i, c := range m.displayCharts() {198198 x, y := m.chartScreen(c)199- if x >= m.width || y >= bottom || x+c.W <= rowHdrW || y+c.H <= top {199+ if x >= m.width || y >= bottom || x+c.W <= m.hdrW() || y+c.H <= top {200200 continue201201 }202202 lines := m.drawChart(i, c, i == sel)203203 // Clip to the grid: the headers and panel stay on top.204- from, to := max(rowHdrW-x, 0), min(c.W, m.width-x)204+ from, to := max(m.hdrW()-x, 0), min(c.W, m.width-x)205205 b := box{id: chartBoxID(i), x: x + from, y: max(y, top)}206206 for k, l := range lines {207207 if y+k >= top && y+k < bottom {@@ -185,14 +185,14 @@ func (s *chartSel) release(m *Model) {
185185 func (g *grid) chartCellAt(x, y int) sheet.Addr {186186 a := sheet.Addr{Row: max(g.top+y-gridTop, 0), Col: g.left}187187 switch {188- case x >= rowHdrW:188+ case x >= g.hdrW():189189 if col, _, ok := g.colSpan(x); ok {190190 a.Col = col191191 } else {192192 a.Col = g.left + g.visibleCols(g.left) - 1193193 }194194 default:195- for cx := rowHdrW; cx > x && a.Col > 0; {195+ for cx := g.hdrW(); cx > x && a.Col > 0; {196196 a.Col--197197 cx -= g.sheet.ColWidth(a.Col)198198 }@@ -11,7 +11,7 @@ import (
1111 )1212 1313 // handleX is the screen x of the fill handle in column c (scrolled to A).14-func handleX(c int) int { return rowHdrW + (c+1)*sheet.DefaultWidth - 1 }14+func handleX(c int) int { return minRowHdrW + (c+1)*sheet.DefaultWidth - 1 }1515 1616 func TestFillHandleDrag(t *testing.T) {1717 tests := []struct {@@ -68,10 +68,7 @@ func init() {
6868 for _, ts := range textStyles {6969 get, title := ts.get, ts.title7070 register(&command{id: ts.id, title: title, desc: ts.desc, edits: (*Model).selection, run: func(m *Model) tea.Cmd {71- var cur sheet.Style72- if c := m.sheet.Cell(m.cur); c != nil {73- cur = c.Style74- }71+ cur := m.sheet.CellStyle(m.cur)7572 on := !*get(&cur)7673 r := m.selection()7774 label := strings.ToLower(title) + " " + r.String()@@ -15,7 +15,7 @@ import (
1515 func rowCells(m *Model, r, n int) []string {1616 l := line(m, gridTop+r-1)1717 out := make([]string, n)18- x := rowHdrW18+ x := minRowHdrW1919 for i := range n {2020 w := m.sheet.ColWidth(m.left + i)2121 // Cut by display columns: cells may hold multi-byte text.@@ -30,7 +30,7 @@ func raw(m *Model, r, n int) string {
3030 l := line(m, gridTop+r-1)3131 l += strings.Repeat(" ", max(0, 400-len(l)))3232 var parts []string33- x := rowHdrW33+ x := minRowHdrW3434 for i := range n {3535 w := m.sheet.ColWidth(m.left + i)3636 parts = append(parts, l[x:x+w])@@ -286,7 +286,7 @@ func click(m *Model, x, y int, mod tea.KeyMod) {
286286 }287287 288288 // cellX returns a screen x inside column c when scrolled to column A.289-func cellX(c int) int { return rowHdrW + c*sheet.DefaultWidth + 2 }289+func cellX(c int) int { return minRowHdrW + c*sheet.DefaultWidth + 2 }290290 291291 func TestMouseSelection(t *testing.T) {292292 m := newModel()@@ -307,7 +307,7 @@ func TestMouseSelection(t *testing.T) {
307307 }308308 309309 click(m, cellX(3), headerLine, 0)310- if got := m.selection(); got.From != addr("D1") || got.To != addr("D8192") {310+ if got := m.selection(); got.From != addr("D1") || got.To != addr("D1048576") {311311 t.Errorf("column header selected %s", got)312312 }313313 click(m, 1, gridTop+3, 0)@@ -80,7 +80,7 @@ func (m *Model) hitTest(x, y int) hit {
8080 return hit{kind: hitFormulaBar, x: x - tx}8181 }8282 return hit{kind: hitPanel}83- case y == headerLine && x < rowHdrW:83+ case y == headerLine && x < m.hdrW():8484 return hit{kind: hitCorner}8585 case y == headerLine:8686 col, start, ok := m.colSpan(x)@@ -100,7 +100,7 @@ func (m *Model) hitTest(x, y int) hit {
100100 if !ok {101101 return hit{}102102 }103- if x < rowHdrW {103+ if x < m.hdrW() {104104 return hit{kind: hitRowHeader, addr: sheet.Addr{Col: m.left, Row: row}}105105 }106106 col, start, ok := m.colSpan(x)@@ -67,7 +67,7 @@ func TestClickFormulaBarEdits(t *testing.T) {
6767 6868 func TestResizeColumnByDragging(t *testing.T) {6969 m := newModel()70- border := rowHdrW + sheet.DefaultWidth - 170+ border := minRowHdrW + sheet.DefaultWidth - 17171 send(m, tea.MouseMotionMsg{X: border, Y: headerLine})7272 if m.mouse.hover.kind != hitColBorder || !strings.Contains(line(m, headerLine), "▐") {7373 t.Fatalf("hover %v header %q", m.mouse.hover.kind, line(m, headerLine))@@ -89,7 +89,7 @@ func TestResizeColumnByDragging(t *testing.T) {
8989 func TestDoubleClickBorderAutofits(t *testing.T) {9090 m := newModel()9191 press(t, m, "A much longer label", "<enter>")92- border := rowHdrW + sheet.DefaultWidth - 192+ border := minRowHdrW + sheet.DefaultWidth - 19393 click(m, border, headerLine, 0)9494 click(m, border, headerLine, 0)9595 if w := m.sheet.ColWidth(0); w != len("A much longer label")+2 {@@ -125,7 +125,7 @@ func TestPointerShapes(t *testing.T) {
125125 want string126126 }{127127 {cellX(1), gridTop + 1, "cell"},128- {rowHdrW + sheet.DefaultWidth - 1, headerLine, "col-resize"},128+ {minRowHdrW + sheet.DefaultWidth - 1, headerLine, "col-resize"},129129 {cellX(1), headerLine, "pointer"},130130 {formulaBarTextX() + 1, formulaLine, "text"},131131 {m.width - 1, m.height - 1, "default"},@@ -1,6 +1,8 @@
11 package ui22 33 import (4+ "strconv"5+46 "github.com/FelineStateMachine/012/internal/sheet"57 )68 @@ -22,7 +24,7 @@ func (g *grid) frozen() (rows, cols int) {
2224 w := 02325 for c := range cols {2426 w += g.sheet.ColWidth(c)25- if rowHdrW+w+1+3 > g.width {27+ if g.hdrW()+w+1+3 > g.width {2628 cols = c2729 break2830 }@@ -71,14 +73,35 @@ func (g *grid) screenRows() []int {
7173 if fr > 0 {7274 out = append(out, divider)7375 }74- for r := g.top; len(out) < n && r < sheet.MaxRows; r++ {75- if !g.sheet.RowHidden(r) {76- out = append(out, r)77- }76+ r, ok := g.top, true77+ if g.sheet.RowHidden(r) {78+ r, ok = g.sheet.NextShownRow(r, 1)79+ }80+ for ; ok && len(out) < n; r, ok = g.sheet.NextShownRow(r, 1) {81+ out = append(out, r)7882 }7983 return out8084 }8185 86+// hdrW is the width of the row numbers: the widest number on screen with87+// a space either side, and at least minRowHdrW, so it widens past row88+// 9999 as Excel's does.89+func (g *grid) hdrW() int {90+ last := g.top + g.visibleRows()91+ if g.sheet.HiddenRows() > 0 {92+ last = g.top93+ for range g.visibleRows() {94+ r, ok := g.sheet.NextShownRow(last, 1)95+ if !ok {96+ break97+ }98+ last = r99+ }100+ last++101+ }102+ return max(minRowHdrW, len(strconv.Itoa(min(last, sheet.MaxRows)))+2)103+}104+82105 // rowAt returns the row on screen line y, or false for the divider and83106 // lines outside the grid.84107 func (g *grid) rowAt(y int) (int, bool) {@@ -107,11 +130,8 @@ func (g *grid) stepRow(r, n int) int {
107130 d, n = -1, -n108131 }109132 for ; n > 0; n-- {110- next := r + d111- for next >= 0 && next < sheet.MaxRows && g.sheet.RowHidden(next) {112- next += d113- }114- if next < 0 || next >= sheet.MaxRows {133+ next, ok := g.sheet.NextShownRow(r, d)134+ if !ok {115135 break116136 }117137 r = next@@ -136,7 +156,7 @@ func (g *grid) visibleRow(r int) int {
136156 // row numbers, the frozen columns and their divider.137157 func (g *grid) scrollX() int {138158 _, fc := g.frozen()139- x := rowHdrW159+ x := g.hdrW()140160 for c := range fc {141161 x += g.sheet.ColWidth(c)142162 }@@ -206,7 +226,7 @@ func (g *grid) visibleCols(left int) int {
206226 // a frozen column or a scrolling one, but not the divider between them.207227 func (g *grid) colSpan(x int) (col, start int, ok bool) {208228 _, fc := g.frozen()209- cx := rowHdrW229+ cx := g.hdrW()210230 for c := 0; c < fc; c++ {211231 w := g.sheet.ColWidth(c)212232 if x >= cx && x < cx+w {@@ -231,7 +251,7 @@ func (g *grid) colSpan(x int) (col, start int, ok bool) {
231251 func (g *grid) colStart(c int) int {232252 _, fc := g.frozen()233253 if c < fc {234- x := rowHdrW254+ x := g.hdrW()235255 for k := range c {236256 x += g.sheet.ColWidth(k)237257 }@@ -301,7 +321,7 @@ func (g *grid) dragTarget(x, y int, anchor sheet.Addr) (a sheet.Addr, dc, dr int
301321 a.Col, dc = g.left+cols-1, 1302322 case ok && (col >= fc || !scrolledRight || anchor.Col < fc):303323 a.Col = col304- case x >= rowHdrW && !scrolledRight:324+ case x >= g.hdrW() && !scrolledRight:305325 a.Col = g.left306326 case scrolledRight && anchor.Col >= fc:307327 a.Col, dc = g.left, -1@@ -90,21 +90,21 @@ func TestMouseWithFrozenPanes(t *testing.T) {
9090 table(t, m, 40)9191 m.sheet.SetFrozen(2, 1)9292 m.top, m.left = 20, 393- divX := rowHdrW + sheet.DefaultWidth // the column divider93+ divX := minRowHdrW + sheet.DefaultWidth // the column divider9494 tests := []struct {9595 x, y int9696 want hit9797 }{98- {rowHdrW + 1, gridTop, hit{kind: hitCell, addr: addr("A1")}},99- {rowHdrW + 1, gridTop + 1, hit{kind: hitCell, addr: addr("A2")}},100- {rowHdrW + 1, gridTop + 2, hit{}}, // the divider line101- {rowHdrW + 1, gridTop + 3, hit{kind: hitCell, addr: addr("A21")}},98+ {minRowHdrW + 1, gridTop, hit{kind: hitCell, addr: addr("A1")}},99+ {minRowHdrW + 1, gridTop + 1, hit{kind: hitCell, addr: addr("A2")}},100+ {minRowHdrW + 1, gridTop + 2, hit{}}, // the divider line101+ {minRowHdrW + 1, gridTop + 3, hit{kind: hitCell, addr: addr("A21")}},102102 {divX, gridTop + 3, hit{}},103103 {divX + 1, gridTop + 3, hit{kind: hitCell, addr: addr("D21")}},104104 {divX + 1, gridTop, hit{kind: hitCell, addr: addr("D1")}},105105 {2, gridTop + 3, hit{kind: hitRowHeader, addr: sheet.Addr{Col: 3, Row: 20}}},106106 {divX + sheet.DefaultWidth, headerLine, hit{kind: hitColBorder, addr: sheet.Addr{Col: 3, Row: 20}}},107- {rowHdrW + sheet.DefaultWidth - 1, headerLine, hit{kind: hitColBorder, addr: sheet.Addr{Col: 0, Row: 20}}},107+ {minRowHdrW + sheet.DefaultWidth - 1, headerLine, hit{kind: hitColBorder, addr: sheet.Addr{Col: 0, Row: 20}}},108108 }109109 for _, tt := range tests {110110 if got := m.hitTest(tt.x, tt.y); got != tt.want {@@ -200,9 +200,9 @@ func TestNavigationSkipsFilteredRows(t *testing.T) {
200200 201201 func TestFilteredNavigationIsFast(t *testing.T) {202202 m := newModel()203- table(t, m, sheet.MaxRows-2)203+ table(t, m, 8190)204204 m.sheet.SetFrozen(1, 1)205- m.sheet.CreateFilter(sheet.Rect{To: sheet.Addr{Col: 1, Row: sheet.MaxRows - 1}})205+ m.sheet.CreateFilter(sheet.Rect{To: sheet.Addr{Col: 1, Row: sheet.MaxRows - 1}}) // whole columns206206 m.sheet.FilterColumn(1, sheet.Criteria{Hidden: []string{"1", "2", "3"}})207207 m.View()208208 start := time.Now()@@ -13,6 +13,7 @@ import (
1313 "github.com/FelineStateMachine/012/internal/config"1414 "github.com/FelineStateMachine/012/internal/jev"1515 "github.com/FelineStateMachine/012/internal/keyring"16+ "github.com/FelineStateMachine/012/internal/sheet"1617 "github.com/FelineStateMachine/012/internal/ui/theme"1718 )1819 @@ -59,6 +60,7 @@ func (m *Model) applyConfig() string {
5960 m.term.noImages = !c.Bool("chart-images")6061 m.term.noNotify = !c.Bool("notifications")6162 m.SetVimKeys(c.String("keymap") == "vim")63+ sheet.SetMaxCells(c.Int("max-cells"))6264 return m.applyTheme()6365 }6466 @@ -79,21 +79,21 @@ func (l *rowLayout) content(c int) *sheet.Cell {
7979 // col returns where column c starts and ends, relative to column first.8080 func (l *rowLayout) col(c int) (int, int) { return l.x[c-l.first], l.x[c-l.first+1] }8181 82+// maxReach is how many columns away text may run in from: 2,56083+// characters in default columns.84+const maxReach = 25685+8286 // reach returns the nearest filled columns outside lo..hi, within minCol83-// and maxCol, whose text might run into view; lo and hi if there are none.87+// and maxCol and maxReach of the viewport, whose text might run into88+// view; lo and hi if there are none. The filled cells are found through89+// the sheet's index, not by looking at every column between.8490 func (l *rowLayout) reach(minCol, maxCol int) (first, last int) {8591 first, last = l.lo, l.hi86- for c := l.lo - 1; c >= minCol; c-- {87- if l.content(c) != nil {88- first = c89- break90- }92+ if c, ok := l.s.NextFilledCol(l.row, l.lo-1, -1, max(minCol, l.lo-maxReach)); ok {93+ first = c9194 }92- for c := l.hi + 1; c <= maxCol; c++ {93- if l.content(c) != nil {94- last = c95- break96- }95+ if c, ok := l.s.NextFilledCol(l.row, l.hi+1, 1, min(maxCol, l.hi+maxReach)); ok {96+ last = c9797 }9898 return first, last9999 }@@ -116,12 +116,13 @@ func (l *rowLayout) place(c int, cell *sheet.Cell) {
116116 from, to := l.room(c, cell, start, tw)117117 l.claimed = to118118 cut := textCutter{s: text}119+ style := l.s.CellStyle(sheet.Addr{Col: c, Row: l.row})119120 for k := max(l.first, l.lo); k <= min(l.last, l.hi); k++ {120121 kx0, kx1 := l.col(k)121122 if kx1 <= from || kx0 >= to {122123 continue123124 }124- sp := Span{Trail: kx1 - kx0, Style: cell.Style, Owner: c}125+ sp := Span{Trail: kx1 - kx0, Style: style, Owner: c}125126 if seg0, seg1 := max(start, kx0, from), min(start+tw, kx1, to); seg1 > seg0 {126127 sp.Lead, sp.Text, sp.Trail = seg0-kx0, cut.cut(seg0-start, seg1-start), kx1-seg1127128 }@@ -143,7 +144,7 @@ func (l *rowLayout) display(c int, cell *sheet.Cell, w int) (string, sheet.Align
143144 text, pad = wider, 0144145 }145146 }146- if a := cell.Style.Align; a != sheet.AlignAuto && align != sheet.AlignFill {147+ if a := l.s.CellStyle(sheet.Addr{Col: c, Row: l.row}).Align; a != sheet.AlignAuto && align != sheet.AlignFill {147148 align = a148149 }149150 return text, align, pad@@ -36,9 +36,9 @@ type uiShape struct {
3636 func uiShapes() []uiShape {3737 return []uiShape{3838 {"empty", sheet.New},39- {"dense-8192x26", func() *sheet.Sheet { return stress.Dense(sheet.MaxRows, 26) }},40- {"dense-8192x256", func() *sheet.Sheet { return stress.Dense(sheet.MaxRows, sheet.MaxCols) }},41- {"longtext-8192x500", func() *sheet.Sheet { return stress.LongText(sheet.MaxRows, 500) }},39+ {"dense-8192x26", func() *sheet.Sheet { return stress.Dense(stress.Rows, 26) }},40+ {"dense-8192x256", func() *sheet.Sheet { return stress.Dense(stress.Rows, stress.Cols) }},41+ {"longtext-8192x500", func() *sheet.Sheet { return stress.LongText(stress.Rows, 500) }},4242 {"names-1000", func() *sheet.Sheet { return stress.Names(1000) }},4343 {"charts-20", func() *sheet.Sheet {4444 s := stress.Table(200, 3)@@ -156,7 +156,7 @@ func BenchmarkKeystroke(b *testing.B) {
156156 {"select-sheet/dense-8192x256", uiShapes()[2], []string{"ctrl+a", "ctrl+a"}, nil},157157 {"extend-data/dense-8192x256", uiShapes()[2], []string{"ctrl+a"}, []string{"shift+up", "shift+down"}},158158 {"extend/dense-8192x26", uiShapes()[1], []string{"shift+down"}, []string{"shift+down", "shift+up"}},159- {"type/fanin-1000", uiShape{"fanin", func() *sheet.Sheet { return stress.FanIn(sheet.MaxRows, 1000) }},159+ {"type/fanin-1000", uiShape{"fanin", func() *sheet.Sheet { return stress.FanIn(stress.Rows, 1000) }},160160 nil, []string{"7", "enter", "up"}},161161 {"type/dense-8192x26", uiShapes()[1], nil, []string{"7", "enter", "up"}},162162 }@@ -186,7 +186,7 @@ func BenchmarkKeystroke(b *testing.B) {
186186 // that recalculates, with telemetry off and on (a JSON log in a temp187187 // file): what the instrumentation costs.188188 func BenchmarkTelemetry(b *testing.B) {189- s := lazy(func() *sheet.Sheet { return stress.Dense(sheet.MaxRows, 26) })189+ s := lazy(func() *sheet.Sheet { return stress.Dense(stress.Rows, 26) })190190 for _, on := range []bool{false, true} {191191 name := "off"192192 if on {@@ -55,7 +55,7 @@ func TestTraceDependents(t *testing.T) {
5555 // A click ends it too.5656 m.cur = addr("B1")5757 press(t, m, "<alt+.>")58- send(m, tea.MouseClickMsg{X: rowHdrW + 1, Y: gridTop + 4, Button: tea.MouseLeft})58+ send(m, tea.MouseClickMsg{X: minRowHdrW + 1, Y: gridTop + 4, Button: tea.MouseLeft})5959 if m.trace != nil {6060 t.Error("click kept the trace")6161 }@@ -61,7 +61,7 @@ func (m *Model) screenCols() []int {
6161 6262 func (m *Model) headerRow() string {6363 var b strings.Builder64- b.WriteString(m.th.Header.Render(strings.Repeat(" ", rowHdrW)))64+ b.WriteString(m.th.Header.Render(strings.Repeat(" ", m.hdrW())))6565 focus := m.active()6666 sel, selecting := m.highlight()6767 for _, c := range m.screenCols() {@@ -131,7 +131,7 @@ func (m *Model) gridRow(row int) string {
131131 hdr = m.th.HeaderHover132132 }133133 var b strings.Builder134- b.WriteString(hdr.Render(theme.PadLeft(strconv.Itoa(row+1), rowHdrW-1) + " "))134+ b.WriteString(hdr.Render(theme.PadLeft(strconv.Itoa(row+1), m.hdrW()-1) + " "))135135 136136 _, fc := m.frozen()137137 if fc > 0 {@@ -189,7 +189,7 @@ func (m *Model) cellsText(row, first int, spans []rowtext.Span, focus sheet.Addr
189189 // the frozen columns.190190 func (m *Model) dividerRow() string {191191 var b strings.Builder192- b.WriteString(strings.Repeat("─", rowHdrW))192+ b.WriteString(strings.Repeat("─", m.hdrW()))193193 for _, c := range m.screenCols() {194194 if c == divider {195195 b.WriteString("┼")