cat / 012
clone

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

Add used range, data-edge jumps and range stats to the engine

Dami committed 5acda22
Edge implements Ctrl+arrow jumps (to the end of a filled block, or to
the next filled cell), UsedRange backs Ctrl+End and Ctrl+A, and
RangeStats computes Sum/Count over a range, visiting whichever is
smaller: the range or the filled cells.
commit
5acda22f33b03e0b53bd2f01173ddc7380c2b4c6
parent
4626856f1a364dee3432ee909ef030a7fa316ce9
author
Dami <dami.etoile@gmail.com>
2 files changed, +134 -0
Ainternal/sheet/nav.go84++++++++++++++++++++++++++++++
Ainternal/sheet/nav_test.go50++++++++++++++++++
A internal/sheet/nav.go +84 -0
@@ -0,0 +1,84 @@
1+package sheet2+3+// UsedRange returns the smallest range from A1 covering every non-blank4+// cell, and false if the sheet is empty.5+func (s *Sheet) UsedRange() (Rect, bool) {6+ if len(s.cells) == 0 {7+ return Rect{}, false8+ }9+ var last Addr10+ for a := range s.cells {11+ last.Col = max(last.Col, a.Col)12+ last.Row = max(last.Row, a.Row)13+ }14+ return Rect{To: last}, true15+}16+17+// Edge returns where a data-edge jump (Ctrl+arrow in Excel, End+arrow in18+// 1-2-3) from a in direction (dc, dr) lands. Inside a block of filled cells19+// it stops at the block's last cell; from a blank cell, or at the end of a20+// block, it goes to the next filled cell. With nothing filled ahead it21+// stops at the edge of the worksheet.22+func (s *Sheet) Edge(a Addr, dc, dr int) Addr {23+ filled := func(a Addr) bool { return s.cells[a] != nil }24+ next := Addr{Col: a.Col + dc, Row: a.Row + dr}25+ if !next.Valid() {26+ return a27+ }28+ if filled(a) && filled(next) {29+ for {30+ n := Addr{Col: next.Col + dc, Row: next.Row + dr}31+ if !n.Valid() || !filled(n) {32+ return next33+ }34+ next = n35+ }36+ }37+ for {38+ if filled(next) {39+ return next40+ }41+ n := Addr{Col: next.Col + dc, Row: next.Row + dr}42+ if !n.Valid() {43+ return next44+ }45+ next = n46+ }47+}48+49+// Stats summarizes the values in r, as shown in the status line for a50+// selection. Count is non-blank cells, Nums is numeric cells.51+type Stats struct {52+ Sum float6453+ Count, Nums int54+}55+56+// RangeStats computes Stats over r, visiting whichever is smaller: the57+// cells in r or the non-blank cells in the sheet.58+func (s *Sheet) RangeStats(r Rect) Stats {59+ var st Stats60+ add := func(c *Cell) {61+ st.Count++62+ if c.Value.Kind == Number {63+ st.Nums++64+ st.Sum += c.Value.Num65+ }66+ }67+ area := (r.To.Col - r.From.Col + 1) * (r.To.Row - r.From.Row + 1)68+ if area <= len(s.cells) {69+ for row := r.From.Row; row <= r.To.Row; row++ {70+ for col := r.From.Col; col <= r.To.Col; col++ {71+ if c := s.cells[Addr{Col: col, Row: row}]; c != nil {72+ add(c)73+ }74+ }75+ }76+ return st77+ }78+ for a, c := range s.cells {79+ if r.Contains(a) {80+ add(c)81+ }82+ }83+ return st84+}
A internal/sheet/nav_test.go +50 -0