• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

iddan / react-spreadsheet / 6062654729

03 Sep 2023 05:21AM UTC coverage: 80.232% (-0.04%) from 80.269%
6062654729

push

github

iddan
Fix formatting

417 of 568 branches covered (0.0%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

967 of 1157 relevant lines covered (83.58%)

28.47 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.42
/src/engine.ts
1
import FormulaParser, { FormulaError, Value } from "fast-formula-parser";
11✔
2
import { getReferences } from "./formula";
11✔
3
import * as Matrix from "./matrix";
11✔
4
import * as Formula from "./formula";
11✔
5
import { Point } from "./point";
6
import { PointGraph } from "./point-graph";
11✔
7
import { PointSet } from "./point-set";
11✔
8
import { CellBase } from "./types";
9

10
export class Model<Cell extends CellBase> {
11✔
11
  readonly data!: Matrix.Matrix<Cell>;
12
  readonly evaluatedData!: Matrix.Matrix<Cell>;
13

14
  readonly referenceGraph!: PointGraph;
15

16
  constructor(
17
    data: Matrix.Matrix<Cell>,
18
    referenceGraph?: PointGraph,
19
    evaluatedData?: Matrix.Matrix<Cell>
20
  ) {
21
    this.data = data;
38✔
22
    this.referenceGraph = referenceGraph || createReferenceGraph(data);
38✔
23
    this.evaluatedData =
38✔
24
      evaluatedData || createEvaluatedData(data, this.referenceGraph);
70✔
25
  }
26
}
27

28
export function updateCellValue<Cell extends CellBase>(
11✔
29
  model: Model<Cell>,
30
  point: Point,
31
  cell: Cell,
32
  parserConstructor?: (getData: () => Matrix.Matrix<CellBase>) => FormulaParser
33
): Model<Cell> {
34
  const nextData = Matrix.set(point, cell, model.data);
6✔
35
  const nextReferenceGraph = Formula.isFormulaValue(cell.value)
6✔
36
    ? updateReferenceGraph(model.referenceGraph, point, cell, nextData)
6✔
37
    : model.referenceGraph;
38

39
  const nextEvaluatedData = evaluateCell(
6✔
40
    model.evaluatedData,
41
    nextData,
42
    nextReferenceGraph,
43
    point,
44
    cell,
45
    parserConstructor
46
  );
47
  return new Model(nextData, nextReferenceGraph, nextEvaluatedData);
6✔
48
}
49

50
function updateReferenceGraph(
51
  referenceGraph: PointGraph,
52
  point: Point,
53
  cell: CellBase<string>,
54
  data: Matrix.Matrix<CellBase>
55
): PointGraph {
56
  const references = getReferences(
3✔
57
    Formula.extractFormula(cell.value),
58
    point,
59
    data
60
  );
61
  const nextReferenceGraph = referenceGraph.set(point, references);
3✔
62
  return nextReferenceGraph;
3✔
63
}
64

65
function evaluateCell<Cell extends CellBase>(
66
  prevEvaluatedData: Matrix.Matrix<Cell>,
67
  data: Matrix.Matrix<Cell>,
68
  referenceGraph: PointGraph,
69
  point: Point,
70
  cell: Cell,
71
  parserConstructor?: (getData: () => Matrix.Matrix<CellBase>) => FormulaParser
72
): Matrix.Matrix<Cell> {
73
  if (referenceGraph.hasCircularDependency(point)) {
6✔
74
    let visited = PointSet.from([point]);
1✔
75
    let nextEvaluatedData = Matrix.set(
1✔
76
      point,
77
      { ...cell, value: FormulaError.REF },
78
      prevEvaluatedData
79
    );
80
    for (const referrer of referenceGraph.getBackwardsRecursive(point)) {
1✔
81
      if (visited.has(referrer)) {
1✔
82
        break;
1✔
83
      }
84
      visited = visited.add(referrer);
×
85
      const referrerCell = Matrix.get(referrer, data);
×
86
      if (!referrerCell) {
×
87
        continue;
×
88
      }
89
      nextEvaluatedData = Matrix.set(
×
90
        referrer,
91
        { ...referrerCell, value: FormulaError.REF },
92
        nextEvaluatedData
93
      );
94
    }
95
    return nextEvaluatedData;
1✔
96
  }
97

98
  let nextEvaluatedData = prevEvaluatedData;
5✔
99

100
  const formulaParser = (parserConstructor ?? Formula.createBoundFormulaParser)(
5!
101
    () => nextEvaluatedData
2✔
102
  );
103

104
  const evaluatedValue = Formula.isFormulaValue(cell.value)
5✔
105
    ? getFormulaComputedValue(cell.value, point, formulaParser)
5✔
106
    : cell.value;
107

108
  const evaluatedCell = { ...cell, value: evaluatedValue };
5✔
109

110
  nextEvaluatedData = Matrix.set(point, evaluatedCell, nextEvaluatedData);
5✔
111

112
  // for every formula cell that references the cell re-evaluate (recursive)
113
  for (const referrer of referenceGraph.getBackwardsRecursive(point)) {
5✔
114
    const referrerCell = Matrix.get(referrer, data);
×
115
    if (!referrerCell) {
×
116
      continue;
×
117
    }
118
    const evaluatedValue = Formula.isFormulaValue(referrerCell.value)
×
119
      ? getFormulaComputedValue(referrerCell.value, point, formulaParser)
×
120
      : referrerCell.value;
121
    const evaluatedCell = { ...referrerCell, value: evaluatedValue };
×
122
    nextEvaluatedData = Matrix.set(referrer, evaluatedCell, nextEvaluatedData);
×
123
  }
124

125
  return nextEvaluatedData;
5✔
126
}
127

128
/**
129
 *
130
 * @param data - the spreadsheet data
131
 * @returns the spreadsheet reference graph
132
 */
133
export function createReferenceGraph(
11✔
134
  data: Matrix.Matrix<CellBase>
135
): PointGraph {
136
  const entries: Array<[Point, PointSet]> = [];
32✔
137
  for (const [point, cell] of Matrix.entries(data)) {
32✔
138
    if (cell && Formula.isFormulaValue(cell.value)) {
268!
139
      const references = getReferences(
×
140
        Formula.extractFormula(cell.value),
141
        point,
142
        data
143
      );
144
      entries.push([point, references]);
×
145
    }
146
  }
147
  return PointGraph.from(entries);
32✔
148
}
149

150
export function createEvaluatedData<Cell extends CellBase>(
11✔
151
  data: Matrix.Matrix<Cell>,
152
  referenceGraph: PointGraph,
153
  parserConstructor?: (getData: () => Matrix.Matrix<CellBase>) => FormulaParser
154
): Matrix.Matrix<Cell> {
155
  let evaluatedData = data;
32✔
156

157
  const formulaParser = (parserConstructor ?? Formula.createBoundFormulaParser)(
32!
158
    () => evaluatedData
×
159
  );
160

161
  // Iterate over the points in the reference graph, starting from the leaves
162
  for (const point of referenceGraph.traverseBFS()) {
32✔
163
    // Get the cell at the current point in the data Matrix
164
    const cell = Matrix.get(point, data);
×
165
    if (!cell) {
×
166
      continue;
×
167
    }
168

169
    // If the cell is a formula cell, evaluate it
170
    if (Formula.isFormulaValue(cell.value)) {
×
171
      const evaluatedValue = getFormulaComputedValue(
×
172
        cell.value,
173
        point,
174
        formulaParser
175
      );
176
      evaluatedData = Matrix.set(
×
177
        point,
178
        { ...cell, value: evaluatedValue },
179
        evaluatedData
180
      );
181
    }
182
  }
183

184
  return evaluatedData;
32✔
185
}
186

187
/** Get the computed value of a formula cell */
188
export function getFormulaComputedValue(
11✔
189
  value: string,
190
  point: Point,
191
  formulaParser: FormulaParser
192
): Value {
193
  const formula = Formula.extractFormula(value);
4✔
194
  try {
4✔
195
    return Formula.evaluate(formula, point, formulaParser);
4✔
196
  } catch (e) {
197
    return FormulaError.REF;
×
198
  }
199
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc