- commit
- 5f1f93d65828eb7bca4689925caf06c3387ef5fd
- parent
- 23f0bf06ca18390900d605424164e381bd540711
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 2 | +- |
| A | internal/ | 70 | ++++++++++++++++++++++++++++++ |
| A | internal/ | 39 | +++++++++++++++++ |
| M | internal/ | 9 | +++- |
@@ -418,7 +418,7 @@ func (w *Workbook) swap(undo bool) (Change, bool) {
418418 for k, l := range st.lines {419419 inv.lines[k] = k.s.line(k.row, k.n)420420 k.s.setLine(k.row, k.n, l)421- changed = append(changed, k.s.lineCells(k.row, k.n)...)421+ changed = append(changed, k.s.lineChanged(k.row, k.n)...)422422 }423423 for k, n := range st.names {424424 inv.names[k] = w.namePtr(k)@@ -0,0 +1,70 @@
1+package sheet2+3+// Formulas infer their format from the cells they read (=B5*2 shows4+// currency when B5 does), blank ones included. A blank cell has no entry5+// of its own to mark changed when its column, row or the sheet is6+// formatted, so the formulas reading anywhere in the line are marked7+// instead, found through the indexes recalculation uses: the cost is the8+// formulas, not the line's million cells.9+10+// readersIn returns the formulas, on any sheet, that read a cell of r on11+// s: by reference, through a range, a named range, or naming s.12+func (s *Sheet) readersIn(r Rect) []loc {13+ seen := map[loc]struct{}{}14+ add := func(l loc) { seen[l] = struct{}{} }15+ for a, users := range s.dependents {16+ if r.Contains(a) {17+ for u := range users {18+ add(loc{s, u})19+ }20+ }21+ }22+ s.rangeUsers.usersIn(r, func(u Addr) { add(loc{s, u}) })23+ w := s.wb24+ for _, nu := range w.namedInUse() {25+ if nu.s == s && overlaps(nu.r, r) {26+ for u := range nu.users {27+ add(u)28+ }29+ }30+ }31+ for u := range w.crossUsers {32+ if c := u.s.cells.get(u.a); c != nil {33+ for _, x := range c.xrefs {34+ if w.byKey[x.key] == s && overlaps(x.r, r) {35+ add(u)36+ }37+ }38+ }39+ }40+ out := make([]loc, 0, len(seen))41+ for l := range seen {42+ out = append(out, l)43+ }44+ return out45+}46+47+// usersIn calls fn with the formulas one of whose ranges overlaps r; a48+// formula may come more than once.49+func (x *rangeIndex) usersIn(r Rect, fn func(Addr)) {50+ each := func(iv *intervals) {51+ for rg, users := range iv.users {52+ if overlaps(rg, r) {53+ for u := range users {54+ fn(u)55+ }56+ }57+ }58+ }59+ for c, iv := range x.byCol {60+ if c >= r.From.Col && c <= r.To.Col {61+ each(iv)62+ }63+ }64+ each(&x.wide)65+}66+67+// overlaps reports whether two ranges share a cell.68+func overlaps(p, q Rect) bool {69+ return p.From.Col <= q.To.Col && q.From.Col <= p.To.Col && p.From.Row <= q.To.Row && q.From.Row <= p.To.Row70+}@@ -0,0 +1,39 @@
1+package sheet2+3+import "testing"4+5+// Formulas reading blank cells of a column, row or sheet whose format6+// changes infer their format again at once, not at the next recalc.7+func TestLineFormatReinfers(t *testing.T) {8+ w := NewBook()9+ s := w.Sheets()[0]10+ s.Set(at("D1"), "=B5*2")11+ s.Set(at("D2"), "=SUM(B7:B9)")12+ s.Set(at("D3"), "=C4")13+ o, _ := w.AddSheet("Other", 1)14+ o.Set(at("A1"), "=Sheet1!B6")15+ s.SetFormat(col(1), Preset(FmtCurrency))16+ for _, c := range []struct {17+ s *Sheet18+ a string19+ }{{s, "D1"}, {s, "D2"}, {o, "A1"}} {20+ if f := c.s.DisplayFormat(at(c.a)); f.Kind != FmtCurrency {21+ t.Errorf("%s shows %v after formatting column B, want currency", c.a, f.Kind)22+ }23+ }24+ if f := s.DisplayFormat(at("D3")); !f.IsZero() {25+ t.Errorf("D3 shows %v, reading column C", f.Kind)26+ }27+ s.SetFormat(rowRect(3, 3), Preset(FmtPercent))28+ if f := s.DisplayFormat(at("D3")); f.Kind != FmtPercent {29+ t.Errorf("D3 shows %v after formatting row 4, want percent", f.Kind)30+ }31+ s.Undo()32+ if f := s.DisplayFormat(at("D3")); !f.IsZero() {33+ t.Errorf("D3 shows %v after undo", f.Kind)34+ }35+ s.SetFormat(Rect{To: Addr{Col: MaxCols - 1, Row: MaxRows - 1}}, Preset(FmtPercent))36+ if f := s.DisplayFormat(at("D3")); f.Kind != FmtPercent {37+ t.Errorf("D3 shows %v after formatting the sheet, want percent", f.Kind)38+ }39+}@@ -214,10 +214,17 @@ func (s *Sheet) setLine(row bool, n int, l lineFmt) {
214214 }215215 s.version++216216 if s.wb.hist.open != nil {217- s.wb.hist.dirty = append(s.wb.hist.dirty, s.lineCells(row, n)...)217+ s.wb.hist.dirty = append(s.wb.hist.dirty, s.lineChanged(row, n)...)218218 }219219 }220220 221+// lineChanged is what to recalculate when a column's or row's format222+// changes: its stored cells, and the formulas reading any cell of it,223+// which infer their format from blank cells too.224+func (s *Sheet) lineChanged(row bool, n int) []loc {225+ return append(s.lineCells(row, n), s.readersIn(s.lineRect(row, n, n))...)226+}227+221228 // lineCells are the stored cells of a column or row.222229 func (s *Sheet) lineCells(row bool, n int) []loc {223230 var out []loc