1package sheet23import (4 "fmt"5 "strings"67 "src.rest/cat/012/internal/notebook"8)910// The edits TestRandomEdits draws from, each one undo step (or none,11// when it's refused), described for the log.12var randEdits = []func(r *randBook, e *edits) string{13 (*randBook).editSet, (*randBook).editSet, (*randBook).editSet,14 (*randBook).editErase, (*randBook).editPaste, (*randBook).editFill,15 (*randBook).editSeries, (*randBook).editSort, (*randBook).editInsert,16 (*randBook).editDelete, (*randBook).editFormat, (*randBook).editStyle,17 (*randBook).editMerge, (*randBook).editMove, (*randBook).editLayout,18 (*randBook).editNote, (*randBook).editRule, (*randBook).editRegion,19 (*randBook).editSheets, (*randBook).editView,20}2122func (r *randBook) edit(e *edits) string {23 return randEdits[e.n(len(randEdits))](r, e)24}2526// sheet picks one of the grid sheets (not the notebook nor a source) to27// edit.28func (r *randBook) sheet(e *edits) *Sheet {29 var grids []*Sheet30 for _, s := range r.wb.sheets {31 if !s.IsNotebook() && !s.IsSource() {32 grids = append(grids, s)33 }34 }35 return grids[e.n(len(grids))]36}3738// input is something to type into a cell: numbers, text, dates and39// formulas that reach across ranges, sheets, names, regions and spills.40func (r *randBook) input(e *edits) string {41 ref := func() string {42 a := e.addr()43 if e.n(4) == 0 {44 return "$" + ColName(a.Col) + "$" + fmt.Sprint(a.Row+1)45 }46 return a.String()47 }48 rng := func() string { return e.rect().String() }49 other := []string{"Sheet1", "S2"}[e.n(2)]50 forms := []func() string{51 func() string { return fmt.Sprint(e.n(20) - 5) },52 func() string { return fmt.Sprintf("%d.5", e.n(10)) },53 func() string { return []string{"a", "b", "x y", "TRUE", "2026-01-05", "10%"}[e.n(6)] },54 func() string { return "=" + ref() + "+" + ref() },55 func() string { return "=SUM(" + rng() + ")" },56 func() string { return "=" + other + "!" + ref() + "*2" },57 func() string { return "=SUM(" + other + "!" + rng() + ")" },58 func() string { return "=IFERROR(" + ref() + "/" + ref() + ",0)" },59 func() string { return `=COUNTIF(` + rng() + `,">2")` },60 func() string { return "=SEQUENCE(" + fmt.Sprint(1+e.n(3)) + ")" },61 func() string { return "=SORT(" + rng() + ")" },62 func() string { return "=VLOOKUP(" + ref() + "," + rng() + ",1,FALSE)" },63 func() string { return "=SUM(nu.r1)" },64 func() string { return "=SUM(Total)*2" },65 func() string { return "=INDEX(" + rng() + ",1,1)" },66 func() string { return "=MAX(" + ColName(e.n(randCols)) + ":" + ColName(e.n(randCols)) + ")" },67 func() string { return "=CONCAT(" + ref() + "," + ref() + ")" },68 func() string { return "=ABS(" + ref() + ")+1" },69 func() string { return "=" + ref() + "&\"\"" },70 func() string { return "" },71 }72 return forms[e.n(len(forms))]()73}7475func (r *randBook) editSet(e *edits) string {76 s, a, in := r.sheet(e), e.addr(), r.input(e)77 err := s.Set(a, in)78 return fmt.Sprintf("%s!%s = %q (%v)", s.name, a, in, err)79}8081func (r *randBook) editErase(e *edits) string {82 s, rg := r.sheet(e), e.rect()83 s.EraseRange(rg)84 return fmt.Sprintf("erase %s!%s", s.name, rg)85}8687func (r *randBook) editPaste(e *edits) string {88 src, dst := r.sheet(e), r.sheet(e)89 from, to := e.rect(), e.rect()90 values := e.n(3) == 091 _, err := dst.Paste(src.Copy(from), to, values)92 return fmt.Sprintf("copy %s!%s, paste (values %v) %s!%s (%v)", src.name, from, values, dst.name, to, err)93}9495func (r *randBook) editFill(e *edits) string {96 s, rg := r.sheet(e), e.rect()97 var err error98 if e.n(2) == 0 {99 _, err = s.FillDown(rg)100 return fmt.Sprintf("fill down %s!%s (%v)", s.name, rg, err)101 }102 _, err = s.FillRight(rg)103 return fmt.Sprintf("fill right %s!%s (%v)", s.name, rg, err)104}105106func (r *randBook) editSeries(e *edits) string {107 s, src := r.sheet(e), e.rect()108 dst := src109 if e.n(2) == 0 {110 dst.To.Row = min(dst.To.Row+1+e.n(6), randRows-1)111 } else {112 dst.To.Col = min(dst.To.Col+1+e.n(3), randCols-1)113 }114 _, err := s.FillSeries(src, dst)115 return fmt.Sprintf("series %s!%s to %s (%v)", s.name, src, dst, err)116}117118func (r *randBook) editSort(e *edits) string {119 s, rg := r.sheet(e), e.rect()120 keys := []SortKey{{Col: rg.From.Col + e.n(rg.To.Col-rg.From.Col+1), Desc: e.n(2) == 0}}121 if e.n(2) == 0 {122 keys = append(keys, SortKey{Col: rg.From.Col + e.n(rg.To.Col-rg.From.Col+1)})123 }124 s.SortRange(rg, keys)125 return fmt.Sprintf("sort %s!%s by %v", s.name, rg, keys)126}127128func (r *randBook) editInsert(e *edits) string {129 s, at, n := r.sheet(e), e.n(randRows), 1+e.n(3)130 if e.n(2) == 0 {131 err := s.InsertRows(at, n)132 return fmt.Sprintf("insert %d rows at %s!%d (%v)", n, s.name, at+1, err)133 }134 at %= randCols135 err := s.InsertCols(at, n)136 return fmt.Sprintf("insert %d cols at %s!%s (%v)", n, s.name, ColName(at), err)137}138139func (r *randBook) editDelete(e *edits) string {140 s, at, n := r.sheet(e), e.n(randRows), 1+e.n(3)141 if e.n(2) == 0 {142 s.DeleteRows(at, n)143 return fmt.Sprintf("delete %d rows at %s!%d", n, s.name, at+1)144 }145 at %= randCols146 s.DeleteCols(at, n)147 return fmt.Sprintf("delete %d cols at %s!%s", n, s.name, ColName(at))148}149150func (r *randBook) editFormat(e *edits) string {151 s, rg := r.sheet(e), e.rect()152 f := Format{Kind: FormatKind(e.n(int(FmtSize) + 1))}153 if f.Kind.HasDecimals() {154 f.Decimals = e.n(4)155 }156 switch e.n(3) {157 case 0:158 rg = colRect(rg.From.Col, rg.To.Col)159 case 1:160 rg = rowRect(rg.From.Row, rg.To.Row)161 }162 s.SetFormat(rg, f)163 return fmt.Sprintf("format %s!%s %s", s.name, rg, f.Kind)164}165166func (r *randBook) editStyle(e *edits) string {167 s, rg := r.sheet(e), e.rect()168 switch e.n(4) {169 case 0:170 s.SetStyle(rg, func(st *Style) { st.Bold = !st.Bold })171 return fmt.Sprintf("bold %s!%s", s.name, rg)172 case 1:173 k, l := BorderKind(e.n(int(BorderNone)+1)), Line(1+e.n(3))174 s.SetBorders(rg, k, l)175 return fmt.Sprintf("borders %d %d %s!%s", k, l, s.name, rg)176 case 2:177 s.SetStyle(rg, func(st *Style) { st.Wrap = WrapOn })178 return fmt.Sprintf("wrap %s!%s", s.name, rg)179 }180 s.ClearFormatting(rg)181 return fmt.Sprintf("clear formatting %s!%s", s.name, rg)182}183184func (r *randBook) editMerge(e *edits) string {185 s, rg := r.sheet(e), e.rect()186 if e.n(3) == 0 {187 n := s.Unmerge(rg)188 return fmt.Sprintf("unmerge %s!%s (%d)", s.name, rg, n)189 }190 k := MergeKind(e.n(3))191 err := s.Merge(rg, k)192 return fmt.Sprintf("merge %d %s!%s (%v)", k, s.name, rg, err)193}194195func (r *randBook) editMove(e *edits) string {196 src, dst := r.sheet(e), r.sheet(e)197 rg, to := e.rect(), e.addr()198 _, err := src.MoveTo(dst, rg, to)199 return fmt.Sprintf("move %s!%s to %s!%s (%v)", src.name, rg, dst.name, to, err)200}201202func (r *randBook) editLayout(e *edits) string {203 s := r.sheet(e)204 if e.n(2) == 0 {205 c, w := e.n(randCols), 3+e.n(20)206 s.SetColWidth(c, w)207 return fmt.Sprintf("width %s!%s %d", s.name, ColName(c), w)208 }209 row, h := e.n(randRows), e.n(4)210 s.SetRowHeight(row, row, h)211 return fmt.Sprintf("height %s!%d %d", s.name, row+1, h)212}213214func (r *randBook) editNote(e *edits) string {215 s, a := r.sheet(e), e.addr()216 text := []string{"", "note", "another"}[e.n(3)]217 err := s.SetNote(a, text)218 return fmt.Sprintf("note %s!%s %q (%v)", s.name, a, text, err)219}220221func (r *randBook) editRule(e *edits) string {222 s, rg := r.sheet(e), e.rect()223 switch e.n(3) {224 case 0:225 err := s.AddCondFormat(CondFormat{Ranges: []Rect{rg}, Op: RuleGreater, Args: [2]string{fmt.Sprint(e.n(9))}, Style: RuleStyle{Bold: true}})226 return fmt.Sprintf("conditional format %s!%s (%v)", s.name, rg, err)227 case 1:228 err := s.AddValidation(Validation{Ranges: []Rect{rg}, Kind: ValidList, Items: []string{"a", "b"}})229 return fmt.Sprintf("validation %s!%s (%v)", s.name, rg, err)230 }231 s.ClearCondFormats(rg)232 s.ClearValidations(rg)233 return fmt.Sprintf("clear rules %s!%s", s.name, rg)234}235236func (r *randBook) editRegion(e *edits) string {237 _, _, has2 := r.wb.Region("r2")238 switch e.n(5) {239 case 0:240 cells := r.nb.NotebookCells()241 if len(cells) > 2 {242 cells = cells[:2]243 } else {244 cells = append(cells, notebook.Cell{Source: "r2 = $r1 | first 2"})245 }246 r.nb.SetNotebookCells("edit cells", cells)247 return fmt.Sprintf("notebook cells %d", len(cells))248 case 1:249 if has2 {250 err := r.wb.deleteRegion("r2")251 return fmt.Sprintf("delete r2 (%v)", err)252 }253 s := r.sheet(e)254 err := r.addOutput(s, "r2", e)255 return fmt.Sprintf("send r2 to %s (%v)", s.name, err)256 case 2:257 if has2 {258 s, _, _ := r.wb.Region("r2")259 err := s.FreezeRegion("r2")260 return fmt.Sprintf("freeze r2 (%v)", err)261 }262 }263 name := "r1"264 if has2 && e.n(2) == 0 {265 name = "r2"266 }267 r.feed(name, e)268 return "feed " + name269}270271// deleteRegion deletes the region name, wherever it is.272func (w *Workbook) deleteRegion(name string) error {273 s, _, ok := w.Region(name)274 if !ok {275 return ErrNoRegion276 }277 return s.DeleteRegion(name)278}279280func (r *randBook) editSheets(e *edits) string {281 s := r.sheet(e)282 switch e.n(4) {283 case 0:284 n, err := r.wb.AddSheet("", e.n(len(r.wb.sheets)+1))285 if err != nil {286 return fmt.Sprintf("add sheet (%v)", err)287 }288 return "add sheet " + n.name289 case 1:290 if s.name == "Sheet1" || s.name == "S2" {291 return "keep " + s.name292 }293 err := r.wb.DeleteSheet(s)294 return fmt.Sprintf("delete sheet %s (%v)", s.name, err)295 case 2:296 name := s.name + "x"297 if strings.HasPrefix(s.name, "Sheet1") || s.name == "S2" {298 return "keep the name " + s.name299 }300 err := r.wb.RenameSheet(s, name)301 return fmt.Sprintf("rename %s (%v)", s.name, err)302 }303 rg := e.rect()304 err := r.wb.EditName("Total", "Total", s, rg)305 return fmt.Sprintf("Total = %s!%s (%v)", s.name, rg, err)306}307308func (r *randBook) editView(e *edits) string {309 s := r.sheet(e)310 switch e.n(3) {311 case 0:312 rows, cols := e.n(3), e.n(3)313 s.SetFrozen(rows, cols)314 return fmt.Sprintf("freeze %s %d %d", s.name, rows, cols)315 case 1:316 rg := e.rect()317 s.CreateFilter(rg)318 return fmt.Sprintf("filter %s!%s", s.name, rg)319 }320 s.RemoveFilter()321 return "remove filter " + s.name322}