cat / 012
clone

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

main
012 / internal / sheet / regionclash.go
regionclash.go 6.0K
1package sheet23// Regions in each other's way (see region.go). What a region shows4// can't depend on the order their rows arrived in, or a file would open5// differently from how it was saved, its regions sent their rows in6// another order. So, as for arrays (spill.go):7//8// - a region's anchor is in the way of every other region, whether9// it shows a table, why it can't, or nothing yet, as an array's10// formula is in the way of other arrays;11// - when two tables need the same cells, the region whose anchor12// comes first, row by row, gets them, whichever arrived first: the13// other gives way, showing why at its anchor.14//15// A region blocked by another tries again, its source sending its rows,16// when a cell it needs is freed, or a region on its sheet goes.1718// inTheWay returns a cell of need, the cells the region r with meta me19// needs, holding something r didn't write and that keeps it from20// showing, and the regions whose tables are to give way to it. Unless21// whole is set, only what r didn't hold is looked at: nothing else is22// placed in a region's cells without it knowing (regionTouched).23func (s *Sheet) inTheWay(r Region, need Rect, me *regionMeta, whole bool) (at Addr, blocked bool, later []Region) {24 // An array's cells are in the way even where it spilled blanks, as25 // another array's are (spill.go).26 for a, sp := range s.spills {27 if x, ok := intersectRect(sp.area, need); ok && sp.why == "" {28 if x.From != a {29 return x.From, true, nil30 }31 return a, true, nil32 }33 }34 for _, q := range s.regions.list {35 if q.Name != r.Name && need.Contains(q.At) {36 return q.At, true, nil37 }38 }39 parts := []Rect{need}40 if !whole {41 parts = fresh(need, me)42 }43 for _, part := range parts {44 if at, blocked = s.filledIn(part, r, me, &later); blocked {45 return at, true, nil46 }47 }48 return Addr{}, false, later49}5051// filledIn returns a cell of part holding something the region r with52// meta me didn't write that keeps it from showing, adding to later the53// regions whose cells there come after r's anchor.54func (s *Sheet) filledIn(part Rect, r Region, me *regionMeta, later *[]Region) (Addr, bool) {55 for a := range s.cells.anyKeysIn(part) {56 if !s.cells.filledAt(a) || s.regionOwns(a, me) {57 continue58 }59 q, _, ok := s.ownerOf(a)60 if !ok || before(q.At, r.At) {61 return a, true62 }63 if !regionListed(*later, q) {64 *later = append(*later, q)65 }66 }67 return Addr{}, false68}6970// before reports whether the cell at a comes before the cell at b, row71// by row.72func before(a, b Addr) bool { return a.Row < b.Row || a.Row == b.Row && a.Col < b.Col }7374func regionListed(list []Region, r Region) bool {75 for _, x := range list {76 if x.Name == r.Name {77 return true78 }79 }80 return false81}8283// makeRoomAt has the regions whose tables hold r's anchor give way to84// it, and returns the cells that changed.85func (s *Sheet) makeRoomAt(r Region) []loc {86 var changed []loc87 for _, q := range s.regions.list {88 if me := s.regionMeta[nameKey(q.Name)]; q.Name != r.Name && me != nil && me.why == "" && s.regionOwns(r.At, me) {89 changed = append(changed, s.giveWay(q, Rect{From: r.At, To: r.At})...)90 }91 }92 return changed93}9495// giveWay has the region q, showing a table, show why it can't instead:96// another region needs cells of it in taken. It returns the cells that97// changed, with the formulas naming q.98func (s *Sheet) giveWay(q Region, taken Rect) []loc {99 me := s.meta(nameKey(q.Name))100 at, _ := intersectRect(me.written, taken)101 need, anchor := me.written, Rect{From: q.At, To: q.At}102 changed := s.clearOwned(me, anchor)103 me.why = "can't show: it would overwrite data in " + at.From.String()104 me.need, me.written, me.rows, me.cols = need, anchor, 0, 0105 changed = s.writeRegionCell(q.At, LiveCell{V: ErrRef}, me, changed)106 return s.regionReaders(q, changed)107}108109// wakeRegions marks the regions other than me blocked from showing by a110// cell of gone, which me has just cleared, to be sent again.111func (s *Sheet) wakeRegions(me *regionMeta, gone []Addr) {112 for _, other := range s.regionMeta {113 if other == me || other.why == "" {114 continue115 }116 for _, a := range gone {117 if other.need.Contains(a) {118 other.stale = true119 break120 }121 }122 }123}124125// wakeAllRegions marks every region of the sheet blocked from showing126// to be sent again, as when a region goes: its anchor, which may have127// blocked them, goes with it.128func (s *Sheet) wakeAllRegions() {129 for _, me := range s.regionMeta {130 if me.why != "" {131 me.stale = true132 }133 }134}135136// 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.138func (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}147148// 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.153func (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}163164// 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.167func (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}