Cells were a bare map used directly across the engine. A small store type with get, set, delete, len, all and inRange now owns it, and no code outside store.go touches the map, so the representation can change later without the rest of the engine noticing. inRange picks between probing each address and scanning the store, the choice RangeStats made on its own; cellsIn and the pushed-off check for inserts use it, so they no longer scan every cell for a small range. The representation is unchanged.
- commit
- 6b40936f4c473559e9214ad4892c0ba134cd37c4
- parent
- cbb6aa77a43924ec885a6f8eb53f2d599acc8b1e
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 4 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 6 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 4 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 6 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 27 | +++---------- |
| M | internal/ | 2 | +- |
| M | internal/ | 40 | ++++++++++---------- |
| M | internal/ | 10 | ++-- |
| M | internal/ | 2 | +- |
| M | internal/ | 4 | +- |
| M | internal/ | 28 | ++++++------- |
| M | internal/ | 4 | +- |
| A | internal/ | 61 | ++++++++++++++++++++++++++++++ |
| M | internal/ | 4 | +- |
| M | internal/ | 10 | ++-- |
@@ -14,7 +14,7 @@ const materializeLimit = 1 << 16
1414 // or for Automatic, the one inferred from its formula (=DATE() shows a1515 // date, =SUM(B2:B4) of currency shows currency).1616 func (s *Sheet) DisplayFormat(a Addr) Format {17- c := s.cells[a]17+ c := s.cells.get(a)1818 switch {1919 case c == nil:2020 return Format{}@@ -83,7 +83,7 @@ func (s *Sheet) eachCell(r Rect, create bool, fn func(Addr, *Cell)) {
8383 addrs = s.cellsIn(r)8484 }8585 for _, a := range addrs {86- old := s.cells[a]86+ old := s.cells.get(a)8787 c := old.clone()8888 if c == nil {8989 c = &Cell{}@@ -171,7 +171,7 @@ func (s *Sheet) clipToUsed(r Rect) Rect {
171171 // nothing more touches it. A blank cell with no filled neighbors is a172172 // region of its own.173173 func (s *Sheet) Region(a Addr) Rect {174- filled := func(a Addr) bool { return a.Valid() && !s.cells[a].Blank() }174+ filled := func(a Addr) bool { return a.Valid() && !s.cells.get(a).Blank() }175175 seen := map[Addr]bool{}176176 var queue []Addr177177 visit := func(p Addr) {@@ -274,14 +274,14 @@ func (s *Sheet) writeBody(b *bytes.Buffer, indent, names string) error {
274274 if err := s.writeView(b, indent); err != nil {275275 return err276276 }277- addrs := make([]Addr, 0, len(s.cells))278- for a := range s.cells {277+ addrs := make([]Addr, 0, s.cells.len())278+ for a := range s.cells.all() {279279 addrs = append(addrs, a)280280 }281281 sortAddrs(addrs)282282 b.WriteString(indent + `"cells": {`)283283 for i, a := range addrs {284- raw, err := encodeCell(s.cells[a])284+ raw, err := encodeCell(s.cells.get(a))285285 if err != nil {286286 return err287287 }@@ -254,7 +254,7 @@ func (s *Sheet) rowPasses(row int, tests []colTest) bool {
254254 // ShownText is the cell's value as its format displays it, with no width255255 // limit: what filters and the values list compare.256256 func (s *Sheet) ShownText(a Addr) string {257- c := s.cells[a]257+ c := s.cells.get(a)258258 if c.Blank() {259259 return ""260260 }@@ -65,7 +65,7 @@ func (s *Sheet) Find(query string, o FindOptions) ([]Addr, error) {
6565 if o.Within != nil && !o.Within.Contains(a) {6666 continue6767 }68- if f.re.MatchString(f.text(s.cells[a])) {68+ if f.re.MatchString(f.text(s.cells.get(a))) {6969 out = append(out, a)7070 }7171 }@@ -85,7 +85,7 @@ func (s *Sheet) Replace(a Addr, query, repl string, o FindOptions) (bool, error)
8585 }8686 8787 func (s *Sheet) replace(a Addr, f *finder, repl string) (bool, error) {88- c := s.cells[a]88+ c := s.cells.get(a)8989 if c == nil || (c.IsFormula() && !f.o.InFormulas) {9090 return false, nil9191 }@@ -145,7 +145,7 @@ func (s *Sheet) record(a Addr) {
145145 }146146 l := loc{s, a}147147 if _, seen := st.cells[l]; !seen {148- st.cells[l] = s.cells[a].clone()148+ st.cells[l] = s.cells.get(a).clone()149149 }150150 s.wb.hist.dirty = append(s.wb.hist.dirty, l)151151 }@@ -246,7 +246,7 @@ func (w *Workbook) push(st *step) {
246246 }247247 }248248 for l, c := range st.cells {249- if c == nil && l.s.cells[l.a] == nil {249+ if c == nil && l.s.cells.get(l.a) == nil {250250 delete(st.cells, l)251251 }252252 }@@ -366,7 +366,7 @@ func (w *Workbook) swap(from, to *[]*step) (Change, bool) {
366366 }367367 changed := make([]loc, 0, len(st.cells))368368 for l, c := range st.cells {369- inv.cells[l] = l.s.cells[l.a].clone()369+ inv.cells[l] = l.s.cells.get(l.a).clone()370370 l.s.place(l.a, c.clone())371371 changed = append(changed, l)372372 }@@ -22,7 +22,7 @@ func sheetOf(t *testing.T, cells map[string]string) *Sheet {
2222 // inputs returns every cell's input by address.2323 func inputs(s *Sheet) map[string]string {2424 out := map[string]string{}25- for a, c := range s.cells {25+ for a, c := range s.cells.all() {2626 out[a.String()] = c.Input2727 }2828 return out@@ -167,7 +167,7 @@ func (w *Workbook) renameInFormulas(from, to string) {
167167 return n168168 }}169169 for _, l := range slices.Collect(maps.Keys(w.nameUsers[from])) {170- l.s.place(l.a, l.s.cells[l.a].rewritten(rw))170+ l.s.place(l.a, l.s.cells.get(l.a).rewritten(rw))171171 }172172 }173173 @@ -5,7 +5,7 @@ package sheet
55 func (s *Sheet) UsedRange() (Rect, bool) {66 var last Addr77 found := false8- for a, c := range s.cells {8+ for a, c := range s.cells.all() {99 if c.Blank() {1010 continue1111 }@@ -22,7 +22,7 @@ func (s *Sheet) UsedRange() (Rect, bool) {
2222 // block, it goes to the next filled cell. With nothing filled ahead it2323 // stops at the edge of the worksheet. Rows the filter hides are skipped.2424 func (s *Sheet) Edge(a Addr, dc, dr int) Addr {25- filled := func(a Addr) bool { return !s.cells[a].Blank() }25+ filled := func(a Addr) bool { return !s.cells.get(a).Blank() }2626 step := func(a Addr) Addr {2727 n := Addr{Col: a.Col + dc, Row: a.Row + dr}2828 for dr != 0 && n.Valid() && s.RowHidden(n.Row) {@@ -72,10 +72,8 @@ type statsCache struct {
7272 ok bool7373 }7474 75-// RangeStats computes Stats over r, visiting whichever is cheaper: the76-// cells in r one lookup at a time, or every stored cell (iterating the77-// map costs about a quarter of a lookup per cell). The result is kept78-// until a cell or value changes.75+// RangeStats computes Stats over r. The result is kept until a cell or76+// value changes.7977 func (s *Sheet) RangeStats(r Rect) Stats {8078 if c := s.stats; c.ok && c.r == r && c.version == s.version {8179 return c.st@@ -88,20 +86,9 @@ func (s *Sheet) RangeStats(r Rect) Stats {
8886 st.Sum += c.Value.Num8987 }9088 }91- area := (r.To.Col - r.From.Col + 1) * (r.To.Row - r.From.Row + 1)92- if area <= len(s.cells)/4 {93- for row := r.From.Row; row <= r.To.Row; row++ {94- for col := r.From.Col; col <= r.To.Col; col++ {95- if c := s.cells[Addr{Col: col, Row: row}]; !c.Blank() {96- add(c)97- }98- }99- }100- } else {101- for a, c := range s.cells {102- if r.Contains(a) && !c.Blank() {103- add(c)104- }89+ for _, c := range s.cells.inRange(r) {90+ if !c.Blank() {91+ add(c)10592 }10693 }10794 s.stats = statsCache{r: r, version: s.version, st: st, ok: true}@@ -33,7 +33,7 @@ func (w *Workbook) observe(full bool, start time.Time, evaluated int) {
3333 }3434 info := RecalcInfo{Full: full, Evaluated: evaluated, Circular: w.Circular}3535 for _, s := range w.sheets {36- info.Cells += len(s.cells)36+ info.Cells += s.cells.len()3737 info.Volatile += len(s.volatile)3838 }3939 info.Duration = time.Since(start)@@ -25,8 +25,8 @@ const maxFill = 1 << 20
2525 // stay as they are.2626 func (s *Sheet) remap(label string, focus Rect, cell func(Addr) (Addr, bool), rng func(Rect) (Rect, bool)) {2727 rw := formula.Relocate(s.onThis(s), cell, rng)28- next := make(map[Addr]*Cell, len(s.cells))29- for a, c := range s.cells {28+ next := make(map[Addr]*Cell, s.cells.len())29+ for a, c := range s.cells.all() {3030 if to, ok := cell(a); ok {3131 next[to] = c.rewritten(rw)3232 }@@ -36,7 +36,7 @@ func (s *Sheet) remap(label string, focus Rect, cell func(Addr) (Addr, bool), rn
3636 if l.s == s {3737 continue3838 }39- c := l.s.cells[l.a]39+ c := l.s.cells.get(l.a)4040 if nc := c.rewritten(formula.Relocate(s.onThis(l.s), cell, rng)); nc != c {4141 others[l] = nc4242 }@@ -45,13 +45,13 @@ func (s *Sheet) remap(label string, focus Rect, cell func(Addr) (Addr, bool), rn
4545 for l, c := range others {4646 l.s.place(l.a, c)4747 }48- for a, c := range s.cells {48+ for a, c := range s.cells.all() {4949 if next[a] != c {5050 s.place(a, next[a])5151 }5252 }5353 for a, c := range next {54- if s.cells[a] != c {54+ if s.cells.get(a) != c {5555 s.place(a, c)5656 }5757 }@@ -85,14 +85,14 @@ func (s *Sheet) insert(rows bool, at, n int) error {
8585 if rows {8686 size = MaxRows8787 }88- for a := range s.cells {89- line := a.Col90- if rows {91- line = a.Row92- }93- if line >= at && line >= size-n {94- return ErrPushedOff95- }88+ // The lines pushed past the edge must be empty.89+ first := max(at, size-n)90+ edge := colRect(first, size-1)91+ if rows {92+ edge = rowRect(first, size-1)93+ }94+ for range s.cells.inRange(edge) {95+ return ErrPushedOff9696 }9797 s.restructure(rows, formula.Span{At: at, N: n, Size: size})9898 return nil@@ -201,20 +201,20 @@ func (s *Sheet) MoveTo(dst *Sheet, src Rect, to Addr) (Rect, error) {
201201 202202 moved := map[Addr]*Cell{}203203 for _, a := range s.cellsIn(src) {204- moved[shift(a)] = s.cells[a].rewritten(rw(s, dst))204+ moved[shift(a)] = s.cells.get(a).rewritten(rw(s, dst))205205 }206206 rest := map[loc]*Cell{}207207 keep := func(l loc) {208208 if src.Contains(l.a) && l.s == s || d.Contains(l.a) && l.s == dst {209209 return210210 }211- c := l.s.cells[l.a]211+ c := l.s.cells.get(l.a)212212 if nc := c.rewritten(rw(l.s, l.s)); nc != c {213213 rest[l] = nc214214 }215215 }216216 for _, t := range []*Sheet{s, dst} {217- for a := range t.cells {217+ for a := range t.cells.all() {218218 keep(loc{t, a})219219 }220220 }@@ -299,7 +299,7 @@ type Clip struct {
299299 func (s *Sheet) Copy(r Rect) *Clip {300300 c := &Clip{Src: r, cells: map[Addr]*Cell{}}301301 for _, a := range s.cellsIn(r) {302- c.cells[Addr{Col: a.Col - r.From.Col, Row: a.Row - r.From.Row}] = s.cells[a].clone()302+ c.cells[Addr{Col: a.Col - r.From.Col, Row: a.Row - r.From.Row}] = s.cells.get(a).clone()303303 }304304 return c305305 }@@ -363,7 +363,7 @@ func (s *Sheet) Paste(c *Clip, dst Rect, values bool) (Rect, error) {
363363 func (s *Sheet) pasteCell(a Addr, c *Cell, dc, dr int, values bool) {364364 switch {365365 case c == nil:366- if s.cells[a] != nil {366+ if s.cells.get(a) != nil {367367 s.place(a, nil)368368 }369369 case values:@@ -445,7 +445,7 @@ func (s *Sheet) FillEntry(r Rect, origin Addr, input string) error {
445445 if err := s.put(origin, input); err != nil {446446 return err447447 }448- c := s.cells[origin].clone()448+ c := s.cells.get(origin).clone()449449 for row := r.From.Row; row <= r.To.Row; row++ {450450 for col := r.From.Col; col <= r.To.Col; col++ {451451 if a := (Addr{Col: col, Row: row}); a != origin {@@ -469,7 +469,7 @@ func (s *Sheet) seriesStart(r Rect, down bool) (Rect, bool) {
469469 }470470 used := map[int]bool{}471471 for _, a := range s.cellsIn(r) {472- if !s.cells[a].Blank() {472+ if !s.cells.get(a).Blank() {473473 used[line(a)] = true474474 }475475 }@@ -30,8 +30,8 @@ func (w *Workbook) recalcAll() {
3030 start := recalcStart()3131 n := 03232 for _, s := range w.sheets {33- s.calc = make(map[Addr]int, len(s.cells))34- for a := range s.cells {33+ s.calc = make(map[Addr]int, s.cells.len())34+ for a := range s.cells.all() {3535 s.calc[a] = dirty3636 }3737 n += len(s.calc)@@ -125,7 +125,7 @@ func (m *marking) readersOf(l loc) {
125125 m.push(loc{s, d})126126 }127127 for u := range s.rangeUsers.candidates(a.Col) {128- if s.cells[u].rangeHas(a) {128+ if s.cells.get(u).rangeHas(a) {129129 m.push(loc{s, u})130130 }131131 }@@ -169,7 +169,7 @@ func (w *Workbook) evaluate() {
169169 compute = func(s *Sheet, a Addr) Value {170170 // Every cell a formula reads comes through here, so it looks171171 // each map up once: a SUM over 8192 cells makes 8192 calls.172- c := s.cells[a]172+ c := s.cells.get(a)173173 switch st := s.calc[a]; {174174 case c == nil:175175 return Value{}@@ -217,7 +217,7 @@ func (w *Workbook) evaluate() {
217217 // crossReads reports whether the formula at u reads the cell l through a218218 // reference that names l's sheet.219219 func (w *Workbook) crossReads(u, l loc) bool {220- c := u.s.cells[u.a]220+ c := u.s.cells.get(u.a)221221 if c == nil {222222 return false223223 }@@ -58,7 +58,7 @@ func (s *Sheet) RecalcVolatile() { s.wb.recalc(nil) }
5858 // RemoteCalls returns the questions the formula at a asks, with their5959 // current inputs, so the UI can show details or re-ask them.6060 func (s *Sheet) RemoteCalls(a Addr) []RemoteCall {61- c := s.cells[a]61+ c := s.cells.get(a)6262 if c == nil || c.expr == nil {6363 return nil6464 }@@ -75,7 +75,7 @@ func (s *Sheet) FillSeries(src, dst Rect) (Rect, error) {
7575 func (s *Sheet) fillLane(seq, targets []Addr) {7676 cells := make([]*Cell, len(seq))7777 for i, a := range seq {78- cells[i] = s.cells[a]78+ cells[i] = s.cells.get(a)7979 }8080 next := detectSeries(cells)8181 for i, to := range targets {@@ -91,7 +91,7 @@ func (s *Sheet) fillLane(seq, targets []Addr) {
9191 fallthrough9292 case c != nil:9393 s.pasteCell(to, c, to.Col-seq[k].Col, to.Row-seq[k].Row, false)94- case s.cells[to] != nil:94+ case s.cells.get(to) != nil:9595 s.place(to, nil)9696 }9797 }@@ -54,7 +54,7 @@ type Sheet struct {
5454 name string5555 live bool // in the workbook's list; false once deleted5656 57- cells map[Addr]*Cell57+ cells cellStore // see store.go5858 widths map[int]int5959 6060 // dependents maps a cell to the formula cells that reference it@@ -90,11 +90,11 @@ func New() *Sheet {
9090 9191 // Cell returns the cell at a, or nil if it has neither contents nor9292 // formatting. Use Blank to test for contents.93-func (s *Sheet) Cell(a Addr) *Cell { return s.cells[a] }93+func (s *Sheet) Cell(a Addr) *Cell { return s.cells.get(a) }9494 9595 // Value returns the computed value at a.9696 func (s *Sheet) Value(a Addr) Value {97- if c := s.cells[a]; c != nil {97+ if c := s.cells.get(a); c != nil {9898 return c.Value9999 }100100 return Value{}@@ -103,7 +103,7 @@ func (s *Sheet) Value(a Addr) Value {
103103 // Len returns the number of non-blank cells.104104 func (s *Sheet) Len() int {105105 n := 0106- for _, c := range s.cells {106+ for _, c := range s.cells.all() {107107 if !c.Blank() {108108 n++109109 }@@ -113,8 +113,8 @@ func (s *Sheet) Len() int {
113113 114114 // Addrs returns every non-blank cell in row-major order.115115 func (s *Sheet) Addrs() []Addr {116- out := make([]Addr, 0, len(s.cells))117- for a, c := range s.cells {116+ out := make([]Addr, 0, s.cells.len())117+ for a, c := range s.cells.all() {118118 if !c.Blank() {119119 out = append(out, a)120120 }@@ -214,7 +214,7 @@ func (s *Sheet) Set(a Addr, input string) error {
214214 func (s *Sheet) put(a Addr, input string) error {215215 var f Format216216 var st Style217- if old := s.cells[a]; old != nil {217+ if old := s.cells.get(a); old != nil {218218 f, st = old.Format, old.Style219219 }220220 c, err := newCell(input, f, st, true)@@ -299,7 +299,7 @@ func (s *Sheet) place(a Addr, c *Cell) {
299299 if c == nil {300300 return301301 }302- s.cells[a] = c302+ s.cells.set(a, c)303303 for _, r := range c.refs {304304 if s.dependents[r] == nil {305305 s.dependents[r] = make(map[Addr]struct{})@@ -320,7 +320,7 @@ func (s *Sheet) place(a Addr, c *Cell) {
320320 func (s *Sheet) EraseRange(r Rect) {321321 s.change("clear "+r.String(), r, func() {322322 for _, a := range s.cellsIn(r) {323- if c := s.cells[a]; !c.Blank() {323+ if c := s.cells.get(a); !c.Blank() {324324 s.place(a, formattingOnly(c.Format, c.Style))325325 }326326 }@@ -330,17 +330,15 @@ func (s *Sheet) EraseRange(r Rect) {
330330 // cellsIn returns the cells in r that have contents or formatting.331331 func (s *Sheet) cellsIn(r Rect) []Addr {332332 var out []Addr333- for a := range s.cells {334- if r.Contains(a) {335- out = append(out, a)336- }333+ for a := range s.cells.inRange(r) {334+ out = append(out, a)337335 }338336 return out339337 }340338 341339 // unlink removes the cell at a and its dependency edges.342340 func (s *Sheet) unlink(a Addr) {343- old := s.cells[a]341+ old := s.cells.get(a)344342 if old == nil {345343 return346344 }@@ -355,5 +353,5 @@ func (s *Sheet) unlink(a Addr) {
355353 }356354 s.rangeUsers.remove(a, old.ranges)357355 delete(s.volatile, a)358- delete(s.cells, a)356+ s.cells.delete(a)359357 }@@ -88,7 +88,7 @@ func (s *Sheet) SortRange(r Rect, keys []SortKey) {
8888 })8989 old := map[Addr]*Cell{}9090 for _, a := range s.cellsIn(r) {91- old[a] = s.cells[a]91+ old[a] = s.cells.get(a)9292 }9393 s.change("sort "+r.String(), r, func() {9494 for k, src := range order {@@ -101,7 +101,7 @@ func (s *Sheet) SortRange(r Rect, keys []SortKey) {
101101 switch c := old[Addr{Col: col, Row: src}]; {102102 case c != nil:103103 s.place(to, c.rewritten(formula.Shift(0, dst-src)))104- case s.cells[to] != nil:104+ case s.cells.get(to) != nil:105105 s.place(to, nil)106106 }107107 }@@ -0,0 +1,61 @@
1+package sheet2+3+import (4+ "iter"5+ "maps"6+)7+8+// cellStore holds a sheet's cells: those with contents or formatting, by9+// address. The rest of the engine reads and writes cells only through10+// these methods, never the map beneath, so the representation can change11+// (to column blocks of compact values, with formulas and formats in side12+// tables; see docs/limits.md) without touching it.13+//14+// Iteration order is unspecified. Cells may be set or deleted while15+// iterating, with a map's rules: a deleted cell not yet reached isn't16+// yielded, a new one may or may not be.17+type cellStore struct {18+ m map[Addr]*Cell19+}20+21+func newCellStore() cellStore { return cellStore{m: make(map[Addr]*Cell)} }22+23+// get returns the cell at a, or nil.24+func (st *cellStore) get(a Addr) *Cell { return st.m[a] }25+26+// set stores c, which isn't nil, at a.27+func (st *cellStore) set(a Addr, c *Cell) { st.m[a] = c }28+29+// delete removes the cell at a, if any.30+func (st *cellStore) delete(a Addr) { delete(st.m, a) }31+32+// len is the number of cells stored, formatting-only ones included.33+func (st *cellStore) len() int { return len(st.m) }34+35+// all yields every stored cell.36+func (st *cellStore) all() iter.Seq2[Addr, *Cell] { return maps.All(st.m) }37+38+// inRange yields the stored cells in r, visiting whichever is cheaper:39+// each address of r (a lookup each), or every stored cell (iterating the40+// map costs about a quarter of a lookup per cell).41+func (st *cellStore) inRange(r Rect) iter.Seq2[Addr, *Cell] {42+ return func(yield func(Addr, *Cell) bool) {43+ area := (r.To.Col - r.From.Col + 1) * (r.To.Row - r.From.Row + 1)44+ if area <= len(st.m)/4 {45+ for row := r.From.Row; row <= r.To.Row; row++ {46+ for col := r.From.Col; col <= r.To.Col; col++ {47+ a := Addr{Col: col, Row: row}48+ if c := st.m[a]; c != nil && !yield(a, c) {49+ return50+ }51+ }52+ }53+ return54+ }55+ for a, c := range st.m {56+ if r.Contains(a) && !yield(a, c) {57+ return58+ }59+ }60+ }61+}@@ -23,7 +23,7 @@ type Target struct {
2323 // repeats dropped. References to sheets that don't exist are left out. It2424 // is empty for anything but a formula.2525 func (s *Sheet) Precedents(a Addr) []Target {26- c := s.cells[a]26+ c := s.cells.get(a)2727 if c == nil || !c.IsFormula() {2828 return nil2929 }@@ -48,7 +48,7 @@ func (s *Sheet) Dependents(a Addr) []Target {
4848 seen[loc{s, d}] = true4949 }5050 for u := range s.rangeUsers.candidates(a.Col) {51- for _, r := range s.cells[u].ranges {51+ for _, r := range s.cells.get(u).ranges {5252 if r.Contains(a) {5353 seen[loc{s, u}] = true5454 }@@ -79,7 +79,7 @@ func (w *Workbook) newSheet(name string) *Sheet {
7979 return &Sheet{8080 wb: w,8181 name: name,82- cells: make(map[Addr]*Cell),82+ cells: newCellStore(),8383 widths: make(map[int]int),8484 dependents: make(map[Addr]map[Addr]struct{}),8585 volatile: make(map[Addr]struct{}),@@ -207,7 +207,7 @@ func (w *Workbook) DuplicateSheet(s *Sheet) (*Sheet, error) {
207207 return nil, errors.New("That sheet was deleted")208208 }209209 cp := w.newSheet(w.freeName("Copy of " + s.name))210- for a, c := range s.cells {210+ for a, c := range s.cells.all() {211211 c := c.clone()212212 c.setExpr(c.expr) // fresh reference slices, shared with nothing213213 cp.place(a, c)@@ -285,7 +285,7 @@ func (w *Workbook) renameRefs(old, name string) {
285285 },286286 }287287 for _, l := range w.crossList() {288- if c := l.s.cells[l.a]; c.readsSheet(old) {288+ if c := l.s.cells.get(l.a); c.readsSheet(old) {289289 l.s.place(l.a, c.rewritten(rw))290290 }291291 }@@ -328,7 +328,7 @@ func (w *Workbook) attach(s *Sheet) {
328328 }329329 s.live = true330330 w.byKey[formula.SheetKey(s.name)] = s331- for a, c := range s.cells {331+ for a, c := range s.cells.all() {332332 w.index(loc{s, a}, c)333333 }334334 w.structural = true@@ -344,7 +344,7 @@ func (w *Workbook) detach(s *Sheet) {
344344 if w.byKey[formula.SheetKey(s.name)] == s {345345 delete(w.byKey, formula.SheetKey(s.name))346346 }347- for a, c := range s.cells {347+ for a, c := range s.cells.all() {348348 w.unindex(loc{s, a}, c)349349 }350350 w.structural = true