oficina / sheets Public

Clone
git clone https://amadan.net/oficina/sheets

Plain git — no account needed to clone.

Download

Download this file

1package sheets
2
3import (
4 "strings"
5
6 "amadan.net/oficina/sheets/internal/sheets/calc"
7)
8
9// Copy and fill: spec §4.2's translation pass.
10//
11// > **Copy / drag-fill** — Translation pass at paste time. Relative refs
12// > are re-resolved by offset from the paste anchor; absolute (`$A$1`)
13// > refs resolve to the same id. Unavoidable — it is what relative
14// > means.
15//
16// This is the pass stable row and column identity does NOT remove, and
17// the spec says so in the same paragraph. calc.Translate has existed
18// since the engine landed and had no caller until now, so filling
19// `=A1*2` down a column wrote `=A1*2` into every cell: the grid showed
20// the same number three times and nothing in the suite disagreed,
21// because every test wrote a cell rather than copying one.
22//
23// The offset arrives on the cell write itself rather than through a
24// route of its own. A paste is n cell writes, each of which already
25// carries its destination; naming the cell it came from is one more
26// field on a body that is already explicit and already allowlisted, and
27// it keeps one code path for "a cell changed" rather than two that can
28// disagree about sealing, versioning or fan-out.
29
30// translateCopied rewrites a copied cell's raw input for its new home.
31//
32// dRow and dCol are the destination minus the origin. Everything that
33// is not a formula comes back untouched — a text cell reading "A1 and
34// B2" is not a reference and rewriting it would corrupt the one kind of
35// cell that cannot need this.
36//
37// A formula that will not parse also comes back untouched, which is
38// deliberate: the write then fails exactly as it would have if the same
39// text had been typed, rather than failing differently because it
40// arrived by paste.
41func translateCopied(raw string, dRow, dCol int) string {
42 if dRow == 0 && dCol == 0 {
43 return raw
44 }
45 if !strings.HasPrefix(raw, "=") {
46 return raw
47 }
48 n, err := calc.Parse(raw[1:])
49 if err != nil {
50 return raw
51 }
52 out, ok := calc.TranslateChecked(n, dRow, dCol)
53 if !ok {
54 // Off the top or the left of the grid. Excel writes #REF! and so
55 // do we, as LITERAL TEXT and not as an error value: calc's parser
56 // has no error literal, so there is no formula source that
57 // evaluates to calc.ErrRef, and inventing one is a change to the
58 // grammar rather than to this function.
59 //
60 // The difference is real and worth knowing: =SUM() over a cell
61 // holding the text "#REF!" treats it as text, where Excel would
62 // propagate the error. It is visible to the person either way,
63 // which is the property that matters most here, and the cell is
64 // written rather than skipped — a paste that silently drops a
65 // cell is the failure this whole change exists to remove.
66 return calc.ErrRef.String()
67 }
68 return "=" + calc.Render(out)
69}
70