1package findbar23import (4 "encoding/json"5 "errors"6 "fmt"7 "slices"8 "strings"910 "src.rest/cat/012/internal/sheet"11)1213// answerJSON is a replacement as a macro answers Find and replace:14//15// {"find": "Rent", "replace": "Lease", "matchCase": true, "within": "all"}16//17// the query and the replacement, the options that are on, where to18// search (left out for the sheet shown, "all" for every sheet, or a19// range of the sheet shown), and "cell" to replace only the match in20// that cell rather than all of them.21type answerJSON struct {22 Find string `json:"find"`23 Replace string `json:"replace"`24 MatchCase bool `json:"matchCase,omitempty"`25 WholeCell bool `json:"wholeCell,omitempty"`26 Regex bool `json:"regex,omitempty"`27 InFormulas bool `json:"inFormulas,omitempty"`28 Within string `json:"within,omitempty"`29 Cell string `json:"cell,omitempty"`30}3132// answer is the bar's search and replacement as a macro records them,33// replacing only in cell when it isn't "".34func (f *Bar) answer(cell string) string {35 a := answerJSON{Find: f.fields[0], Replace: f.fields[1], MatchCase: f.opts.MatchCase, WholeCell: f.opts.WholeCell,36 Regex: f.opts.Regex, InFormulas: f.opts.InFormulas, Cell: cell}37 switch f.where {38 case inAll:39 a.Within = "all"40 case inRange:41 a.Within = f.scope.String()42 }43 raw, _ := json.Marshal(a)44 return string(raw)45}4647// Answer makes the replacement a macro recorded (see answerJSON), as if48// typed and chosen in the bar.49func (f *Bar) Answer(text string) error {50 var a answerJSON51 if err := json.Unmarshal([]byte(text), &a); err != nil {52 return fmt.Errorf("the answer: %w", err)53 }54 if a.Find == "" {55 return errors.New(`the answer has nothing to find: give "find"`)56 }57 f.fields, f.replace, f.field = [2]string{a.Find, a.Replace}, true, 158 f.h.Line().Set(a.Replace)59 f.opts = sheet.FindOptions{MatchCase: a.MatchCase, WholeCell: a.WholeCell, Regex: a.Regex, InFormulas: a.InFormulas}60 s, at := f.h.At()61 switch a.Within {62 case "":63 f.where = inSheet64 case "all":65 f.where = inAll66 default:67 r, ok := sheet.ParseRange(strings.ToUpper(a.Within))68 if !ok {69 return fmt.Errorf(`"within" is %q: "all", or a range such as B2:D9`, a.Within)70 }71 f.scope, f.home, f.where = &r, s, inRange72 }73 f.from = Match{s, at}74 if f.search(); f.err != "" {75 return errors.New(f.err)76 }77 if a.Cell == "" {78 f.replaceAll()79 return nil80 }81 c, ok := sheet.ParseAddr(strings.ToUpper(a.Cell))82 if !ok {83 return fmt.Errorf("%q isn't a cell", a.Cell)84 }85 i := slices.Index(f.matches, Match{s, c})86 if i < 0 {87 return fmt.Errorf("%s doesn't match %q", c, a.Find)88 }89 f.cur = i90 f.replaceOne()91 return nil92}