cat / 012
clone

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

Arrays over a region's table whose cells they read are circular

Dami committed 31f8f17
An array spilling over an output's table blocked it, its anchor showing
#REF!, which the array read, came out smaller and freed the output,
which spilled over again once sent its rows: the two went around
without settling, and a reopened file ended elsewhere (randedit seed
10782). The array is now blocked by the cycle, and checked again when
the table moves.
commit
31f8f178a438fde501dc7f2a6e4920843e9afcff
parent
07dc4228e10d21a249f6894d1cf6074546f66f10
author
Dami <dami.etoile@gmail.com>
kind
fix risk score 1.8 of 3, Jev's estimate
4 files changed, +108 -1
Minternal/sheet/regionclash.go56++++++++++++++++++++++++++++++
Minternal/sheet/regionclash_test.go30++++++++++++++++
Minternal/sheet/regionwrite.go10+++++
Minternal/sheet/spillblock.go13++++++-
M internal/sheet/regionclash.go +56 -0
@@ -132,3 +132,59 @@ func (s *Sheet) wakeAllRegions() {
132132 }133133 }134134 }135+136+// table is the cells the region with meta me takes: its table shown or,137+// blocked, the one it needs; none before its rows first arrive.138+func (me *regionMeta) table() (Rect, bool) {139+ switch {140+ case me == nil || !me.has:141+ return Rect{}, false142+ case me.why != "":143+ return me.need, true144+ }145+ return me.written, true146+}147+148+// regionMoved returns the anchors of the arrays over the region's149+// table, before and after it changed, spilling or blocked by a cycle,150+// to compute again: an array reading the table's cells is blocked by151+// it (spillsIntoItself) wherever the table goes, which the cells it152+// reads may not tell it.153+func (s *Sheet) regionMoved(before, after Rect, had bool) []loc {154+ var out []loc155+ for b, sp := range s.spills {156+ if (sp.why == "" || sp.circular) && (had && overlaps(sp.area, before) || overlaps(sp.area, after)) {157+ sp.stale = true158+ out = append(out, loc{s, b})159+ }160+ }161+ return out162+}163+164+// heldByArrays is the region r's table, when an array spilling over it165+// decides whether it shows: shown, or blocked by arrays alone, which it166+// would show without.167+func (s *Sheet) heldByArrays(r Region) (Rect, bool) {168+ me := s.regionMeta[nameKey(r.Name)]169+ t, ok := me.table()170+ if !ok || me.why == "" {171+ return t, ok172+ }173+ if t.To.Row >= MaxRows || t.To.Col >= MaxCols {174+ return Rect{}, false175+ }176+ for a := range s.cells.anyKeysIn(t) {177+ if a == r.At || !s.cells.filledAt(a) {178+ continue179+ }180+ if _, spilled := s.SpillAnchor(a); !spilled || s.cells.derivedAt(a) != slotSpill {181+ return Rect{}, false182+ }183+ }184+ for _, q := range s.regions.list {185+ if q.Name != r.Name && t.Contains(q.At) {186+ return Rect{}, false187+ }188+ }189+ return t, true190+}
M internal/sheet/regionclash_test.go +30 -0
M internal/sheet/regionwrite.go +10 -0
@@ -76,6 +76,9 @@ func (s *Sheet) writeRegions() []loc {
7676 for k, me := range s.regionMeta {7777 if s.regionIndex(k) < 0 {7878 changed = append(changed, s.clearOwned(me, noRect)...)79+ if t, ok := me.table(); ok {80+ changed = append(changed, s.regionMoved(t, noRect, true)...)81+ }7982 delete(s.regionMeta, k)8083 s.wakeAllRegions()8184 for u := range s.wb.nameUsers[nameKey(regionPrefix+k)] {
@@ -97,6 +100,9 @@ func (s *Sheet) writeRegions() []loc {
97100 // naming it read #REF! until then, so they're among the cells returned.98101 func (s *Sheet) emptyRegion(r Region, me *regionMeta) []loc {99102 changed := s.clearOwned(me, noRect)103+ if t, ok := me.table(); ok {104+ changed = append(changed, s.regionMoved(t, noRect, true)...)105+ }100106 if me.has {101107 changed = s.regionReaders(r, changed)102108 }
@@ -115,6 +121,7 @@ func (s *Sheet) emptyRegion(r Region, me *regionMeta) []loc {
115121 func (s *Sheet) writeTable(r Region, me *regionMeta, header LiveRow, rows []LiveRow, reset bool) []loc {116122 cols, nrows := tableSize(me, header, rows, reset)117123 was, had, shape := me.written, me.has, [2]int{me.rows, me.cols}124+ before, _ := me.table()118125 need := s.tableArea(r, cols, nrows)119126 changed := s.makeRoomAt(r)120127 whole := me.why != "" || me.placed // it may not hold every cell it wrote
@@ -160,6 +167,9 @@ func (s *Sheet) writeTable(r Region, me *regionMeta, header LiveRow, rows []Live
160167 for _, c := range changed {161168 s.freedFor(c.a)162169 }170+ if after, _ := me.table(); !had || after != before {171+ changed = append(changed, s.regionMoved(before, after, had)...)172+ }163173 if !had || was != me.written || shape != [2]int{me.rows, me.cols} { // moved, or its table changed shape164174 changed = s.regionReaders(r, changed)165175 }
M internal/sheet/spillblock.go +12 -1