@ricsam/formula-engine 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/engine.cjs +12 -8
- package/dist/cjs/core/engine.cjs.map +3 -3
- package/dist/cjs/core/managers/dependency-manager.cjs +8 -8
- package/dist/cjs/core/managers/dependency-manager.cjs.map +3 -3
- package/dist/cjs/core/managers/evaluation-manager.cjs +10 -10
- package/dist/cjs/core/managers/evaluation-manager.cjs.map +2 -2
- package/dist/cjs/core/managers/frontier-dependency-manager.cjs.map +1 -1
- package/dist/cjs/core/managers/isDependencyNode.cjs +6 -6
- package/dist/cjs/core/managers/isDependencyNode.cjs.map +2 -2
- package/dist/cjs/core/managers/named-expression-manager.cjs.map +1 -1
- package/dist/cjs/core/managers/range-eval-order-builder.cjs +2 -2
- package/dist/cjs/core/managers/range-eval-order-builder.cjs.map +3 -3
- package/dist/cjs/core/managers/workbook-manager.cjs +3 -3
- package/dist/cjs/core/managers/workbook-manager.cjs.map +2 -2
- package/dist/cjs/core/types.cjs.map +2 -2
- package/dist/cjs/core/utils.cjs +2 -2
- package/dist/cjs/core/utils.cjs.map +2 -2
- package/dist/cjs/evaluator/arithmetic/add/add.cjs +2 -2
- package/dist/cjs/evaluator/arithmetic/add/add.cjs.map +2 -2
- package/dist/cjs/evaluator/arithmetic/divide/divide.cjs +2 -2
- package/dist/cjs/evaluator/arithmetic/divide/divide.cjs.map +2 -2
- package/dist/cjs/evaluator/arithmetic/multiply/multiply.cjs +2 -2
- package/dist/cjs/evaluator/arithmetic/multiply/multiply.cjs.map +2 -2
- package/dist/cjs/evaluator/arithmetic/power/power.cjs +2 -2
- package/dist/cjs/evaluator/arithmetic/power/power.cjs.map +2 -2
- package/dist/cjs/evaluator/arithmetic/subtract/subtract.cjs +2 -2
- package/dist/cjs/evaluator/arithmetic/subtract/subtract.cjs.map +2 -2
- package/dist/cjs/evaluator/comparison/equals.cjs.map +1 -1
- package/dist/cjs/evaluator/comparison/greater-than-or-equal.cjs +2 -2
- package/dist/cjs/evaluator/comparison/greater-than-or-equal.cjs.map +2 -2
- package/dist/cjs/evaluator/comparison/greater-than.cjs +2 -2
- package/dist/cjs/evaluator/comparison/greater-than.cjs.map +2 -2
- package/dist/cjs/evaluator/comparison/less-than-or-equal.cjs +2 -2
- package/dist/cjs/evaluator/comparison/less-than-or-equal.cjs.map +2 -2
- package/dist/cjs/evaluator/comparison/less-than.cjs +2 -2
- package/dist/cjs/evaluator/comparison/less-than.cjs.map +2 -2
- package/dist/cjs/evaluator/comparison/not-equals.cjs +2 -2
- package/dist/cjs/evaluator/comparison/not-equals.cjs.map +2 -2
- package/dist/cjs/evaluator/concatenation/concatenate.cjs +2 -2
- package/dist/cjs/evaluator/concatenation/concatenate.cjs.map +2 -2
- package/dist/cjs/evaluator/dependency-nodes/ast-evaluation-node.cjs +2 -2
- package/dist/cjs/evaluator/dependency-nodes/ast-evaluation-node.cjs.map +2 -2
- package/dist/cjs/evaluator/dependency-nodes/base-eval-node.cjs.map +2 -2
- package/dist/cjs/evaluator/dependency-nodes/cell-value-node.cjs +2 -2
- package/dist/cjs/evaluator/dependency-nodes/cell-value-node.cjs.map +2 -2
- package/dist/cjs/evaluator/dependency-nodes/empty-cell-evaluation-node.cjs +3 -3
- package/dist/cjs/evaluator/dependency-nodes/empty-cell-evaluation-node.cjs.map +2 -2
- package/dist/cjs/evaluator/dependency-nodes/spill-meta-node.cjs +2 -2
- package/dist/cjs/evaluator/dependency-nodes/spill-meta-node.cjs.map +2 -2
- package/dist/cjs/evaluator/evaluate-scalar-operator.cjs.map +1 -1
- package/dist/cjs/evaluator/evaluation-context.cjs.map +2 -2
- package/dist/cjs/evaluator/evaluation-error.cjs.map +1 -1
- package/dist/cjs/evaluator/formula-evaluator.cjs +5 -5
- package/dist/cjs/evaluator/formula-evaluator.cjs.map +3 -3
- package/dist/cjs/evaluator/range-evaluation-node.cjs +3 -3
- package/dist/cjs/evaluator/range-evaluation-node.cjs.map +2 -2
- package/dist/cjs/functions/_template/template.cjs.map +1 -1
- package/dist/cjs/functions/array/sequence/sequence.cjs +4 -4
- package/dist/cjs/functions/array/sequence/sequence.cjs.map +2 -2
- package/dist/cjs/functions/criteria-parser.cjs.map +1 -1
- package/dist/cjs/functions/criteria-utils.cjs +4 -4
- package/dist/cjs/functions/criteria-utils.cjs.map +3 -3
- package/dist/cjs/functions/function-utils.cjs.map +1 -1
- package/dist/cjs/functions/index.cjs.map +1 -1
- package/dist/cjs/functions/information/cell/cell.cjs +3 -3
- package/dist/cjs/functions/information/cell/cell.cjs.map +2 -2
- package/dist/cjs/functions/information/column/column.cjs +4 -4
- package/dist/cjs/functions/information/column/column.cjs.map +2 -2
- package/dist/cjs/functions/information/row/row.cjs +4 -4
- package/dist/cjs/functions/information/row/row.cjs.map +2 -2
- package/dist/cjs/functions/logical/and/and.cjs +2 -2
- package/dist/cjs/functions/logical/and/and.cjs.map +2 -2
- package/dist/cjs/functions/logical/if/if.cjs +2 -2
- package/dist/cjs/functions/logical/if/if.cjs.map +3 -3
- package/dist/cjs/functions/logical/iferror/iferror.cjs +3 -3
- package/dist/cjs/functions/logical/iferror/iferror.cjs.map +2 -2
- package/dist/cjs/functions/logical/or/or.cjs +2 -2
- package/dist/cjs/functions/logical/or/or.cjs.map +2 -2
- package/dist/cjs/functions/lookup/count/count-utils.cjs.map +1 -1
- package/dist/cjs/functions/lookup/count/count.cjs +2 -2
- package/dist/cjs/functions/lookup/count/count.cjs.map +2 -2
- package/dist/cjs/functions/lookup/count/countif.cjs +2 -2
- package/dist/cjs/functions/lookup/count/countif.cjs.map +2 -2
- package/dist/cjs/functions/lookup/count/countifs.cjs.map +1 -1
- package/dist/cjs/functions/lookup/index-lookup/index-lookup.cjs +2 -2
- package/dist/cjs/functions/lookup/index-lookup/index-lookup.cjs.map +2 -2
- package/dist/cjs/functions/lookup/match/match.cjs +2 -2
- package/dist/cjs/functions/lookup/match/match.cjs.map +2 -2
- package/dist/cjs/functions/lookup/xlookup/xlookup.cjs +2 -2
- package/dist/cjs/functions/lookup/xlookup/xlookup.cjs.map +2 -2
- package/dist/cjs/functions/math/average/average-utils.cjs +2 -2
- package/dist/cjs/functions/math/average/average-utils.cjs.map +2 -2
- package/dist/cjs/functions/math/average/average.cjs +2 -2
- package/dist/cjs/functions/math/average/average.cjs.map +2 -2
- package/dist/cjs/functions/math/average/averageif.cjs +3 -3
- package/dist/cjs/functions/math/average/averageif.cjs.map +3 -3
- package/dist/cjs/functions/math/average/averageifs.cjs.map +2 -2
- package/dist/cjs/functions/math/ceiling/ceiling.cjs +2 -2
- package/dist/cjs/functions/math/ceiling/ceiling.cjs.map +3 -3
- package/dist/cjs/functions/math/max/max-utils.cjs +2 -2
- package/dist/cjs/functions/math/max/max-utils.cjs.map +2 -2
- package/dist/cjs/functions/math/max/max.cjs +2 -2
- package/dist/cjs/functions/math/max/max.cjs.map +2 -2
- package/dist/cjs/functions/math/max/maxif.cjs +2 -2
- package/dist/cjs/functions/math/max/maxif.cjs.map +2 -2
- package/dist/cjs/functions/math/max/maxifs.cjs.map +2 -2
- package/dist/cjs/functions/math/min/min-utils.cjs +2 -2
- package/dist/cjs/functions/math/min/min-utils.cjs.map +2 -2
- package/dist/cjs/functions/math/min/min.cjs +2 -2
- package/dist/cjs/functions/math/min/min.cjs.map +3 -3
- package/dist/cjs/functions/math/min/minif.cjs +2 -2
- package/dist/cjs/functions/math/min/minif.cjs.map +2 -2
- package/dist/cjs/functions/math/min/minifs.cjs.map +2 -2
- package/dist/cjs/functions/math/sum/sum.cjs +2 -2
- package/dist/cjs/functions/math/sum/sum.cjs.map +2 -2
- package/dist/cjs/functions/math/sum/sumif.cjs +2 -2
- package/dist/cjs/functions/math/sum/sumif.cjs.map +2 -2
- package/dist/cjs/functions/math/sum/sumifs.cjs.map +2 -2
- package/dist/cjs/functions/math/sum/summation-utils.cjs.map +1 -1
- package/dist/cjs/functions/reference/address/address.cjs +2 -2
- package/dist/cjs/functions/reference/address/address.cjs.map +2 -2
- package/dist/cjs/functions/reference/indirect/indirect.cjs +3 -3
- package/dist/cjs/functions/reference/indirect/indirect.cjs.map +3 -3
- package/dist/cjs/functions/reference/offset/offset.cjs +3 -3
- package/dist/cjs/functions/reference/offset/offset.cjs.map +2 -2
- package/dist/cjs/functions/text/concatenate/concatenate.cjs +2 -2
- package/dist/cjs/functions/text/concatenate/concatenate.cjs.map +2 -2
- package/dist/cjs/functions/text/exact/exact.cjs +2 -2
- package/dist/cjs/functions/text/exact/exact.cjs.map +2 -2
- package/dist/cjs/functions/text/find/find.cjs +2 -2
- package/dist/cjs/functions/text/find/find.cjs.map +3 -3
- package/dist/cjs/functions/text/left/left.cjs +2 -2
- package/dist/cjs/functions/text/left/left.cjs.map +2 -2
- package/dist/cjs/functions/text/len/len.cjs +2 -2
- package/dist/cjs/functions/text/len/len.cjs.map +3 -3
- package/dist/cjs/functions/text/mid/mid.cjs +2 -2
- package/dist/cjs/functions/text/mid/mid.cjs.map +2 -2
- package/dist/cjs/functions/text/right/right.cjs +2 -2
- package/dist/cjs/functions/text/right/right.cjs.map +3 -3
- package/dist/cjs/functions/text/text-helpers.cjs +2 -2
- package/dist/cjs/functions/text/text-helpers.cjs.map +2 -2
- package/dist/cjs/functions/text/textjoin/textjoin.cjs +2 -2
- package/dist/cjs/functions/text/textjoin/textjoin.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/parser/formatter.cjs +2 -2
- package/dist/cjs/parser/formatter.cjs.map +2 -2
- package/dist/mjs/core/engine.mjs +7 -9
- package/dist/mjs/core/engine.mjs.map +3 -3
- package/dist/mjs/core/managers/dependency-manager.mjs +8 -8
- package/dist/mjs/core/managers/dependency-manager.mjs.map +3 -3
- package/dist/mjs/core/managers/evaluation-manager.mjs +10 -10
- package/dist/mjs/core/managers/evaluation-manager.mjs.map +2 -2
- package/dist/mjs/core/managers/frontier-dependency-manager.mjs.map +1 -1
- package/dist/mjs/core/managers/isDependencyNode.mjs +6 -6
- package/dist/mjs/core/managers/isDependencyNode.mjs.map +2 -2
- package/dist/mjs/core/managers/named-expression-manager.mjs.map +1 -1
- package/dist/mjs/core/managers/range-eval-order-builder.mjs +2 -2
- package/dist/mjs/core/managers/range-eval-order-builder.mjs.map +3 -3
- package/dist/mjs/core/managers/workbook-manager.mjs +3 -3
- package/dist/mjs/core/managers/workbook-manager.mjs.map +2 -2
- package/dist/mjs/core/types.mjs.map +2 -2
- package/dist/mjs/core/utils.mjs +2 -2
- package/dist/mjs/core/utils.mjs.map +2 -2
- package/dist/mjs/evaluator/arithmetic/add/add.mjs +2 -2
- package/dist/mjs/evaluator/arithmetic/add/add.mjs.map +2 -2
- package/dist/mjs/evaluator/arithmetic/divide/divide.mjs +2 -2
- package/dist/mjs/evaluator/arithmetic/divide/divide.mjs.map +2 -2
- package/dist/mjs/evaluator/arithmetic/multiply/multiply.mjs +2 -2
- package/dist/mjs/evaluator/arithmetic/multiply/multiply.mjs.map +2 -2
- package/dist/mjs/evaluator/arithmetic/power/power.mjs +2 -2
- package/dist/mjs/evaluator/arithmetic/power/power.mjs.map +2 -2
- package/dist/mjs/evaluator/arithmetic/subtract/subtract.mjs +2 -2
- package/dist/mjs/evaluator/arithmetic/subtract/subtract.mjs.map +2 -2
- package/dist/mjs/evaluator/comparison/equals.mjs.map +1 -1
- package/dist/mjs/evaluator/comparison/greater-than-or-equal.mjs +2 -2
- package/dist/mjs/evaluator/comparison/greater-than-or-equal.mjs.map +2 -2
- package/dist/mjs/evaluator/comparison/greater-than.mjs +2 -2
- package/dist/mjs/evaluator/comparison/greater-than.mjs.map +2 -2
- package/dist/mjs/evaluator/comparison/less-than-or-equal.mjs +2 -2
- package/dist/mjs/evaluator/comparison/less-than-or-equal.mjs.map +2 -2
- package/dist/mjs/evaluator/comparison/less-than.mjs +2 -2
- package/dist/mjs/evaluator/comparison/less-than.mjs.map +2 -2
- package/dist/mjs/evaluator/comparison/not-equals.mjs +2 -2
- package/dist/mjs/evaluator/comparison/not-equals.mjs.map +2 -2
- package/dist/mjs/evaluator/concatenation/concatenate.mjs +2 -2
- package/dist/mjs/evaluator/concatenation/concatenate.mjs.map +2 -2
- package/dist/mjs/evaluator/dependency-nodes/ast-evaluation-node.mjs +2 -2
- package/dist/mjs/evaluator/dependency-nodes/ast-evaluation-node.mjs.map +2 -2
- package/dist/mjs/evaluator/dependency-nodes/base-eval-node.mjs.map +2 -2
- package/dist/mjs/evaluator/dependency-nodes/cell-value-node.mjs +2 -2
- package/dist/mjs/evaluator/dependency-nodes/cell-value-node.mjs.map +2 -2
- package/dist/mjs/evaluator/dependency-nodes/empty-cell-evaluation-node.mjs +3 -3
- package/dist/mjs/evaluator/dependency-nodes/empty-cell-evaluation-node.mjs.map +2 -2
- package/dist/mjs/evaluator/dependency-nodes/spill-meta-node.mjs +2 -2
- package/dist/mjs/evaluator/dependency-nodes/spill-meta-node.mjs.map +2 -2
- package/dist/mjs/evaluator/evaluate-scalar-operator.mjs.map +1 -1
- package/dist/mjs/evaluator/evaluation-context.mjs.map +2 -2
- package/dist/mjs/evaluator/evaluation-error.mjs.map +1 -1
- package/dist/mjs/evaluator/formula-evaluator.mjs +7 -7
- package/dist/mjs/evaluator/formula-evaluator.mjs.map +3 -3
- package/dist/mjs/evaluator/range-evaluation-node.mjs +3 -3
- package/dist/mjs/evaluator/range-evaluation-node.mjs.map +2 -2
- package/dist/mjs/functions/_template/template.mjs.map +1 -1
- package/dist/mjs/functions/array/sequence/sequence.mjs +4 -4
- package/dist/mjs/functions/array/sequence/sequence.mjs.map +2 -2
- package/dist/mjs/functions/criteria-parser.mjs.map +1 -1
- package/dist/mjs/functions/criteria-utils.mjs +8 -8
- package/dist/mjs/functions/criteria-utils.mjs.map +3 -3
- package/dist/mjs/functions/function-utils.mjs.map +1 -1
- package/dist/mjs/functions/index.mjs.map +1 -1
- package/dist/mjs/functions/information/cell/cell.mjs +3 -3
- package/dist/mjs/functions/information/cell/cell.mjs.map +2 -2
- package/dist/mjs/functions/information/column/column.mjs +4 -4
- package/dist/mjs/functions/information/column/column.mjs.map +2 -2
- package/dist/mjs/functions/information/row/row.mjs +4 -4
- package/dist/mjs/functions/information/row/row.mjs.map +2 -2
- package/dist/mjs/functions/logical/and/and.mjs +2 -2
- package/dist/mjs/functions/logical/and/and.mjs.map +2 -2
- package/dist/mjs/functions/logical/if/if.mjs +2 -2
- package/dist/mjs/functions/logical/if/if.mjs.map +3 -3
- package/dist/mjs/functions/logical/iferror/iferror.mjs +3 -3
- package/dist/mjs/functions/logical/iferror/iferror.mjs.map +2 -2
- package/dist/mjs/functions/logical/or/or.mjs +2 -2
- package/dist/mjs/functions/logical/or/or.mjs.map +2 -2
- package/dist/mjs/functions/lookup/count/count-utils.mjs.map +1 -1
- package/dist/mjs/functions/lookup/count/count.mjs +2 -2
- package/dist/mjs/functions/lookup/count/count.mjs.map +2 -2
- package/dist/mjs/functions/lookup/count/countif.mjs +2 -2
- package/dist/mjs/functions/lookup/count/countif.mjs.map +2 -2
- package/dist/mjs/functions/lookup/count/countifs.mjs.map +1 -1
- package/dist/mjs/functions/lookup/index-lookup/index-lookup.mjs +2 -2
- package/dist/mjs/functions/lookup/index-lookup/index-lookup.mjs.map +2 -2
- package/dist/mjs/functions/lookup/match/match.mjs +2 -2
- package/dist/mjs/functions/lookup/match/match.mjs.map +2 -2
- package/dist/mjs/functions/lookup/xlookup/xlookup.mjs +2 -2
- package/dist/mjs/functions/lookup/xlookup/xlookup.mjs.map +2 -2
- package/dist/mjs/functions/math/average/average-utils.mjs +2 -2
- package/dist/mjs/functions/math/average/average-utils.mjs.map +2 -2
- package/dist/mjs/functions/math/average/average.mjs +2 -2
- package/dist/mjs/functions/math/average/average.mjs.map +2 -2
- package/dist/mjs/functions/math/average/averageif.mjs +3 -3
- package/dist/mjs/functions/math/average/averageif.mjs.map +3 -3
- package/dist/mjs/functions/math/average/averageifs.mjs.map +2 -2
- package/dist/mjs/functions/math/ceiling/ceiling.mjs +2 -2
- package/dist/mjs/functions/math/ceiling/ceiling.mjs.map +3 -3
- package/dist/mjs/functions/math/max/max-utils.mjs +2 -2
- package/dist/mjs/functions/math/max/max-utils.mjs.map +2 -2
- package/dist/mjs/functions/math/max/max.mjs +2 -2
- package/dist/mjs/functions/math/max/max.mjs.map +2 -2
- package/dist/mjs/functions/math/max/maxif.mjs +2 -2
- package/dist/mjs/functions/math/max/maxif.mjs.map +2 -2
- package/dist/mjs/functions/math/max/maxifs.mjs.map +2 -2
- package/dist/mjs/functions/math/min/min-utils.mjs +2 -2
- package/dist/mjs/functions/math/min/min-utils.mjs.map +2 -2
- package/dist/mjs/functions/math/min/min.mjs +2 -2
- package/dist/mjs/functions/math/min/min.mjs.map +2 -2
- package/dist/mjs/functions/math/min/minif.mjs +2 -2
- package/dist/mjs/functions/math/min/minif.mjs.map +2 -2
- package/dist/mjs/functions/math/min/minifs.mjs.map +2 -2
- package/dist/mjs/functions/math/sum/sum.mjs +2 -2
- package/dist/mjs/functions/math/sum/sum.mjs.map +2 -2
- package/dist/mjs/functions/math/sum/sumif.mjs +2 -2
- package/dist/mjs/functions/math/sum/sumif.mjs.map +2 -2
- package/dist/mjs/functions/math/sum/sumifs.mjs.map +2 -2
- package/dist/mjs/functions/math/sum/summation-utils.mjs.map +1 -1
- package/dist/mjs/functions/reference/address/address.mjs +2 -2
- package/dist/mjs/functions/reference/address/address.mjs.map +2 -2
- package/dist/mjs/functions/reference/indirect/indirect.mjs +3 -3
- package/dist/mjs/functions/reference/indirect/indirect.mjs.map +3 -3
- package/dist/mjs/functions/reference/offset/offset.mjs +3 -3
- package/dist/mjs/functions/reference/offset/offset.mjs.map +2 -2
- package/dist/mjs/functions/text/concatenate/concatenate.mjs +2 -2
- package/dist/mjs/functions/text/concatenate/concatenate.mjs.map +2 -2
- package/dist/mjs/functions/text/exact/exact.mjs +2 -2
- package/dist/mjs/functions/text/exact/exact.mjs.map +2 -2
- package/dist/mjs/functions/text/find/find.mjs +2 -2
- package/dist/mjs/functions/text/find/find.mjs.map +3 -3
- package/dist/mjs/functions/text/left/left.mjs +3 -3
- package/dist/mjs/functions/text/left/left.mjs.map +2 -2
- package/dist/mjs/functions/text/len/len.mjs +2 -2
- package/dist/mjs/functions/text/len/len.mjs.map +3 -3
- package/dist/mjs/functions/text/mid/mid.mjs +3 -3
- package/dist/mjs/functions/text/mid/mid.mjs.map +2 -2
- package/dist/mjs/functions/text/right/right.mjs +4 -4
- package/dist/mjs/functions/text/right/right.mjs.map +3 -3
- package/dist/mjs/functions/text/text-helpers.mjs +2 -2
- package/dist/mjs/functions/text/text-helpers.mjs.map +2 -2
- package/dist/mjs/functions/text/textjoin/textjoin.mjs +2 -2
- package/dist/mjs/functions/text/textjoin/textjoin.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/parser/formatter.mjs +2 -2
- package/dist/mjs/parser/formatter.mjs.map +2 -2
- package/dist/types/core/engine.d.ts +5 -1
- package/dist/types/core/managers/dependency-manager.d.ts +7 -7
- package/dist/types/core/managers/dependency-node.d.ts +5 -5
- package/dist/types/core/managers/evaluation-manager.d.ts +4 -4
- package/dist/types/core/managers/frontier-dependency-manager.d.ts +2 -2
- package/dist/types/core/managers/named-expression-manager.d.ts +2 -2
- package/dist/types/core/managers/workbook-manager.d.ts +1 -1
- package/dist/types/core/types.d.ts +3 -3
- package/dist/types/evaluator/arithmetic/add/add.d.ts +1 -1
- package/dist/types/evaluator/arithmetic/divide/divide.d.ts +1 -1
- package/dist/types/evaluator/arithmetic/multiply/multiply.d.ts +1 -1
- package/dist/types/evaluator/arithmetic/power/power.d.ts +1 -1
- package/dist/types/evaluator/arithmetic/subtract/subtract.d.ts +1 -1
- package/dist/types/evaluator/comparison/equals.d.ts +1 -1
- package/dist/types/evaluator/comparison/greater-than-or-equal.d.ts +1 -1
- package/dist/types/evaluator/comparison/greater-than.d.ts +1 -1
- package/dist/types/evaluator/comparison/less-than-or-equal.d.ts +1 -1
- package/dist/types/evaluator/comparison/less-than.d.ts +1 -1
- package/dist/types/evaluator/comparison/not-equals.d.ts +1 -1
- package/dist/types/evaluator/concatenation/concatenate.d.ts +1 -1
- package/dist/types/evaluator/dependency-nodes/ast-evaluation-node.d.ts +2 -2
- package/dist/types/evaluator/dependency-nodes/base-eval-node.d.ts +1 -1
- package/dist/types/evaluator/dependency-nodes/cell-value-node.d.ts +1 -1
- package/dist/types/evaluator/dependency-nodes/empty-cell-evaluation-node.d.ts +4 -4
- package/dist/types/evaluator/dependency-nodes/spill-meta-node.d.ts +1 -1
- package/dist/types/evaluator/evaluate-scalar-operator.d.ts +1 -1
- package/dist/types/evaluator/evaluation-context.d.ts +3 -3
- package/dist/types/evaluator/evaluation-error.d.ts +2 -2
- package/dist/types/evaluator/formula-evaluator.d.ts +5 -5
- package/dist/types/evaluator/range-evaluation-node.d.ts +4 -4
- package/dist/types/functions/_template/template.d.ts +1 -1
- package/dist/types/functions/array/sequence/sequence.d.ts +1 -1
- package/dist/types/functions/criteria-parser.d.ts +1 -1
- package/dist/types/functions/criteria-utils.d.ts +5 -5
- package/dist/types/functions/function-utils.d.ts +5 -5
- package/dist/types/functions/index.d.ts +1 -1
- package/dist/types/functions/information/cell/cell.d.ts +1 -1
- package/dist/types/functions/information/column/column.d.ts +1 -1
- package/dist/types/functions/information/row/row.d.ts +1 -1
- package/dist/types/functions/logical/and/and.d.ts +1 -1
- package/dist/types/functions/logical/if/if.d.ts +1 -1
- package/dist/types/functions/logical/iferror/iferror.d.ts +1 -1
- package/dist/types/functions/logical/or/or.d.ts +1 -1
- package/dist/types/functions/lookup/count/count-utils.d.ts +2 -2
- package/dist/types/functions/lookup/count/count.d.ts +1 -1
- package/dist/types/functions/lookup/count/countif.d.ts +1 -1
- package/dist/types/functions/lookup/count/countifs.d.ts +1 -1
- package/dist/types/functions/lookup/index-lookup/index-lookup.d.ts +1 -1
- package/dist/types/functions/lookup/match/match.d.ts +1 -1
- package/dist/types/functions/lookup/xlookup/xlookup.d.ts +1 -1
- package/dist/types/functions/math/average/average-utils.d.ts +2 -2
- package/dist/types/functions/math/average/average.d.ts +1 -1
- package/dist/types/functions/math/average/averageif.d.ts +1 -1
- package/dist/types/functions/math/average/averageifs.d.ts +1 -1
- package/dist/types/functions/math/ceiling/ceiling.d.ts +1 -1
- package/dist/types/functions/math/max/max-utils.d.ts +2 -2
- package/dist/types/functions/math/max/max.d.ts +1 -1
- package/dist/types/functions/math/max/maxif.d.ts +1 -1
- package/dist/types/functions/math/max/maxifs.d.ts +1 -1
- package/dist/types/functions/math/min/min-utils.d.ts +2 -2
- package/dist/types/functions/math/min/min.d.ts +1 -1
- package/dist/types/functions/math/min/minif.d.ts +1 -1
- package/dist/types/functions/math/min/minifs.d.ts +1 -1
- package/dist/types/functions/math/sum/sum.d.ts +1 -1
- package/dist/types/functions/math/sum/sumif.d.ts +1 -1
- package/dist/types/functions/math/sum/sumifs.d.ts +1 -1
- package/dist/types/functions/math/sum/summation-utils.d.ts +1 -1
- package/dist/types/functions/reference/address/address.d.ts +1 -1
- package/dist/types/functions/reference/indirect/indirect.d.ts +1 -1
- package/dist/types/functions/reference/offset/offset.d.ts +1 -1
- package/dist/types/functions/text/concatenate/concatenate.d.ts +1 -1
- package/dist/types/functions/text/exact/exact.d.ts +1 -1
- package/dist/types/functions/text/find/find.d.ts +1 -1
- package/dist/types/functions/text/left/left.d.ts +1 -1
- package/dist/types/functions/text/len/len.d.ts +1 -1
- package/dist/types/functions/text/mid/mid.d.ts +1 -1
- package/dist/types/functions/text/right/right.d.ts +1 -1
- package/dist/types/functions/text/text-helpers.d.ts +3 -3
- package/dist/types/functions/text/textjoin/textjoin.d.ts +1 -1
- package/package.json +1 -1
- package/dist/cjs/core/managers/index.cjs +0 -49
- package/dist/cjs/core/managers/index.cjs.map +0 -10
- package/dist/mjs/core/managers/index.mjs +0 -21
- package/dist/mjs/core/managers/index.mjs.map +0 -10
- package/dist/types/core/managers/index.d.ts +0 -7
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/core/managers/range-eval-order-builder.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { flags } from \"src/debug/flags\";\nimport {\n FormulaError,\n type CellAddress,\n type RangeAddress,\n type SerializedCellValue,\n type Sheet,\n} from \"../types.mjs\";\nimport { getCellReference, parseCellReference } from \"../utils.mjs\";\nimport {\n type WorkbookManager,\n type SheetIndexes,\n IndexEntryBinarySearch,\n} from \"./workbook-manager.mjs\";\nimport { EvaluationError } from \"src/evaluator/evaluation-error\";\n\nexport type LookupOrder = \"row-major\" | \"col-major\";\n\nexport type RangeEvalOrderEntryDict = {\n value: {\n type: \"value\";\n address: CellAddress;\n };\n empty_cell: {\n type: \"empty_cell\";\n address: CellAddress;\n candidates: CellAddress[];\n };\n empty_range: {\n type: \"empty_range\";\n address: RangeAddress;\n candidates: CellAddress[];\n };\n};\n\nexport type RangeEvalOrderEntry =\n | RangeEvalOrderEntryDict[\"value\"]\n | RangeEvalOrderEntryDict[\"empty_cell\"]\n | RangeEvalOrderEntryDict[\"empty_range\"];\n\n/**\n * Build a deterministic, ordered list describing every cell inside a lookup range.\n * This function analyzes the range and classifies each cell/range as either:\n * - A value-like cell (occupied)\n * - An empty cell with 0-2 frontier candidates\n * - An empty range with 0-2 frontier candidates\n *\n * @param this - WorkbookManager instance\n * @param lookupOrder - \"row-major\" or \"col-major\" iteration order\n * @param lookupRange - The range to analyze\n * @returns Ordered array of entries describing the range\n */\nexport function buildRangeEvalOrder(\n this: WorkbookManager,\n lookupOrder: LookupOrder,\n lookupRange: RangeAddress\n): RangeEvalOrderEntry[] {\n const sheet = this.getSheet(lookupRange);\n if (!sheet) {\n throw new EvaluationError(\n FormulaError.REF,\n `Sheet ${lookupRange.sheetName} not found`\n );\n }\n\n const result: RangeEvalOrderEntry[] = [];\n\n // Get range bounds\n const startRow = lookupRange.range.start.row;\n const startCol = lookupRange.range.start.col;\n\n // Build a map of occupied cells within the range\n const occupiedCells = new Map<string, CellAddress>();\n let maxRow = startRow;\n let maxCol = startCol;\n\n for (const cellAddr of this.iterateCellsInRange(lookupRange)) {\n occupiedCells.set(getCellReference(cellAddr), cellAddr);\n maxRow = Math.max(maxRow, cellAddr.rowIndex);\n maxCol = Math.max(maxCol, cellAddr.colIndex);\n }\n\n // For infinite ranges, also consider ALL occupied cells in the sheet\n // (including frontier candidates and their spills) to determine extent\n if (\n lookupRange.range.end.row.type === \"infinity\" ||\n lookupRange.range.end.col.type === \"infinity\"\n ) {\n // Get all cells in the sheet to find the full extent\n const allCells = this.iterateCellsInRange({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row: 0, col: 0 },\n end: {\n row: { type: \"infinity\", sign: \"positive\" },\n col: { type: \"infinity\", sign: \"positive\" },\n },\n },\n });\n\n for (const cellAddr of allCells) {\n // Consider all occupied cells (formulas and values) to determine extent\n maxRow = Math.max(maxRow, cellAddr.rowIndex);\n maxCol = Math.max(maxCol, cellAddr.colIndex);\n }\n }\n\n // Determine the effective end bounds\n // For finite ranges, use the specified end\n // For infinite ranges, use the maximum cell found (including frontier candidates)\n const endRow =\n lookupRange.range.end.row.type === \"number\"\n ? lookupRange.range.end.row.value\n : maxRow;\n const endCol =\n lookupRange.range.end.col.type === \"number\"\n ? lookupRange.range.end.col.value\n : maxCol;\n\n // Optimization: If the lookup range is infinite and has NO occupied cells within it,\n // emit a single infinite range with ALL candidates from the sheet that could affect it\n const isInfinite =\n lookupRange.range.end.row.type === \"infinity\" ||\n lookupRange.range.end.col.type === \"infinity\";\n\n if (isInfinite && occupiedCells.size === 0) {\n // Find all formula candidates that could spill into this range\n // by checking formulas to the left and above of the range\n const leftCandidates = new Map<string, CellAddress>();\n const aboveCandidates = new Map<string, CellAddress>();\n const diagonalCandidates = new Map<string, CellAddress>();\n\n // Iterate all cells in the sheet to find candidates\n // (formulas to the left or above that could spill into the range)\n const allCells = this.iterateCellsInRange({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row: 0, col: 0 },\n end: {\n row: { type: \"infinity\", sign: \"positive\" },\n col: { type: \"infinity\", sign: \"positive\" },\n },\n },\n });\n\n for (const cellAddr of allCells) {\n const cellRef = getCellReference(cellAddr);\n const content = sheet.content.get(cellRef);\n\n // Only consider formula cells\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n // Check if this formula could spill into the lookup range\n // A formula at (r, c) can spill into a range starting at (startRow, startCol) if:\n // 1. It's directly to the left: c < startCol AND r >= startRow (same row or below)\n // 2. It's directly above: r < startRow AND c >= startCol (same column or to the right)\n // 3. It's diagonal: r < startRow AND c < startCol (can spill down-right)\n\n const isLeftCandidate =\n cellAddr.colIndex < startCol && cellAddr.rowIndex >= startRow;\n const isAboveCandidate =\n cellAddr.rowIndex < startRow && cellAddr.colIndex >= startCol;\n const isDiagonalCandidate =\n cellAddr.rowIndex < startRow && cellAddr.colIndex < startCol;\n\n if (isLeftCandidate) {\n leftCandidates.set(cellRef, cellAddr);\n } else if (isAboveCandidate) {\n aboveCandidates.set(cellRef, cellAddr);\n } else if (isDiagonalCandidate) {\n diagonalCandidates.set(cellRef, cellAddr);\n }\n }\n }\n\n // Only include diagonal candidates if there are NO direct left/above candidates\n const candidateMap = new Map<string, CellAddress>();\n if (leftCandidates.size > 0 || aboveCandidates.size > 0) {\n // Have direct candidates - use only those\n for (const [ref, addr] of leftCandidates) {\n candidateMap.set(ref, addr);\n }\n for (const [ref, addr] of aboveCandidates) {\n candidateMap.set(ref, addr);\n }\n } else {\n // No direct candidates - use diagonals\n for (const [ref, addr] of diagonalCandidates) {\n candidateMap.set(ref, addr);\n }\n }\n\n const candidates = sortCandidates(\n Array.from(candidateMap.values()),\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: lookupRange,\n candidates,\n });\n return result;\n }\n\n if (lookupOrder === \"row-major\") {\n // Get indexes for efficient row/column lookups\n const indexes = this.getSheetIndexes({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n });\n\n // Iterate row by row, left to right\n for (let row = startRow; row <= endRow; row++) {\n // Check if this row has any occupied cells using indexes\n const hasOccupiedInRow = indexes.rowGroups.has(row);\n\n // If the row has no occupied cells and the range is infinite in columns,\n // emit a single infinite range for the entire row\n if (!hasOccupiedInRow && lookupRange.range.end.col.type === \"infinity\") {\n const firstCell: CellAddress = {\n rowIndex: row,\n colIndex: startCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCell,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: row,\n col: startCol,\n },\n end: {\n row: { type: \"number\", value: row },\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n continue; // Skip normal processing for this row\n }\n\n processRowMajorRow.call(\n this,\n row,\n startCol,\n endCol,\n lookupRange,\n occupiedCells,\n sheet,\n result,\n indexes\n );\n\n // If the lookup range is infinite in columns, emit a final infinite range for this row ONLY\n if (lookupRange.range.end.col.type === \"infinity\") {\n const nextCol = endCol + 1;\n const firstCellBeyondEnd: CellAddress = {\n rowIndex: row,\n colIndex: nextCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellBeyondEnd,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: row,\n col: nextCol,\n },\n end: {\n // Always constrain to this row only in row-major\n row: { type: \"number\", value: row },\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n }\n }\n\n // If the lookup range is infinite in rows, emit a final infinite range\n if (lookupRange.range.end.row.type === \"infinity\") {\n // Find candidates for cells beyond the last processed row\n const nextRow = endRow + 1;\n const firstCellInNextRow: CellAddress = {\n rowIndex: nextRow,\n colIndex: startCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellInNextRow,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: nextRow,\n col: startCol,\n },\n end: {\n row: lookupRange.range.end.row,\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n }\n } else {\n // Get indexes for efficient row/column lookups\n const indexes = this.getSheetIndexes({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n });\n\n // col-major: iterate column by column, top to bottom\n for (let col = startCol; col <= endCol; col++) {\n // Check if this column has any occupied cells using indexes\n const hasOccupiedInColumn = indexes.colGroups.has(col);\n\n // If the column has no occupied cells and the range is infinite in rows,\n // emit a single infinite range for the entire column\n if (\n !hasOccupiedInColumn &&\n lookupRange.range.end.row.type === \"infinity\"\n ) {\n const firstCell: CellAddress = {\n rowIndex: startRow,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCell,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: startRow,\n col: col,\n },\n end: {\n row: lookupRange.range.end.row,\n col: { type: \"number\", value: col },\n },\n },\n },\n candidates,\n });\n continue; // Skip normal processing for this column\n }\n\n processColMajorColumn.call(\n this,\n col,\n startRow,\n endRow,\n lookupRange,\n occupiedCells,\n sheet,\n result,\n indexes\n );\n\n // If the lookup range is infinite in rows, emit a final infinite range for this column ONLY\n if (lookupRange.range.end.row.type === \"infinity\") {\n const nextRow = endRow + 1;\n const firstCellBeyondEnd: CellAddress = {\n rowIndex: nextRow,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellBeyondEnd,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: nextRow,\n col: col,\n },\n end: {\n row: lookupRange.range.end.row,\n // Always constrain to this column only in col-major\n col: { type: \"number\", value: col },\n },\n },\n },\n candidates,\n });\n }\n }\n\n // If the lookup range is infinite in columns, emit a final infinite range\n if (lookupRange.range.end.col.type === \"infinity\") {\n // Find candidates for cells beyond the last processed column\n const nextCol = endCol + 1;\n const firstCellInNextCol: CellAddress = {\n rowIndex: startRow,\n colIndex: nextCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellInNextCol,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: startRow,\n col: nextCol,\n },\n end: {\n row: lookupRange.range.end.row,\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n }\n }\n\n return result;\n}\n\n/**\n * Process a single row in row-major order\n */\nfunction processRowMajorRow(\n this: WorkbookManager,\n row: number,\n startCol: number,\n endCol: number,\n lookupRange: RangeAddress,\n occupiedCells: Map<string, CellAddress>,\n sheet: Sheet,\n result: RangeEvalOrderEntry[],\n indexes: SheetIndexes\n): void {\n let col = startCol;\n\n while (col <= endCol) {\n const cellAddr: CellAddress = {\n rowIndex: row,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n const cellRef = getCellReference(cellAddr);\n\n if (occupiedCells.has(cellRef)) {\n // This cell is occupied (has a value or formula)\n result.push({\n type: \"value\",\n address: cellAddr,\n });\n col++;\n } else {\n // This cell is empty - find the contiguous empty range in this row with same candidates\n const emptyRangeStart = col;\n let emptyRangeEnd = endCol;\n\n // Find next occupied cell in this row using binary search (O(log n))\n const rowGroup = indexes.rowGroups.get(row);\n if (rowGroup) {\n // Binary search for the first cell in this row with colIndex > col\n const nextCellIdx = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n rowGroup,\n col + 1\n );\n if (nextCellIdx !== -1) {\n emptyRangeEnd = rowGroup[nextCellIdx]!.number - 1;\n }\n }\n\n // Find ALL unique candidates across the entire empty range\n const allCandidates = findAllCandidatesForRange(\n this,\n {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row, col: emptyRangeStart },\n end: {\n row: { type: \"number\", value: row },\n col: { type: \"number\", value: emptyRangeEnd },\n },\n },\n },\n sheet,\n \"row-major\"\n );\n\n // If the empty range is a single cell, emit as empty_cell\n if (emptyRangeStart === emptyRangeEnd) {\n result.push({\n type: \"empty_cell\",\n address: cellAddr,\n candidates: allCandidates,\n });\n } else {\n // Emit as empty_range\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row,\n col: emptyRangeStart,\n },\n end: {\n row: { type: \"number\", value: row },\n col: { type: \"number\", value: emptyRangeEnd },\n },\n },\n },\n candidates: allCandidates,\n });\n }\n\n col = emptyRangeEnd + 1;\n }\n }\n}\n\n/**\n * Process a single column in col-major order\n */\nfunction processColMajorColumn(\n this: WorkbookManager,\n col: number,\n startRow: number,\n endRow: number,\n lookupRange: RangeAddress,\n occupiedCells: Map<string, CellAddress>,\n sheet: Sheet,\n result: RangeEvalOrderEntry[],\n indexes: SheetIndexes\n): void {\n let row = startRow;\n\n while (row <= endRow) {\n const cellAddr: CellAddress = {\n rowIndex: row,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n const cellRef = getCellReference(cellAddr);\n\n if (occupiedCells.has(cellRef)) {\n // This cell is occupied (has a value or formula)\n result.push({\n type: \"value\",\n address: cellAddr,\n });\n row++;\n } else {\n // This cell is empty - find the contiguous empty range in this column with same candidates\n const emptyRangeStart = row;\n let emptyRangeEnd = endRow;\n\n // Find next occupied cell in this column using binary search (O(log n))\n const colGroup = indexes.colGroups.get(col);\n if (colGroup) {\n // Binary search for the first cell in this column with rowIndex > row\n const nextCellIdx = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n colGroup,\n row + 1\n );\n if (nextCellIdx !== -1) {\n emptyRangeEnd = colGroup[nextCellIdx]!.number - 1;\n }\n }\n\n // Find ALL unique candidates across the entire empty range\n const allCandidates = findAllCandidatesForRange(\n this,\n {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row: emptyRangeStart, col },\n end: {\n row: { type: \"number\", value: emptyRangeEnd },\n col: { type: \"number\", value: col },\n },\n },\n },\n sheet,\n \"col-major\"\n );\n\n // If the empty range is a single cell, emit as empty_cell\n if (emptyRangeStart === emptyRangeEnd) {\n result.push({\n type: \"empty_cell\",\n address: cellAddr,\n candidates: allCandidates,\n });\n } else {\n // Emit as empty_range\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: emptyRangeStart,\n col,\n },\n end: {\n row: { type: \"number\", value: emptyRangeEnd },\n col: { type: \"number\", value: col },\n },\n },\n },\n candidates: allCandidates,\n });\n }\n\n row = emptyRangeEnd + 1;\n }\n }\n}\n\n/**\n * Find ALL unique candidates that could affect any cell in the given empty range\n * Returns the union of candidates from all cells in the range, sorted deterministically\n */\nfunction findAllCandidatesForRange(\n manager: WorkbookManager,\n emptyRange: RangeAddress,\n sheet: Sheet,\n lookupOrder: LookupOrder\n): CellAddress[] {\n const candidateMap = new Map<string, CellAddress>();\n\n const startRow = emptyRange.range.start.row;\n const startCol = emptyRange.range.start.col;\n const endRow =\n emptyRange.range.end.row.type === \"number\"\n ? emptyRange.range.end.row.value\n : startRow;\n const endCol =\n emptyRange.range.end.col.type === \"number\"\n ? emptyRange.range.end.col.value\n : startCol;\n\n // We need to check positions that could have different candidates:\n // - Different columns might have different \"above\" candidates\n // - Different rows might have different \"left\" candidates\n const positionsToCheck: Array<{ row: number; col: number }> = [];\n\n // For single-row ranges: check each column (different above candidates)\n // For single-column ranges: sample rows efficiently\n // For rectangular ranges: check corners only\n\n if (startRow === endRow) {\n // Single row: check each column (each column might have different above candidates)\n for (let col = startCol; col <= endCol; col++) {\n positionsToCheck.push({ row: startRow, col });\n }\n } else if (startCol === endCol) {\n // Single column: For large ranges, sample rows instead of checking all\n // We check where occupied cells exist, plus endpoints\n const maxSamples = 100; // Performance limit\n const numRows = endRow - startRow + 1;\n\n if (numRows <= maxSamples) {\n // Small range: check every row\n for (let row = startRow; row <= endRow; row++) {\n positionsToCheck.push({ row, col: startCol });\n }\n } else {\n // Large range: sample at occupied cell rows + endpoints\n positionsToCheck.push({ row: startRow, col: startCol }); // First\n positionsToCheck.push({ row: endRow, col: startCol }); // Last\n\n // Add samples at rows where there are occupied cells to the left\n // (these are the rows that might have different left candidates)\n // Use indexes to efficiently find occupied cells instead of nested loop\n const indexes = manager.getSheetIndexes({\n workbookName: emptyRange.workbookName,\n sheetName: emptyRange.sheetName,\n });\n\n const seenRows = new Set<number>();\n for (let c = 0; c < startCol; c++) {\n const colGroup = indexes.colGroups.get(c);\n if (colGroup) {\n // Use binary search to find cells in this column within the row range\n const startIdx = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n colGroup,\n startRow\n );\n if (startIdx !== -1) {\n for (let i = startIdx; i < colGroup.length; i++) {\n const entry = colGroup[i];\n if (!entry || entry.number > endRow) break;\n if (!seenRows.has(entry.number)) {\n seenRows.add(entry.number);\n positionsToCheck.push({ row: entry.number, col: startCol });\n }\n }\n }\n }\n }\n }\n } else {\n // Rectangular range: check corners only for performance\n positionsToCheck.push({ row: startRow, col: startCol }); // Top-left\n positionsToCheck.push({ row: startRow, col: endCol }); // Top-right\n positionsToCheck.push({ row: endRow, col: startCol }); // Bottom-left\n positionsToCheck.push({ row: endRow, col: endCol }); // Bottom-right\n }\n\n // For each position, find candidates\n for (const pos of positionsToCheck) {\n const candidates = findCandidatesForCell.call(\n manager,\n {\n rowIndex: pos.row,\n colIndex: pos.col,\n sheetName: emptyRange.sheetName,\n workbookName: emptyRange.workbookName,\n },\n sheet,\n lookupOrder\n );\n\n for (const cand of candidates) {\n candidateMap.set(getCellReference(cand), cand);\n }\n }\n\n // Return unique candidates, sorted\n return sortCandidates(Array.from(candidateMap.values()), lookupOrder);\n}\n\n/**\n * Find frontier candidates for a single empty cell\n * Returns candidates that could spill into this cell:\n * - If there's a direct left or above candidate, return those (max 2)\n * - Otherwise, return ALL step-pattern diagonal candidates\n * Candidates are sorted deterministically based on lookup order\n */\nfunction findCandidatesForCell(\n this: WorkbookManager,\n cellAddr: CellAddress,\n sheet: Sheet,\n lookupOrder: LookupOrder\n): CellAddress[] {\n // Find nearest-left anchor (same row)\n const leftCandidate = findNearestLeftAnchor.call(this, cellAddr, sheet);\n\n // Find nearest-above anchor (same column)\n const aboveCandidate = findNearestAboveAnchor.call(this, cellAddr, sheet);\n\n // If we have direct candidates (left and/or above), use those\n if (leftCandidate || aboveCandidate) {\n const candidates: CellAddress[] = [];\n if (leftCandidate) candidates.push(leftCandidate);\n if (aboveCandidate) candidates.push(aboveCandidate);\n\n // Sort candidates deterministically based on lookup order\n return sortCandidates(candidates, lookupOrder);\n }\n\n // No direct candidates - find ALL step-pattern diagonal candidates\n const diagonalCandidates = findAllDiagonalStepCandidates.call(\n this,\n cellAddr,\n sheet\n );\n\n // Sort diagonal candidates\n return sortCandidates(diagonalCandidates, lookupOrder);\n}\n\n/**\n * Sort candidates deterministically based on lookup order\n * - Row-major: sort by col first, then row (left-to-right takes priority)\n * - Col-major: sort by row first, then col (top-to-bottom takes priority)\n */\nfunction sortCandidates(\n candidates: CellAddress[],\n lookupOrder: LookupOrder\n): CellAddress[] {\n return candidates.sort((a, b) => {\n if (lookupOrder === \"row-major\") {\n // Row-major: col first, then row\n if (a.colIndex !== b.colIndex) return a.colIndex - b.colIndex;\n return a.rowIndex - b.rowIndex;\n } else {\n // Col-major: row first, then col\n if (a.rowIndex !== b.rowIndex) return a.rowIndex - b.rowIndex;\n return a.colIndex - b.colIndex;\n }\n });\n}\n\n/**\n * Check if two cell addresses refer to the same cell\n */\nfunction isSameCell(a: CellAddress, b: CellAddress): boolean {\n return (\n a.rowIndex === b.rowIndex &&\n a.colIndex === b.colIndex &&\n a.sheetName === b.sheetName &&\n a.workbookName === b.workbookName\n );\n}\n\n/**\n * Find the nearest anchor (formula cell) to the left of the target cell\n * within the same row\n * Only finds cells with formulas (starting with \"=\")\n * Searches both inside and outside the lookup range boundaries\n * OPTIMIZED: Uses indexes to avoid O(n) scan\n */\nfunction findNearestLeftAnchor(\n this: WorkbookManager,\n targetCell: CellAddress,\n sheet: Sheet\n): CellAddress | null {\n const row = targetCell.rowIndex;\n const targetCol = targetCell.colIndex;\n\n // Use indexes to get cells in this row (O(1) lookup + O(cells_in_row) scan)\n const indexes = this.getSheetIndexes({\n workbookName: targetCell.workbookName,\n sheetName: targetCell.sheetName,\n });\n\n const rowGroup = indexes.rowGroups.get(row);\n if (!rowGroup) {\n return null; // No cells in this row\n }\n\n // Search backwards through the sorted array to find nearest left formula\n // rowGroup is sorted by column index\n for (let i = rowGroup.length - 1; i >= 0; i--) {\n const entry = rowGroup[i];\n if (!entry || entry.number >= targetCol) {\n continue; // Skip cells at or right of target\n }\n\n const content = sheet.content.get(entry.key);\n // Only consider formula cells (starting with \"=\")\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n return {\n rowIndex: row,\n colIndex: entry.number,\n sheetName: targetCell.sheetName,\n workbookName: targetCell.workbookName,\n };\n }\n }\n\n return null;\n}\n\n/**\n * Find the nearest anchor (formula cell) above the target cell\n * within the same column\n * Only finds cells with formulas (starting with \"=\")\n * Searches outside the lookup range boundaries\n * OPTIMIZED: Uses indexes to avoid O(n) scan\n */\nfunction findNearestAboveAnchor(\n this: WorkbookManager,\n targetCell: CellAddress,\n sheet: Sheet\n): CellAddress | null {\n const col = targetCell.colIndex;\n const targetRow = targetCell.rowIndex;\n\n // Use indexes to get cells in this column (O(1) lookup + O(cells_in_col) scan)\n const indexes = this.getSheetIndexes({\n workbookName: targetCell.workbookName,\n sheetName: targetCell.sheetName,\n });\n\n const colGroup = indexes.colGroups.get(col);\n if (!colGroup) {\n return null; // No cells in this column\n }\n\n // Search backwards through the sorted array to find nearest above formula\n // colGroup is sorted by row index\n for (let i = colGroup.length - 1; i >= 0; i--) {\n const entry = colGroup[i];\n if (!entry || entry.number >= targetRow) {\n continue; // Skip cells at or below target\n }\n\n const content = sheet.content.get(entry.key);\n // Only consider formula cells (starting with \"=\")\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n return {\n rowIndex: entry.number,\n colIndex: col,\n sheetName: targetCell.sheetName,\n workbookName: targetCell.workbookName,\n };\n }\n }\n\n return null;\n}\n\n/**\n * Find ALL step-pattern candidates in the top-left diagonal quadrant\n * These are formulas that could spill diagonally to reach the target cell\n * and don't block each other (forming a \"staircase\" pattern)\n * Searches outside the lookup range boundaries (like direct left/above search)\n * OPTIMIZED: Uses indexes to avoid O(n²) scan\n *\n * For example, for target D7, candidates might be A6, B5, C4\n * - A6 at (5,0) could spill right and down to reach D7\n * - B5 at (4,1) could spill right and down to reach D7\n * - C4 at (3,2) could spill right and down to reach D7\n * - None blocks the others because each is further from origin (A1)\n */\nfunction findAllDiagonalStepCandidates(\n this: WorkbookManager,\n targetCell: CellAddress,\n sheet: Sheet\n): CellAddress[] {\n const targetRow = targetCell.rowIndex;\n const targetCol = targetCell.colIndex;\n\n // Use indexes to efficiently find all formula cells in the top-left quadrant\n const allCandidates: CellAddress[] = [];\n\n const indexes = this.getSheetIndexes({\n workbookName: targetCell.workbookName,\n sheetName: targetCell.sheetName,\n });\n\n // Iterate only through columns that exist in the top-left quadrant\n for (let col = 0; col < targetCol; col++) {\n const colGroup = indexes.colGroups.get(col);\n if (!colGroup) {\n continue; // No cells in this column\n }\n\n // For each cell in this column, check if it's above target row and is a formula\n for (const entry of colGroup) {\n if (entry.number >= targetRow) {\n break; // Cells are sorted by row, so we can stop early\n }\n\n const content = sheet.content.get(entry.key);\n // Only consider formula cells (starting with \"=\")\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n allCandidates.push({\n rowIndex: entry.number,\n colIndex: col,\n sheetName: targetCell.sheetName,\n workbookName: targetCell.workbookName,\n });\n }\n }\n }\n\n // Filter out candidates that are blocked by other candidates\n // OPTIMIZED: O(n log n) instead of O(n²) using sweep-line algorithm\n //\n // A candidate at (r1, c1) is blocked by another at (r2, c2) if:\n // - r2 >= r1 AND c2 >= c1 (other is at same position or down-right)\n // - This is a 2D dominance problem: find Pareto frontier (undominated points)\n //\n // Algorithm:\n // 1. Sort candidates by row DESCENDING, then col DESCENDING\n // 2. Sweep through, tracking MAXIMUM column seen so far\n // 3. A candidate is unblocked if its col > max col seen\n // 4. For same-row candidates, only the first (largest col) can be unblocked\n\n // Sort by row descending, then col descending\n allCandidates.sort((a, b) => {\n if (a.rowIndex !== b.rowIndex) return b.rowIndex - a.rowIndex; // descending\n return b.colIndex - a.colIndex; // descending\n });\n\n const unblockedCandidates: CellAddress[] = [];\n let maxColSeen = -1;\n let prevRow = -1;\n\n for (const candidate of allCandidates) {\n // First candidate in each row: check if undominated by candidates in rows above\n // Subsequent candidates in same row: dominated by first in row (larger col)\n if (candidate.rowIndex !== prevRow) {\n // New row - check if this candidate's col > max col from rows above\n if (candidate.colIndex > maxColSeen) {\n unblockedCandidates.push(candidate);\n maxColSeen = candidate.colIndex;\n }\n prevRow = candidate.rowIndex;\n }\n // Else: same row as previous, dominated by previous (which had larger col)\n }\n\n return unblockedCandidates;\n}\n"
|
|
5
|
+
"import { EvaluationError } from \"../../evaluator/evaluation-error.mjs\";\nimport {\n FormulaError,\n type CellAddress,\n type RangeAddress,\n type Sheet\n} from \"../types.mjs\";\nimport { getCellReference } from \"../utils.mjs\";\nimport {\n IndexEntryBinarySearch,\n type SheetIndexes,\n type WorkbookManager,\n} from \"./workbook-manager.mjs\";\n\nexport type LookupOrder = \"row-major\" | \"col-major\";\n\nexport type RangeEvalOrderEntryDict = {\n value: {\n type: \"value\";\n address: CellAddress;\n };\n empty_cell: {\n type: \"empty_cell\";\n address: CellAddress;\n candidates: CellAddress[];\n };\n empty_range: {\n type: \"empty_range\";\n address: RangeAddress;\n candidates: CellAddress[];\n };\n};\n\nexport type RangeEvalOrderEntry =\n | RangeEvalOrderEntryDict[\"value\"]\n | RangeEvalOrderEntryDict[\"empty_cell\"]\n | RangeEvalOrderEntryDict[\"empty_range\"];\n\n/**\n * Build a deterministic, ordered list describing every cell inside a lookup range.\n * This function analyzes the range and classifies each cell/range as either:\n * - A value-like cell (occupied)\n * - An empty cell with 0-2 frontier candidates\n * - An empty range with 0-2 frontier candidates\n *\n * @param this - WorkbookManager instance\n * @param lookupOrder - \"row-major\" or \"col-major\" iteration order\n * @param lookupRange - The range to analyze\n * @returns Ordered array of entries describing the range\n */\nexport function buildRangeEvalOrder(\n this: WorkbookManager,\n lookupOrder: LookupOrder,\n lookupRange: RangeAddress\n): RangeEvalOrderEntry[] {\n const sheet = this.getSheet(lookupRange);\n if (!sheet) {\n throw new EvaluationError(\n FormulaError.REF,\n `Sheet ${lookupRange.sheetName} not found`\n );\n }\n\n const result: RangeEvalOrderEntry[] = [];\n\n // Get range bounds\n const startRow = lookupRange.range.start.row;\n const startCol = lookupRange.range.start.col;\n\n // Build a map of occupied cells within the range\n const occupiedCells = new Map<string, CellAddress>();\n let maxRow = startRow;\n let maxCol = startCol;\n\n for (const cellAddr of this.iterateCellsInRange(lookupRange)) {\n occupiedCells.set(getCellReference(cellAddr), cellAddr);\n maxRow = Math.max(maxRow, cellAddr.rowIndex);\n maxCol = Math.max(maxCol, cellAddr.colIndex);\n }\n\n // For infinite ranges, also consider ALL occupied cells in the sheet\n // (including frontier candidates and their spills) to determine extent\n if (\n lookupRange.range.end.row.type === \"infinity\" ||\n lookupRange.range.end.col.type === \"infinity\"\n ) {\n // Get all cells in the sheet to find the full extent\n const allCells = this.iterateCellsInRange({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row: 0, col: 0 },\n end: {\n row: { type: \"infinity\", sign: \"positive\" },\n col: { type: \"infinity\", sign: \"positive\" },\n },\n },\n });\n\n for (const cellAddr of allCells) {\n // Consider all occupied cells (formulas and values) to determine extent\n maxRow = Math.max(maxRow, cellAddr.rowIndex);\n maxCol = Math.max(maxCol, cellAddr.colIndex);\n }\n }\n\n // Determine the effective end bounds\n // For finite ranges, use the specified end\n // For infinite ranges, use the maximum cell found (including frontier candidates)\n const endRow =\n lookupRange.range.end.row.type === \"number\"\n ? lookupRange.range.end.row.value\n : maxRow;\n const endCol =\n lookupRange.range.end.col.type === \"number\"\n ? lookupRange.range.end.col.value\n : maxCol;\n\n // Optimization: If the lookup range is infinite and has NO occupied cells within it,\n // emit a single infinite range with ALL candidates from the sheet that could affect it\n const isInfinite =\n lookupRange.range.end.row.type === \"infinity\" ||\n lookupRange.range.end.col.type === \"infinity\";\n\n if (isInfinite && occupiedCells.size === 0) {\n // Find all formula candidates that could spill into this range\n // by checking formulas to the left and above of the range\n const leftCandidates = new Map<string, CellAddress>();\n const aboveCandidates = new Map<string, CellAddress>();\n const diagonalCandidates = new Map<string, CellAddress>();\n\n // Iterate all cells in the sheet to find candidates\n // (formulas to the left or above that could spill into the range)\n const allCells = this.iterateCellsInRange({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row: 0, col: 0 },\n end: {\n row: { type: \"infinity\", sign: \"positive\" },\n col: { type: \"infinity\", sign: \"positive\" },\n },\n },\n });\n\n for (const cellAddr of allCells) {\n const cellRef = getCellReference(cellAddr);\n const content = sheet.content.get(cellRef);\n\n // Only consider formula cells\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n // Check if this formula could spill into the lookup range\n // A formula at (r, c) can spill into a range starting at (startRow, startCol) if:\n // 1. It's directly to the left: c < startCol AND r >= startRow (same row or below)\n // 2. It's directly above: r < startRow AND c >= startCol (same column or to the right)\n // 3. It's diagonal: r < startRow AND c < startCol (can spill down-right)\n\n const isLeftCandidate =\n cellAddr.colIndex < startCol && cellAddr.rowIndex >= startRow;\n const isAboveCandidate =\n cellAddr.rowIndex < startRow && cellAddr.colIndex >= startCol;\n const isDiagonalCandidate =\n cellAddr.rowIndex < startRow && cellAddr.colIndex < startCol;\n\n if (isLeftCandidate) {\n leftCandidates.set(cellRef, cellAddr);\n } else if (isAboveCandidate) {\n aboveCandidates.set(cellRef, cellAddr);\n } else if (isDiagonalCandidate) {\n diagonalCandidates.set(cellRef, cellAddr);\n }\n }\n }\n\n // Only include diagonal candidates if there are NO direct left/above candidates\n const candidateMap = new Map<string, CellAddress>();\n if (leftCandidates.size > 0 || aboveCandidates.size > 0) {\n // Have direct candidates - use only those\n for (const [ref, addr] of leftCandidates) {\n candidateMap.set(ref, addr);\n }\n for (const [ref, addr] of aboveCandidates) {\n candidateMap.set(ref, addr);\n }\n } else {\n // No direct candidates - use diagonals\n for (const [ref, addr] of diagonalCandidates) {\n candidateMap.set(ref, addr);\n }\n }\n\n const candidates = sortCandidates(\n Array.from(candidateMap.values()),\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: lookupRange,\n candidates,\n });\n return result;\n }\n\n if (lookupOrder === \"row-major\") {\n // Get indexes for efficient row/column lookups\n const indexes = this.getSheetIndexes({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n });\n\n // Iterate row by row, left to right\n for (let row = startRow; row <= endRow; row++) {\n // Check if this row has any occupied cells using indexes\n const hasOccupiedInRow = indexes.rowGroups.has(row);\n\n // If the row has no occupied cells and the range is infinite in columns,\n // emit a single infinite range for the entire row\n if (!hasOccupiedInRow && lookupRange.range.end.col.type === \"infinity\") {\n const firstCell: CellAddress = {\n rowIndex: row,\n colIndex: startCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCell,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: row,\n col: startCol,\n },\n end: {\n row: { type: \"number\", value: row },\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n continue; // Skip normal processing for this row\n }\n\n processRowMajorRow.call(\n this,\n row,\n startCol,\n endCol,\n lookupRange,\n occupiedCells,\n sheet,\n result,\n indexes\n );\n\n // If the lookup range is infinite in columns, emit a final infinite range for this row ONLY\n if (lookupRange.range.end.col.type === \"infinity\") {\n const nextCol = endCol + 1;\n const firstCellBeyondEnd: CellAddress = {\n rowIndex: row,\n colIndex: nextCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellBeyondEnd,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: row,\n col: nextCol,\n },\n end: {\n // Always constrain to this row only in row-major\n row: { type: \"number\", value: row },\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n }\n }\n\n // If the lookup range is infinite in rows, emit a final infinite range\n if (lookupRange.range.end.row.type === \"infinity\") {\n // Find candidates for cells beyond the last processed row\n const nextRow = endRow + 1;\n const firstCellInNextRow: CellAddress = {\n rowIndex: nextRow,\n colIndex: startCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellInNextRow,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: nextRow,\n col: startCol,\n },\n end: {\n row: lookupRange.range.end.row,\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n }\n } else {\n // Get indexes for efficient row/column lookups\n const indexes = this.getSheetIndexes({\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n });\n\n // col-major: iterate column by column, top to bottom\n for (let col = startCol; col <= endCol; col++) {\n // Check if this column has any occupied cells using indexes\n const hasOccupiedInColumn = indexes.colGroups.has(col);\n\n // If the column has no occupied cells and the range is infinite in rows,\n // emit a single infinite range for the entire column\n if (\n !hasOccupiedInColumn &&\n lookupRange.range.end.row.type === \"infinity\"\n ) {\n const firstCell: CellAddress = {\n rowIndex: startRow,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCell,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: startRow,\n col: col,\n },\n end: {\n row: lookupRange.range.end.row,\n col: { type: \"number\", value: col },\n },\n },\n },\n candidates,\n });\n continue; // Skip normal processing for this column\n }\n\n processColMajorColumn.call(\n this,\n col,\n startRow,\n endRow,\n lookupRange,\n occupiedCells,\n sheet,\n result,\n indexes\n );\n\n // If the lookup range is infinite in rows, emit a final infinite range for this column ONLY\n if (lookupRange.range.end.row.type === \"infinity\") {\n const nextRow = endRow + 1;\n const firstCellBeyondEnd: CellAddress = {\n rowIndex: nextRow,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellBeyondEnd,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: nextRow,\n col: col,\n },\n end: {\n row: lookupRange.range.end.row,\n // Always constrain to this column only in col-major\n col: { type: \"number\", value: col },\n },\n },\n },\n candidates,\n });\n }\n }\n\n // If the lookup range is infinite in columns, emit a final infinite range\n if (lookupRange.range.end.col.type === \"infinity\") {\n // Find candidates for cells beyond the last processed column\n const nextCol = endCol + 1;\n const firstCellInNextCol: CellAddress = {\n rowIndex: startRow,\n colIndex: nextCol,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n\n const candidates = findCandidatesForCell.call(\n this,\n firstCellInNextCol,\n sheet,\n lookupOrder\n );\n\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: startRow,\n col: nextCol,\n },\n end: {\n row: lookupRange.range.end.row,\n col: lookupRange.range.end.col,\n },\n },\n },\n candidates,\n });\n }\n }\n\n return result;\n}\n\n/**\n * Process a single row in row-major order\n */\nfunction processRowMajorRow(\n this: WorkbookManager,\n row: number,\n startCol: number,\n endCol: number,\n lookupRange: RangeAddress,\n occupiedCells: Map<string, CellAddress>,\n sheet: Sheet,\n result: RangeEvalOrderEntry[],\n indexes: SheetIndexes\n): void {\n let col = startCol;\n\n while (col <= endCol) {\n const cellAddr: CellAddress = {\n rowIndex: row,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n const cellRef = getCellReference(cellAddr);\n\n if (occupiedCells.has(cellRef)) {\n // This cell is occupied (has a value or formula)\n result.push({\n type: \"value\",\n address: cellAddr,\n });\n col++;\n } else {\n // This cell is empty - find the contiguous empty range in this row with same candidates\n const emptyRangeStart = col;\n let emptyRangeEnd = endCol;\n\n // Find next occupied cell in this row using binary search (O(log n))\n const rowGroup = indexes.rowGroups.get(row);\n if (rowGroup) {\n // Binary search for the first cell in this row with colIndex > col\n const nextCellIdx = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n rowGroup,\n col + 1\n );\n if (nextCellIdx !== -1) {\n emptyRangeEnd = rowGroup[nextCellIdx]!.number - 1;\n }\n }\n\n // Find ALL unique candidates across the entire empty range\n const allCandidates = findAllCandidatesForRange(\n this,\n {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row, col: emptyRangeStart },\n end: {\n row: { type: \"number\", value: row },\n col: { type: \"number\", value: emptyRangeEnd },\n },\n },\n },\n sheet,\n \"row-major\"\n );\n\n // If the empty range is a single cell, emit as empty_cell\n if (emptyRangeStart === emptyRangeEnd) {\n result.push({\n type: \"empty_cell\",\n address: cellAddr,\n candidates: allCandidates,\n });\n } else {\n // Emit as empty_range\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row,\n col: emptyRangeStart,\n },\n end: {\n row: { type: \"number\", value: row },\n col: { type: \"number\", value: emptyRangeEnd },\n },\n },\n },\n candidates: allCandidates,\n });\n }\n\n col = emptyRangeEnd + 1;\n }\n }\n}\n\n/**\n * Process a single column in col-major order\n */\nfunction processColMajorColumn(\n this: WorkbookManager,\n col: number,\n startRow: number,\n endRow: number,\n lookupRange: RangeAddress,\n occupiedCells: Map<string, CellAddress>,\n sheet: Sheet,\n result: RangeEvalOrderEntry[],\n indexes: SheetIndexes\n): void {\n let row = startRow;\n\n while (row <= endRow) {\n const cellAddr: CellAddress = {\n rowIndex: row,\n colIndex: col,\n sheetName: lookupRange.sheetName,\n workbookName: lookupRange.workbookName,\n };\n const cellRef = getCellReference(cellAddr);\n\n if (occupiedCells.has(cellRef)) {\n // This cell is occupied (has a value or formula)\n result.push({\n type: \"value\",\n address: cellAddr,\n });\n row++;\n } else {\n // This cell is empty - find the contiguous empty range in this column with same candidates\n const emptyRangeStart = row;\n let emptyRangeEnd = endRow;\n\n // Find next occupied cell in this column using binary search (O(log n))\n const colGroup = indexes.colGroups.get(col);\n if (colGroup) {\n // Binary search for the first cell in this column with rowIndex > row\n const nextCellIdx = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n colGroup,\n row + 1\n );\n if (nextCellIdx !== -1) {\n emptyRangeEnd = colGroup[nextCellIdx]!.number - 1;\n }\n }\n\n // Find ALL unique candidates across the entire empty range\n const allCandidates = findAllCandidatesForRange(\n this,\n {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: { row: emptyRangeStart, col },\n end: {\n row: { type: \"number\", value: emptyRangeEnd },\n col: { type: \"number\", value: col },\n },\n },\n },\n sheet,\n \"col-major\"\n );\n\n // If the empty range is a single cell, emit as empty_cell\n if (emptyRangeStart === emptyRangeEnd) {\n result.push({\n type: \"empty_cell\",\n address: cellAddr,\n candidates: allCandidates,\n });\n } else {\n // Emit as empty_range\n result.push({\n type: \"empty_range\",\n address: {\n workbookName: lookupRange.workbookName,\n sheetName: lookupRange.sheetName,\n range: {\n start: {\n row: emptyRangeStart,\n col,\n },\n end: {\n row: { type: \"number\", value: emptyRangeEnd },\n col: { type: \"number\", value: col },\n },\n },\n },\n candidates: allCandidates,\n });\n }\n\n row = emptyRangeEnd + 1;\n }\n }\n}\n\n/**\n * Find ALL unique candidates that could affect any cell in the given empty range\n * Returns the union of candidates from all cells in the range, sorted deterministically\n */\nfunction findAllCandidatesForRange(\n manager: WorkbookManager,\n emptyRange: RangeAddress,\n sheet: Sheet,\n lookupOrder: LookupOrder\n): CellAddress[] {\n const candidateMap = new Map<string, CellAddress>();\n\n const startRow = emptyRange.range.start.row;\n const startCol = emptyRange.range.start.col;\n const endRow =\n emptyRange.range.end.row.type === \"number\"\n ? emptyRange.range.end.row.value\n : startRow;\n const endCol =\n emptyRange.range.end.col.type === \"number\"\n ? emptyRange.range.end.col.value\n : startCol;\n\n // We need to check positions that could have different candidates:\n // - Different columns might have different \"above\" candidates\n // - Different rows might have different \"left\" candidates\n const positionsToCheck: Array<{ row: number; col: number }> = [];\n\n // For single-row ranges: check each column (different above candidates)\n // For single-column ranges: sample rows efficiently\n // For rectangular ranges: check corners only\n\n if (startRow === endRow) {\n // Single row: check each column (each column might have different above candidates)\n for (let col = startCol; col <= endCol; col++) {\n positionsToCheck.push({ row: startRow, col });\n }\n } else if (startCol === endCol) {\n // Single column: For large ranges, sample rows instead of checking all\n // We check where occupied cells exist, plus endpoints\n const maxSamples = 100; // Performance limit\n const numRows = endRow - startRow + 1;\n\n if (numRows <= maxSamples) {\n // Small range: check every row\n for (let row = startRow; row <= endRow; row++) {\n positionsToCheck.push({ row, col: startCol });\n }\n } else {\n // Large range: sample at occupied cell rows + endpoints\n positionsToCheck.push({ row: startRow, col: startCol }); // First\n positionsToCheck.push({ row: endRow, col: startCol }); // Last\n\n // Add samples at rows where there are occupied cells to the left\n // (these are the rows that might have different left candidates)\n // Use indexes to efficiently find occupied cells instead of nested loop\n const indexes = manager.getSheetIndexes({\n workbookName: emptyRange.workbookName,\n sheetName: emptyRange.sheetName,\n });\n\n const seenRows = new Set<number>();\n for (let c = 0; c < startCol; c++) {\n const colGroup = indexes.colGroups.get(c);\n if (colGroup) {\n // Use binary search to find cells in this column within the row range\n const startIdx = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n colGroup,\n startRow\n );\n if (startIdx !== -1) {\n for (let i = startIdx; i < colGroup.length; i++) {\n const entry = colGroup[i];\n if (!entry || entry.number > endRow) break;\n if (!seenRows.has(entry.number)) {\n seenRows.add(entry.number);\n positionsToCheck.push({ row: entry.number, col: startCol });\n }\n }\n }\n }\n }\n }\n } else {\n // Rectangular range: check corners only for performance\n positionsToCheck.push({ row: startRow, col: startCol }); // Top-left\n positionsToCheck.push({ row: startRow, col: endCol }); // Top-right\n positionsToCheck.push({ row: endRow, col: startCol }); // Bottom-left\n positionsToCheck.push({ row: endRow, col: endCol }); // Bottom-right\n }\n\n // For each position, find candidates\n for (const pos of positionsToCheck) {\n const candidates = findCandidatesForCell.call(\n manager,\n {\n rowIndex: pos.row,\n colIndex: pos.col,\n sheetName: emptyRange.sheetName,\n workbookName: emptyRange.workbookName,\n },\n sheet,\n lookupOrder\n );\n\n for (const cand of candidates) {\n candidateMap.set(getCellReference(cand), cand);\n }\n }\n\n // Return unique candidates, sorted\n return sortCandidates(Array.from(candidateMap.values()), lookupOrder);\n}\n\n/**\n * Find frontier candidates for a single empty cell\n * Returns candidates that could spill into this cell:\n * - If there's a direct left or above candidate, return those (max 2)\n * - Otherwise, return ALL step-pattern diagonal candidates\n * Candidates are sorted deterministically based on lookup order\n */\nfunction findCandidatesForCell(\n this: WorkbookManager,\n cellAddr: CellAddress,\n sheet: Sheet,\n lookupOrder: LookupOrder\n): CellAddress[] {\n // Find nearest-left anchor (same row)\n const leftCandidate = findNearestLeftAnchor.call(this, cellAddr, sheet);\n\n // Find nearest-above anchor (same column)\n const aboveCandidate = findNearestAboveAnchor.call(this, cellAddr, sheet);\n\n // If we have direct candidates (left and/or above), use those\n if (leftCandidate || aboveCandidate) {\n const candidates: CellAddress[] = [];\n if (leftCandidate) candidates.push(leftCandidate);\n if (aboveCandidate) candidates.push(aboveCandidate);\n\n // Sort candidates deterministically based on lookup order\n return sortCandidates(candidates, lookupOrder);\n }\n\n // No direct candidates - find ALL step-pattern diagonal candidates\n const diagonalCandidates = findAllDiagonalStepCandidates.call(\n this,\n cellAddr,\n sheet\n );\n\n // Sort diagonal candidates\n return sortCandidates(diagonalCandidates, lookupOrder);\n}\n\n/**\n * Sort candidates deterministically based on lookup order\n * - Row-major: sort by col first, then row (left-to-right takes priority)\n * - Col-major: sort by row first, then col (top-to-bottom takes priority)\n */\nfunction sortCandidates(\n candidates: CellAddress[],\n lookupOrder: LookupOrder\n): CellAddress[] {\n return candidates.sort((a, b) => {\n if (lookupOrder === \"row-major\") {\n // Row-major: col first, then row\n if (a.colIndex !== b.colIndex) return a.colIndex - b.colIndex;\n return a.rowIndex - b.rowIndex;\n } else {\n // Col-major: row first, then col\n if (a.rowIndex !== b.rowIndex) return a.rowIndex - b.rowIndex;\n return a.colIndex - b.colIndex;\n }\n });\n}\n\n/**\n * Check if two cell addresses refer to the same cell\n */\nfunction isSameCell(a: CellAddress, b: CellAddress): boolean {\n return (\n a.rowIndex === b.rowIndex &&\n a.colIndex === b.colIndex &&\n a.sheetName === b.sheetName &&\n a.workbookName === b.workbookName\n );\n}\n\n/**\n * Find the nearest anchor (formula cell) to the left of the target cell\n * within the same row\n * Only finds cells with formulas (starting with \"=\")\n * Searches both inside and outside the lookup range boundaries\n * OPTIMIZED: Uses indexes to avoid O(n) scan\n */\nfunction findNearestLeftAnchor(\n this: WorkbookManager,\n targetCell: CellAddress,\n sheet: Sheet\n): CellAddress | null {\n const row = targetCell.rowIndex;\n const targetCol = targetCell.colIndex;\n\n // Use indexes to get cells in this row (O(1) lookup + O(cells_in_row) scan)\n const indexes = this.getSheetIndexes({\n workbookName: targetCell.workbookName,\n sheetName: targetCell.sheetName,\n });\n\n const rowGroup = indexes.rowGroups.get(row);\n if (!rowGroup) {\n return null; // No cells in this row\n }\n\n // Search backwards through the sorted array to find nearest left formula\n // rowGroup is sorted by column index\n for (let i = rowGroup.length - 1; i >= 0; i--) {\n const entry = rowGroup[i];\n if (!entry || entry.number >= targetCol) {\n continue; // Skip cells at or right of target\n }\n\n const content = sheet.content.get(entry.key);\n // Only consider formula cells (starting with \"=\")\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n return {\n rowIndex: row,\n colIndex: entry.number,\n sheetName: targetCell.sheetName,\n workbookName: targetCell.workbookName,\n };\n }\n }\n\n return null;\n}\n\n/**\n * Find the nearest anchor (formula cell) above the target cell\n * within the same column\n * Only finds cells with formulas (starting with \"=\")\n * Searches outside the lookup range boundaries\n * OPTIMIZED: Uses indexes to avoid O(n) scan\n */\nfunction findNearestAboveAnchor(\n this: WorkbookManager,\n targetCell: CellAddress,\n sheet: Sheet\n): CellAddress | null {\n const col = targetCell.colIndex;\n const targetRow = targetCell.rowIndex;\n\n // Use indexes to get cells in this column (O(1) lookup + O(cells_in_col) scan)\n const indexes = this.getSheetIndexes({\n workbookName: targetCell.workbookName,\n sheetName: targetCell.sheetName,\n });\n\n const colGroup = indexes.colGroups.get(col);\n if (!colGroup) {\n return null; // No cells in this column\n }\n\n // Search backwards through the sorted array to find nearest above formula\n // colGroup is sorted by row index\n for (let i = colGroup.length - 1; i >= 0; i--) {\n const entry = colGroup[i];\n if (!entry || entry.number >= targetRow) {\n continue; // Skip cells at or below target\n }\n\n const content = sheet.content.get(entry.key);\n // Only consider formula cells (starting with \"=\")\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n return {\n rowIndex: entry.number,\n colIndex: col,\n sheetName: targetCell.sheetName,\n workbookName: targetCell.workbookName,\n };\n }\n }\n\n return null;\n}\n\n/**\n * Find ALL step-pattern candidates in the top-left diagonal quadrant\n * These are formulas that could spill diagonally to reach the target cell\n * and don't block each other (forming a \"staircase\" pattern)\n * Searches outside the lookup range boundaries (like direct left/above search)\n * OPTIMIZED: Uses indexes to avoid O(n²) scan\n *\n * For example, for target D7, candidates might be A6, B5, C4\n * - A6 at (5,0) could spill right and down to reach D7\n * - B5 at (4,1) could spill right and down to reach D7\n * - C4 at (3,2) could spill right and down to reach D7\n * - None blocks the others because each is further from origin (A1)\n */\nfunction findAllDiagonalStepCandidates(\n this: WorkbookManager,\n targetCell: CellAddress,\n sheet: Sheet\n): CellAddress[] {\n const targetRow = targetCell.rowIndex;\n const targetCol = targetCell.colIndex;\n\n // Use indexes to efficiently find all formula cells in the top-left quadrant\n const allCandidates: CellAddress[] = [];\n\n const indexes = this.getSheetIndexes({\n workbookName: targetCell.workbookName,\n sheetName: targetCell.sheetName,\n });\n\n // Iterate only through columns that exist in the top-left quadrant\n for (let col = 0; col < targetCol; col++) {\n const colGroup = indexes.colGroups.get(col);\n if (!colGroup) {\n continue; // No cells in this column\n }\n\n // For each cell in this column, check if it's above target row and is a formula\n for (const entry of colGroup) {\n if (entry.number >= targetRow) {\n break; // Cells are sorted by row, so we can stop early\n }\n\n const content = sheet.content.get(entry.key);\n // Only consider formula cells (starting with \"=\")\n if (typeof content === \"string\" && content.startsWith(\"=\")) {\n allCandidates.push({\n rowIndex: entry.number,\n colIndex: col,\n sheetName: targetCell.sheetName,\n workbookName: targetCell.workbookName,\n });\n }\n }\n }\n\n // Filter out candidates that are blocked by other candidates\n // OPTIMIZED: O(n log n) instead of O(n²) using sweep-line algorithm\n //\n // A candidate at (r1, c1) is blocked by another at (r2, c2) if:\n // - r2 >= r1 AND c2 >= c1 (other is at same position or down-right)\n // - This is a 2D dominance problem: find Pareto frontier (undominated points)\n //\n // Algorithm:\n // 1. Sort candidates by row DESCENDING, then col DESCENDING\n // 2. Sweep through, tracking MAXIMUM column seen so far\n // 3. A candidate is unblocked if its col > max col seen\n // 4. For same-row candidates, only the first (largest col) can be unblocked\n\n // Sort by row descending, then col descending\n allCandidates.sort((a, b) => {\n if (a.rowIndex !== b.rowIndex) return b.rowIndex - a.rowIndex; // descending\n return b.colIndex - a.colIndex; // descending\n });\n\n const unblockedCandidates: CellAddress[] = [];\n let maxColSeen = -1;\n let prevRow = -1;\n\n for (const candidate of allCandidates) {\n // First candidate in each row: check if undominated by candidates in rows above\n // Subsequent candidates in same row: dominated by first in row (larger col)\n if (candidate.rowIndex !== prevRow) {\n // New row - check if this candidate's col > max col from rows above\n if (candidate.colIndex > maxColSeen) {\n unblockedCandidates.push(candidate);\n maxColSeen = candidate.colIndex;\n }\n prevRow = candidate.rowIndex;\n }\n // Else: same row as previous, dominated by previous (which had larger col)\n }\n\n return unblockedCandidates;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "5006D0CC3F4E0DC564756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -4,8 +4,8 @@ import { buildRangeEvalOrder } from "./range-eval-order-builder.mjs";
|
|
|
4
4
|
import {
|
|
5
5
|
SheetNotFoundError,
|
|
6
6
|
WorkbookNotFoundError
|
|
7
|
-
} from "
|
|
8
|
-
import { normalizeSerializedCellValue } from "
|
|
7
|
+
} from "../../evaluator/evaluation-error.mjs";
|
|
8
|
+
import { normalizeSerializedCellValue } from "../../parser/formatter.mjs";
|
|
9
9
|
|
|
10
10
|
class IndexEntryBinarySearch {
|
|
11
11
|
static findInsertionPoint(entries, target) {
|
|
@@ -444,4 +444,4 @@ export {
|
|
|
444
444
|
IndexEntryBinarySearch
|
|
445
445
|
};
|
|
446
446
|
|
|
447
|
-
//# debugId=
|
|
447
|
+
//# debugId=CBEBB8CDB5AA670364756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/core/managers/workbook-manager.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {\n FormulaError,\n type CellAddress,\n type FiniteSpreadsheetRange,\n type LocalCellAddress,\n type SerializedCellValue,\n type Sheet,\n type SpreadsheetRange,\n type Workbook,\n} from \"../types.mjs\";\nimport { getCellReference, parseCellReference } from \"../utils.mjs\";\n\nimport type { RangeAddress } from \"src/core/types\";\nimport { buildRangeEvalOrder } from \"./range-eval-order-builder.mjs\";\nimport {\n EvaluationError,\n SheetNotFoundError,\n WorkbookNotFoundError,\n} from \"src/evaluator/evaluation-error\";\nimport { normalizeSerializedCellValue } from \"src/parser/formatter\";\n\ninterface IndexEntry {\n number: number;\n key: string;\n}\n\nexport interface SheetIndexes {\n // lookup maps - cells grouped by row/column\n rowGroups: Map<number, IndexEntry[]>; // row number -> cells in that row (sorted by col)\n colGroups: Map<number, IndexEntry[]>; // col number -> cells in that col (sorted by row)\n\n // Sorted flat indexes - for finding cells before a given row/col\n cellsSortedByRow: IndexEntry[];\n cellsSortedByCol: IndexEntry[];\n}\n\n/**\n * Utility class for binary search operations on IndexEntry arrays\n */\nexport class IndexEntryBinarySearch {\n /**\n * Find the insertion point for a number in a sorted IndexEntry array\n * Returns the index where the number should be inserted to maintain sort order\n */\n static findInsertionPoint(entries: IndexEntry[], target: number): number {\n let left = 0;\n let right = entries.length;\n\n while (left < right) {\n const mid = Math.floor((left + right) / 2);\n const midEntry = entries[mid];\n if (midEntry && midEntry.number < target) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n\n return left;\n }\n\n /**\n * Find the first element >= target\n * Returns the index of the first element, or -1 if not found\n */\n static findFirstGreaterOrEqual(\n entries: IndexEntry[],\n target: number\n ): number {\n if (entries.length === 0) return -1;\n\n let left = 0;\n let right = entries.length - 1;\n let result = -1;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n const midEntry = entries[mid];\n if (midEntry && midEntry.number >= target) {\n result = mid;\n right = mid - 1;\n } else {\n left = mid + 1;\n }\n }\n\n return result;\n }\n\n /**\n * Find the rightmost position where we could insert a target value\n * Useful for finding elements that come before a target\n */\n static findRightmostInsertionPoint(\n entries: IndexEntry[],\n target: number\n ): number {\n return IndexEntryBinarySearch.findInsertionPoint(entries, target);\n }\n}\n\nexport class WorkbookManager {\n private workbooks: Map<string, Workbook> = new Map();\n\n // Map from \"workbookName|sheetName\" to indexes\n private sheetIndexes: Map<string, SheetIndexes> = new Map();\n\n /**\n * Generate a key for the sheet indexes map\n */\n private getSheetIndexKey(workbookName: string, sheetName: string): string {\n return `${workbookName}|${sheetName}`;\n }\n\n /**\n * Get or create indexes for a sheet\n */\n public getSheetIndexes(opts: {\n workbookName: string;\n sheetName: string;\n }): SheetIndexes {\n const key = this.getSheetIndexKey(opts.workbookName, opts.sheetName);\n let indexes = this.sheetIndexes.get(key);\n\n if (!indexes) {\n indexes = {\n rowGroups: new Map(),\n colGroups: new Map(),\n cellsSortedByRow: [],\n cellsSortedByCol: [],\n };\n this.sheetIndexes.set(key, indexes);\n }\n\n return indexes;\n }\n\n getSheets(workbookName: string): Map<string, Sheet> {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n return workbook.sheets;\n }\n\n getWorkbooks(): Map<string, Workbook> {\n return this.workbooks;\n }\n\n addWorkbook(workbookName: string): void {\n if (this.workbooks.has(workbookName)) {\n throw new Error(\"Workbook already exists\");\n }\n this.workbooks.set(workbookName, {\n name: workbookName,\n sheets: new Map(),\n });\n }\n\n removeWorkbook(workbookName: string): void {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n\n // Clean up indexes for all sheets in this workbook\n for (const sheetName of workbook.sheets.keys()) {\n const key = this.getSheetIndexKey(workbookName, sheetName);\n this.sheetIndexes.delete(key);\n }\n\n this.workbooks.delete(workbookName);\n }\n\n isContentEmpty(content: SerializedCellValue): boolean {\n return content === \"\" || content === undefined;\n }\n\n renameWorkbook(opts: {\n workbookName: string;\n newWorkbookName: string;\n }): void {\n const workbook = this.workbooks.get(opts.workbookName);\n if (!workbook) {\n throw new Error(\"Workbook not found\");\n }\n\n // Update indexes for all sheets in this workbook\n for (const sheetName of workbook.sheets.keys()) {\n const oldKey = this.getSheetIndexKey(opts.workbookName, sheetName);\n const newKey = this.getSheetIndexKey(opts.newWorkbookName, sheetName);\n const indexes = this.sheetIndexes.get(oldKey);\n if (indexes) {\n this.sheetIndexes.set(newKey, indexes);\n this.sheetIndexes.delete(oldKey);\n }\n }\n\n this.workbooks.set(opts.newWorkbookName, workbook);\n this.workbooks.delete(opts.workbookName);\n workbook.name = opts.newWorkbookName;\n }\n\n resetWorkbooks(workbooks: Map<string, Workbook>): void {\n this.workbooks.clear();\n this.sheetIndexes.clear();\n\n workbooks.forEach((workbook, workbookName) => {\n this.workbooks.set(workbookName, workbook);\n workbook.sheets.forEach((sheet) => {\n // Initialize indexes for this sheet\n const indexes = this.getSheetIndexes({\n workbookName,\n sheetName: sheet.name,\n });\n indexes.rowGroups.clear();\n indexes.colGroups.clear();\n indexes.cellsSortedByRow = [];\n indexes.cellsSortedByCol = [];\n\n sheet.content.forEach((value, key) => {\n this.setCellContent(\n {\n workbookName,\n sheetName: sheet.name,\n colIndex: parseCellReference(key).colIndex,\n rowIndex: parseCellReference(key).rowIndex,\n },\n value,\n {\n sheet,\n buildingFromScratch: true,\n }\n );\n });\n });\n });\n }\n\n getSheet({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Sheet | undefined {\n const workbook = this.workbooks.get(workbookName);\n const sheet = workbook?.sheets.get(sheetName);\n return sheet;\n }\n\n addSheet({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Sheet {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = {\n name: sheetName,\n index: workbook.sheets.size,\n content: new Map(),\n };\n\n if (workbook.sheets.has(sheet.name)) {\n throw new Error(\"Sheet already exists\");\n }\n\n workbook.sheets.set(sheetName, sheet);\n\n // Initialize empty indexes for this sheet\n this.getSheetIndexes({ workbookName, sheetName });\n\n return sheet;\n }\n\n removeSheet({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Sheet {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = workbook.sheets.get(sheetName);\n if (!sheet) {\n throw new Error(\"Sheet not found\");\n }\n\n // Remove the sheet\n workbook.sheets.delete(sheetName);\n\n // Clean up indexes for this sheet\n const key = this.getSheetIndexKey(workbookName, sheetName);\n this.sheetIndexes.delete(key);\n\n return sheet;\n }\n\n renameSheet({\n workbookName,\n sheetName,\n newSheetName,\n }: {\n workbookName: string;\n sheetName: string;\n newSheetName: string;\n }): Sheet {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = workbook.sheets.get(sheetName);\n if (!sheet) {\n throw new SheetNotFoundError(sheetName);\n }\n\n if (workbook.sheets.has(newSheetName)) {\n throw new Error(\"Sheet with new name already exists\");\n }\n\n // Update sheet name\n sheet.name = newSheetName;\n\n // Update sheets map\n workbook.sheets.set(newSheetName, sheet);\n workbook.sheets.delete(sheetName);\n\n // Move indexes to new key\n const oldKey = this.getSheetIndexKey(workbookName, sheetName);\n const newKey = this.getSheetIndexKey(workbookName, newSheetName);\n const indexes = this.sheetIndexes.get(oldKey);\n if (indexes) {\n this.sheetIndexes.set(newKey, indexes);\n this.sheetIndexes.delete(oldKey);\n }\n\n return sheet;\n }\n\n updateAllFormulas(updateCallback: (formula: string) => string): void {\n const update = (map: Map<string, Sheet>) => {\n map.forEach((sheet) => {\n sheet.content.forEach((cell, key) => {\n if (typeof cell === \"string\" && cell.startsWith(\"=\")) {\n const formula = cell.slice(1);\n const updatedFormula = updateCallback(formula);\n\n // Only update if the formula actually changed\n if (updatedFormula !== formula) {\n sheet.content.set(key, `=${updatedFormula}`);\n }\n }\n });\n });\n };\n\n this.workbooks.forEach((workbook) => {\n update(workbook.sheets);\n });\n }\n\n getSheetSerialized({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Map<string, SerializedCellValue> {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = workbook.sheets.get(sheetName);\n if (!sheet) {\n throw new SheetNotFoundError(sheetName);\n }\n\n return sheet.content;\n }\n\n /**\n * Add a cell to the grouped indexes\n */\n private addCellToGroups(\n indexes: SheetIndexes,\n rowIndex: number,\n colIndex: number,\n key: string\n ): void {\n // Add to row group (cells in this row, sorted by column)\n let rowGroup = indexes.rowGroups.get(rowIndex);\n if (!rowGroup) {\n rowGroup = [];\n indexes.rowGroups.set(rowIndex, rowGroup);\n }\n const colEntry: IndexEntry = { number: colIndex, key };\n const colInsertIdx = this.findInsertIndex(rowGroup, colIndex);\n rowGroup.splice(colInsertIdx, 0, colEntry);\n\n // Add to column group (cells in this column, sorted by row)\n let colGroup = indexes.colGroups.get(colIndex);\n if (!colGroup) {\n colGroup = [];\n indexes.colGroups.set(colIndex, colGroup);\n }\n const rowEntry: IndexEntry = { number: rowIndex, key };\n const rowInsertIdx = this.findInsertIndex(colGroup, rowIndex);\n colGroup.splice(rowInsertIdx, 0, rowEntry);\n\n // Add to sorted flat indexes\n this.insertSorted(indexes.cellsSortedByRow, { number: rowIndex, key });\n this.insertSorted(indexes.cellsSortedByCol, { number: colIndex, key });\n }\n\n /**\n * Remove a cell from the grouped indexes\n */\n private removeCellFromGroups(\n indexes: SheetIndexes,\n rowIndex: number,\n colIndex: number,\n key: string\n ): void {\n // Remove from row group\n const rowGroup = indexes.rowGroups.get(rowIndex);\n if (rowGroup) {\n const filteredGroup = rowGroup.filter((e) => e.key !== key);\n if (filteredGroup.length === 0) {\n indexes.rowGroups.delete(rowIndex);\n } else {\n indexes.rowGroups.set(rowIndex, filteredGroup);\n }\n }\n\n // Remove from column group\n const colGroup = indexes.colGroups.get(colIndex);\n if (colGroup) {\n const filteredGroup = colGroup.filter((e) => e.key !== key);\n if (filteredGroup.length === 0) {\n indexes.colGroups.delete(colIndex);\n } else {\n indexes.colGroups.set(colIndex, filteredGroup);\n }\n }\n\n // Remove from sorted flat indexes\n indexes.cellsSortedByRow = indexes.cellsSortedByRow.filter(\n (item) => item.key !== key\n );\n indexes.cellsSortedByCol = indexes.cellsSortedByCol.filter(\n (item) => item.key !== key\n );\n }\n\n /**\n * Find insertion index in sorted array\n */\n private findInsertIndex(entries: IndexEntry[], n: number): number {\n return IndexEntryBinarySearch.findInsertionPoint(entries, n);\n }\n\n /**\n * Inserts an item into a sorted array by number, maintaining sort order.\n * If an item with the same number and key already exists, it won't be added again.\n */\n private insertSorted(array: IndexEntry[], item: IndexEntry): void {\n // Check if item already exists (same number and key)\n const existingIndex = array.findIndex(\n (existing) => existing.number === item.number && existing.key === item.key\n );\n\n if (existingIndex !== -1) {\n // Item already exists, no need to add it again\n return;\n }\n\n // Find the insertion point using binary search for efficiency\n const insertionPoint = IndexEntryBinarySearch.findInsertionPoint(\n array,\n item.number\n );\n\n // Insert at the found position\n array.splice(insertionPoint, 0, item);\n }\n\n setCellContent(\n address: CellAddress,\n content: SerializedCellValue,\n options?: {\n /**\n * for extra performance, if the sheet is already known, it can be passed in\n */\n sheet?: Sheet;\n /**\n * if the sheet is being built from scratch, we can skip some checks\n */\n buildingFromScratch?: boolean;\n }\n ): void {\n const sheet =\n options?.sheet ||\n this.getSheet({\n sheetName: address.sheetName,\n workbookName: address.workbookName,\n });\n\n if (!sheet) {\n throw new SheetNotFoundError(address.sheetName);\n }\n\n const indexes = this.getSheetIndexes({\n workbookName: address.workbookName,\n sheetName: address.sheetName,\n });\n const adr = getCellReference(address);\n\n if (this.isContentEmpty(content)) {\n if (!options?.buildingFromScratch) {\n sheet.content.delete(adr);\n // Remove from all indexes\n this.removeCellFromGroups(\n indexes,\n address.rowIndex,\n address.colIndex,\n adr\n );\n }\n } else {\n sheet.content.set(adr, content);\n // Add to all indexes\n this.addCellToGroups(indexes, address.rowIndex, address.colIndex, adr);\n }\n }\n\n /**\n * Replace all content for a sheet (safely, without breaking references)\n * This method clears the existing Map and repopulates it rather than replacing the Map reference\n */\n setSheetContent(\n opts: { sheetName: string; workbookName: string },\n newContent: Map<string, SerializedCellValue>\n ): void {\n const sheet = this.getSheet(opts);\n if (!sheet) {\n throw new SheetNotFoundError(opts.sheetName);\n }\n\n // Clear existing content without breaking the Map reference\n sheet.content.clear();\n\n // Clean up indexes for this sheet\n const key = this.getSheetIndexKey(opts.workbookName, opts.sheetName);\n this.sheetIndexes.delete(key);\n\n // Repopulate with new content\n newContent.forEach((value, key) => {\n this.setCellContent(\n {\n workbookName: opts.workbookName,\n sheetName: opts.sheetName,\n colIndex: parseCellReference(key).colIndex,\n rowIndex: parseCellReference(key).rowIndex,\n },\n value,\n {\n sheet,\n buildingFromScratch: true,\n }\n );\n });\n }\n\n /**\n * Removes the content in the spreadsheet that is inside the range.\n * OPTIMIZED: Uses indexes to only process cells that actually exist.\n * ENHANCED: Now supports infinite ranges.\n */\n clearSpreadsheetRange(address: RangeAddress) {\n const sheet = this.getSheet(address);\n\n if (!sheet) {\n throw new SheetNotFoundError(address.sheetName);\n }\n\n // Get current sheet content and prepare new content with cleared cells\n const newContent = new Map(sheet.content);\n\n // Use iterateCellsInRange to only process cells that actually exist\n // This handles both finite and infinite ranges efficiently\n for (const cellAddress of this.iterateCellsInRange(address)) {\n const cellRef = getCellReference(cellAddress);\n\n // Remove from content\n newContent.delete(cellRef);\n }\n\n // setSheetContent will rebuild indexes from scratch, so no need to manually update them\n this.setSheetContent(address, newContent);\n }\n\n /**\n * Optimized generator to iterate over cells defined in the content within a range\n * Uses indexes to efficiently find and yield only cells that exist within the range\n */\n *iterateCellsInRange(address: RangeAddress): Generator<CellAddress> {\n // First check if the sheet exists\n const sheet = this.getSheet(address);\n if (!sheet) {\n throw new SheetNotFoundError(address.sheetName);\n }\n\n const indexes = this.getSheetIndexes(address);\n\n const range = address.range;\n\n // Use the sorted index to find only rows that actually contain cells\n // This avoids iterating through empty rows regardless of finite/infinite bounds\n\n if (range.end.row.type === \"number\") {\n // Finite bounds: Use binary search to find the range of cells to check\n const startIndex = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n indexes.cellsSortedByRow,\n range.start.row\n );\n\n if (startIndex === -1) return; // No cells at or after start row\n\n // Process cells from startIndex until we exceed the end row\n for (let i = startIndex; i < indexes.cellsSortedByRow.length; i++) {\n const cellEntry = indexes.cellsSortedByRow[i];\n if (!cellEntry) continue;\n\n const parsed = parseCellReference(cellEntry.key);\n\n // Stop if we've gone beyond the row range\n if (parsed.rowIndex > range.end.row.value) break;\n\n // Check if cell is within column bounds\n if (parsed.colIndex < range.start.col) continue;\n\n if (\n range.end.col.type === \"number\" &&\n parsed.colIndex > range.end.col.value\n ) {\n continue; // Skip this cell but keep checking others in different rows\n }\n\n yield {\n rowIndex: parsed.rowIndex,\n colIndex: parsed.colIndex,\n sheetName: address.sheetName,\n workbookName: address.workbookName,\n };\n }\n } else {\n // Infinite row bounds: Use binary search to find starting point\n const startIndex = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n indexes.cellsSortedByRow,\n range.start.row\n );\n\n if (startIndex === -1) return; // No cells at or after start row\n\n // Process all cells from startIndex to end\n for (let i = startIndex; i < indexes.cellsSortedByRow.length; i++) {\n const cellEntry = indexes.cellsSortedByRow[i];\n if (!cellEntry) continue;\n\n const parsed = parseCellReference(cellEntry.key);\n\n // Check if cell is within column bounds\n if (parsed.colIndex < range.start.col) continue;\n\n if (\n range.end.col.type === \"number\" &&\n parsed.colIndex > range.end.col.value\n ) {\n continue; // Skip this cell but keep checking others in different rows\n }\n\n yield {\n rowIndex: parsed.rowIndex,\n colIndex: parsed.colIndex,\n sheetName: address.sheetName,\n workbookName: address.workbookName,\n };\n }\n }\n }\n\n getCellsInRange(address: RangeAddress): CellAddress[] {\n return Array.from(this.iterateCellsInRange(address));\n }\n\n private getCellContent(cellAddress: CellAddress): SerializedCellValue {\n const sheet = this.getSheet(cellAddress);\n if (!sheet) {\n throw new SheetNotFoundError(cellAddress.sheetName);\n }\n return sheet.content.get(getCellReference(cellAddress));\n }\n\n public getSerializedCellValue(cellAddress: CellAddress): SerializedCellValue {\n const sheet = this.getSheet(cellAddress);\n if (!sheet) {\n throw new SheetNotFoundError(cellAddress.sheetName);\n }\n return normalizeSerializedCellValue(\n sheet.content.get(getCellReference(cellAddress))\n );\n }\n\n public isCellEmpty(cellAddress: CellAddress): boolean {\n const content = this.getCellContent(cellAddress);\n return (\n content === undefined || (typeof content === \"string\" && content === \"\")\n );\n }\n public isFormulaCell(cellAddress: CellAddress): boolean {\n const content = this.getCellContent(cellAddress);\n return typeof content === \"string\" && content.startsWith(\"=\");\n }\n\n /**\n * Build evaluation order for a range\n * Delegates to the buildRangeEvalOrder function\n */\n public buildRangeEvalOrder(\n lookupOrder: \"row-major\" | \"col-major\",\n lookupRange: RangeAddress\n ) {\n // Import and call the function\n return buildRangeEvalOrder.call(this, lookupOrder, lookupRange);\n }\n}\n"
|
|
5
|
+
"import {\n FormulaError,\n type CellAddress,\n type FiniteSpreadsheetRange,\n type LocalCellAddress,\n type SerializedCellValue,\n type Sheet,\n type SpreadsheetRange,\n type Workbook,\n} from \"../types.mjs\";\nimport { getCellReference, parseCellReference } from \"../utils.mjs\";\n\nimport type { RangeAddress } from \"../types.mjs\";\nimport { buildRangeEvalOrder } from \"./range-eval-order-builder.mjs\";\nimport {\n EvaluationError,\n SheetNotFoundError,\n WorkbookNotFoundError,\n} from \"../../evaluator/evaluation-error.mjs\";\nimport { normalizeSerializedCellValue } from \"../../parser/formatter.mjs\";\n\ninterface IndexEntry {\n number: number;\n key: string;\n}\n\nexport interface SheetIndexes {\n // lookup maps - cells grouped by row/column\n rowGroups: Map<number, IndexEntry[]>; // row number -> cells in that row (sorted by col)\n colGroups: Map<number, IndexEntry[]>; // col number -> cells in that col (sorted by row)\n\n // Sorted flat indexes - for finding cells before a given row/col\n cellsSortedByRow: IndexEntry[];\n cellsSortedByCol: IndexEntry[];\n}\n\n/**\n * Utility class for binary search operations on IndexEntry arrays\n */\nexport class IndexEntryBinarySearch {\n /**\n * Find the insertion point for a number in a sorted IndexEntry array\n * Returns the index where the number should be inserted to maintain sort order\n */\n static findInsertionPoint(entries: IndexEntry[], target: number): number {\n let left = 0;\n let right = entries.length;\n\n while (left < right) {\n const mid = Math.floor((left + right) / 2);\n const midEntry = entries[mid];\n if (midEntry && midEntry.number < target) {\n left = mid + 1;\n } else {\n right = mid;\n }\n }\n\n return left;\n }\n\n /**\n * Find the first element >= target\n * Returns the index of the first element, or -1 if not found\n */\n static findFirstGreaterOrEqual(\n entries: IndexEntry[],\n target: number\n ): number {\n if (entries.length === 0) return -1;\n\n let left = 0;\n let right = entries.length - 1;\n let result = -1;\n\n while (left <= right) {\n const mid = Math.floor((left + right) / 2);\n const midEntry = entries[mid];\n if (midEntry && midEntry.number >= target) {\n result = mid;\n right = mid - 1;\n } else {\n left = mid + 1;\n }\n }\n\n return result;\n }\n\n /**\n * Find the rightmost position where we could insert a target value\n * Useful for finding elements that come before a target\n */\n static findRightmostInsertionPoint(\n entries: IndexEntry[],\n target: number\n ): number {\n return IndexEntryBinarySearch.findInsertionPoint(entries, target);\n }\n}\n\nexport class WorkbookManager {\n private workbooks: Map<string, Workbook> = new Map();\n\n // Map from \"workbookName|sheetName\" to indexes\n private sheetIndexes: Map<string, SheetIndexes> = new Map();\n\n /**\n * Generate a key for the sheet indexes map\n */\n private getSheetIndexKey(workbookName: string, sheetName: string): string {\n return `${workbookName}|${sheetName}`;\n }\n\n /**\n * Get or create indexes for a sheet\n */\n public getSheetIndexes(opts: {\n workbookName: string;\n sheetName: string;\n }): SheetIndexes {\n const key = this.getSheetIndexKey(opts.workbookName, opts.sheetName);\n let indexes = this.sheetIndexes.get(key);\n\n if (!indexes) {\n indexes = {\n rowGroups: new Map(),\n colGroups: new Map(),\n cellsSortedByRow: [],\n cellsSortedByCol: [],\n };\n this.sheetIndexes.set(key, indexes);\n }\n\n return indexes;\n }\n\n getSheets(workbookName: string): Map<string, Sheet> {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n return workbook.sheets;\n }\n\n getWorkbooks(): Map<string, Workbook> {\n return this.workbooks;\n }\n\n addWorkbook(workbookName: string): void {\n if (this.workbooks.has(workbookName)) {\n throw new Error(\"Workbook already exists\");\n }\n this.workbooks.set(workbookName, {\n name: workbookName,\n sheets: new Map(),\n });\n }\n\n removeWorkbook(workbookName: string): void {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n\n // Clean up indexes for all sheets in this workbook\n for (const sheetName of workbook.sheets.keys()) {\n const key = this.getSheetIndexKey(workbookName, sheetName);\n this.sheetIndexes.delete(key);\n }\n\n this.workbooks.delete(workbookName);\n }\n\n isContentEmpty(content: SerializedCellValue): boolean {\n return content === \"\" || content === undefined;\n }\n\n renameWorkbook(opts: {\n workbookName: string;\n newWorkbookName: string;\n }): void {\n const workbook = this.workbooks.get(opts.workbookName);\n if (!workbook) {\n throw new Error(\"Workbook not found\");\n }\n\n // Update indexes for all sheets in this workbook\n for (const sheetName of workbook.sheets.keys()) {\n const oldKey = this.getSheetIndexKey(opts.workbookName, sheetName);\n const newKey = this.getSheetIndexKey(opts.newWorkbookName, sheetName);\n const indexes = this.sheetIndexes.get(oldKey);\n if (indexes) {\n this.sheetIndexes.set(newKey, indexes);\n this.sheetIndexes.delete(oldKey);\n }\n }\n\n this.workbooks.set(opts.newWorkbookName, workbook);\n this.workbooks.delete(opts.workbookName);\n workbook.name = opts.newWorkbookName;\n }\n\n resetWorkbooks(workbooks: Map<string, Workbook>): void {\n this.workbooks.clear();\n this.sheetIndexes.clear();\n\n workbooks.forEach((workbook, workbookName) => {\n this.workbooks.set(workbookName, workbook);\n workbook.sheets.forEach((sheet) => {\n // Initialize indexes for this sheet\n const indexes = this.getSheetIndexes({\n workbookName,\n sheetName: sheet.name,\n });\n indexes.rowGroups.clear();\n indexes.colGroups.clear();\n indexes.cellsSortedByRow = [];\n indexes.cellsSortedByCol = [];\n\n sheet.content.forEach((value, key) => {\n this.setCellContent(\n {\n workbookName,\n sheetName: sheet.name,\n colIndex: parseCellReference(key).colIndex,\n rowIndex: parseCellReference(key).rowIndex,\n },\n value,\n {\n sheet,\n buildingFromScratch: true,\n }\n );\n });\n });\n });\n }\n\n getSheet({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Sheet | undefined {\n const workbook = this.workbooks.get(workbookName);\n const sheet = workbook?.sheets.get(sheetName);\n return sheet;\n }\n\n addSheet({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Sheet {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = {\n name: sheetName,\n index: workbook.sheets.size,\n content: new Map(),\n };\n\n if (workbook.sheets.has(sheet.name)) {\n throw new Error(\"Sheet already exists\");\n }\n\n workbook.sheets.set(sheetName, sheet);\n\n // Initialize empty indexes for this sheet\n this.getSheetIndexes({ workbookName, sheetName });\n\n return sheet;\n }\n\n removeSheet({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Sheet {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = workbook.sheets.get(sheetName);\n if (!sheet) {\n throw new Error(\"Sheet not found\");\n }\n\n // Remove the sheet\n workbook.sheets.delete(sheetName);\n\n // Clean up indexes for this sheet\n const key = this.getSheetIndexKey(workbookName, sheetName);\n this.sheetIndexes.delete(key);\n\n return sheet;\n }\n\n renameSheet({\n workbookName,\n sheetName,\n newSheetName,\n }: {\n workbookName: string;\n sheetName: string;\n newSheetName: string;\n }): Sheet {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = workbook.sheets.get(sheetName);\n if (!sheet) {\n throw new SheetNotFoundError(sheetName);\n }\n\n if (workbook.sheets.has(newSheetName)) {\n throw new Error(\"Sheet with new name already exists\");\n }\n\n // Update sheet name\n sheet.name = newSheetName;\n\n // Update sheets map\n workbook.sheets.set(newSheetName, sheet);\n workbook.sheets.delete(sheetName);\n\n // Move indexes to new key\n const oldKey = this.getSheetIndexKey(workbookName, sheetName);\n const newKey = this.getSheetIndexKey(workbookName, newSheetName);\n const indexes = this.sheetIndexes.get(oldKey);\n if (indexes) {\n this.sheetIndexes.set(newKey, indexes);\n this.sheetIndexes.delete(oldKey);\n }\n\n return sheet;\n }\n\n updateAllFormulas(updateCallback: (formula: string) => string): void {\n const update = (map: Map<string, Sheet>) => {\n map.forEach((sheet) => {\n sheet.content.forEach((cell, key) => {\n if (typeof cell === \"string\" && cell.startsWith(\"=\")) {\n const formula = cell.slice(1);\n const updatedFormula = updateCallback(formula);\n\n // Only update if the formula actually changed\n if (updatedFormula !== formula) {\n sheet.content.set(key, `=${updatedFormula}`);\n }\n }\n });\n });\n };\n\n this.workbooks.forEach((workbook) => {\n update(workbook.sheets);\n });\n }\n\n getSheetSerialized({\n workbookName,\n sheetName,\n }: {\n workbookName: string;\n sheetName: string;\n }): Map<string, SerializedCellValue> {\n const workbook = this.workbooks.get(workbookName);\n if (!workbook) {\n throw new WorkbookNotFoundError(workbookName);\n }\n const sheet = workbook.sheets.get(sheetName);\n if (!sheet) {\n throw new SheetNotFoundError(sheetName);\n }\n\n return sheet.content;\n }\n\n /**\n * Add a cell to the grouped indexes\n */\n private addCellToGroups(\n indexes: SheetIndexes,\n rowIndex: number,\n colIndex: number,\n key: string\n ): void {\n // Add to row group (cells in this row, sorted by column)\n let rowGroup = indexes.rowGroups.get(rowIndex);\n if (!rowGroup) {\n rowGroup = [];\n indexes.rowGroups.set(rowIndex, rowGroup);\n }\n const colEntry: IndexEntry = { number: colIndex, key };\n const colInsertIdx = this.findInsertIndex(rowGroup, colIndex);\n rowGroup.splice(colInsertIdx, 0, colEntry);\n\n // Add to column group (cells in this column, sorted by row)\n let colGroup = indexes.colGroups.get(colIndex);\n if (!colGroup) {\n colGroup = [];\n indexes.colGroups.set(colIndex, colGroup);\n }\n const rowEntry: IndexEntry = { number: rowIndex, key };\n const rowInsertIdx = this.findInsertIndex(colGroup, rowIndex);\n colGroup.splice(rowInsertIdx, 0, rowEntry);\n\n // Add to sorted flat indexes\n this.insertSorted(indexes.cellsSortedByRow, { number: rowIndex, key });\n this.insertSorted(indexes.cellsSortedByCol, { number: colIndex, key });\n }\n\n /**\n * Remove a cell from the grouped indexes\n */\n private removeCellFromGroups(\n indexes: SheetIndexes,\n rowIndex: number,\n colIndex: number,\n key: string\n ): void {\n // Remove from row group\n const rowGroup = indexes.rowGroups.get(rowIndex);\n if (rowGroup) {\n const filteredGroup = rowGroup.filter((e) => e.key !== key);\n if (filteredGroup.length === 0) {\n indexes.rowGroups.delete(rowIndex);\n } else {\n indexes.rowGroups.set(rowIndex, filteredGroup);\n }\n }\n\n // Remove from column group\n const colGroup = indexes.colGroups.get(colIndex);\n if (colGroup) {\n const filteredGroup = colGroup.filter((e) => e.key !== key);\n if (filteredGroup.length === 0) {\n indexes.colGroups.delete(colIndex);\n } else {\n indexes.colGroups.set(colIndex, filteredGroup);\n }\n }\n\n // Remove from sorted flat indexes\n indexes.cellsSortedByRow = indexes.cellsSortedByRow.filter(\n (item) => item.key !== key\n );\n indexes.cellsSortedByCol = indexes.cellsSortedByCol.filter(\n (item) => item.key !== key\n );\n }\n\n /**\n * Find insertion index in sorted array\n */\n private findInsertIndex(entries: IndexEntry[], n: number): number {\n return IndexEntryBinarySearch.findInsertionPoint(entries, n);\n }\n\n /**\n * Inserts an item into a sorted array by number, maintaining sort order.\n * If an item with the same number and key already exists, it won't be added again.\n */\n private insertSorted(array: IndexEntry[], item: IndexEntry): void {\n // Check if item already exists (same number and key)\n const existingIndex = array.findIndex(\n (existing) => existing.number === item.number && existing.key === item.key\n );\n\n if (existingIndex !== -1) {\n // Item already exists, no need to add it again\n return;\n }\n\n // Find the insertion point using binary search for efficiency\n const insertionPoint = IndexEntryBinarySearch.findInsertionPoint(\n array,\n item.number\n );\n\n // Insert at the found position\n array.splice(insertionPoint, 0, item);\n }\n\n setCellContent(\n address: CellAddress,\n content: SerializedCellValue,\n options?: {\n /**\n * for extra performance, if the sheet is already known, it can be passed in\n */\n sheet?: Sheet;\n /**\n * if the sheet is being built from scratch, we can skip some checks\n */\n buildingFromScratch?: boolean;\n }\n ): void {\n const sheet =\n options?.sheet ||\n this.getSheet({\n sheetName: address.sheetName,\n workbookName: address.workbookName,\n });\n\n if (!sheet) {\n throw new SheetNotFoundError(address.sheetName);\n }\n\n const indexes = this.getSheetIndexes({\n workbookName: address.workbookName,\n sheetName: address.sheetName,\n });\n const adr = getCellReference(address);\n\n if (this.isContentEmpty(content)) {\n if (!options?.buildingFromScratch) {\n sheet.content.delete(adr);\n // Remove from all indexes\n this.removeCellFromGroups(\n indexes,\n address.rowIndex,\n address.colIndex,\n adr\n );\n }\n } else {\n sheet.content.set(adr, content);\n // Add to all indexes\n this.addCellToGroups(indexes, address.rowIndex, address.colIndex, adr);\n }\n }\n\n /**\n * Replace all content for a sheet (safely, without breaking references)\n * This method clears the existing Map and repopulates it rather than replacing the Map reference\n */\n setSheetContent(\n opts: { sheetName: string; workbookName: string },\n newContent: Map<string, SerializedCellValue>\n ): void {\n const sheet = this.getSheet(opts);\n if (!sheet) {\n throw new SheetNotFoundError(opts.sheetName);\n }\n\n // Clear existing content without breaking the Map reference\n sheet.content.clear();\n\n // Clean up indexes for this sheet\n const key = this.getSheetIndexKey(opts.workbookName, opts.sheetName);\n this.sheetIndexes.delete(key);\n\n // Repopulate with new content\n newContent.forEach((value, key) => {\n this.setCellContent(\n {\n workbookName: opts.workbookName,\n sheetName: opts.sheetName,\n colIndex: parseCellReference(key).colIndex,\n rowIndex: parseCellReference(key).rowIndex,\n },\n value,\n {\n sheet,\n buildingFromScratch: true,\n }\n );\n });\n }\n\n /**\n * Removes the content in the spreadsheet that is inside the range.\n * OPTIMIZED: Uses indexes to only process cells that actually exist.\n * ENHANCED: Now supports infinite ranges.\n */\n clearSpreadsheetRange(address: RangeAddress) {\n const sheet = this.getSheet(address);\n\n if (!sheet) {\n throw new SheetNotFoundError(address.sheetName);\n }\n\n // Get current sheet content and prepare new content with cleared cells\n const newContent = new Map(sheet.content);\n\n // Use iterateCellsInRange to only process cells that actually exist\n // This handles both finite and infinite ranges efficiently\n for (const cellAddress of this.iterateCellsInRange(address)) {\n const cellRef = getCellReference(cellAddress);\n\n // Remove from content\n newContent.delete(cellRef);\n }\n\n // setSheetContent will rebuild indexes from scratch, so no need to manually update them\n this.setSheetContent(address, newContent);\n }\n\n /**\n * Optimized generator to iterate over cells defined in the content within a range\n * Uses indexes to efficiently find and yield only cells that exist within the range\n */\n *iterateCellsInRange(address: RangeAddress): Generator<CellAddress> {\n // First check if the sheet exists\n const sheet = this.getSheet(address);\n if (!sheet) {\n throw new SheetNotFoundError(address.sheetName);\n }\n\n const indexes = this.getSheetIndexes(address);\n\n const range = address.range;\n\n // Use the sorted index to find only rows that actually contain cells\n // This avoids iterating through empty rows regardless of finite/infinite bounds\n\n if (range.end.row.type === \"number\") {\n // Finite bounds: Use binary search to find the range of cells to check\n const startIndex = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n indexes.cellsSortedByRow,\n range.start.row\n );\n\n if (startIndex === -1) return; // No cells at or after start row\n\n // Process cells from startIndex until we exceed the end row\n for (let i = startIndex; i < indexes.cellsSortedByRow.length; i++) {\n const cellEntry = indexes.cellsSortedByRow[i];\n if (!cellEntry) continue;\n\n const parsed = parseCellReference(cellEntry.key);\n\n // Stop if we've gone beyond the row range\n if (parsed.rowIndex > range.end.row.value) break;\n\n // Check if cell is within column bounds\n if (parsed.colIndex < range.start.col) continue;\n\n if (\n range.end.col.type === \"number\" &&\n parsed.colIndex > range.end.col.value\n ) {\n continue; // Skip this cell but keep checking others in different rows\n }\n\n yield {\n rowIndex: parsed.rowIndex,\n colIndex: parsed.colIndex,\n sheetName: address.sheetName,\n workbookName: address.workbookName,\n };\n }\n } else {\n // Infinite row bounds: Use binary search to find starting point\n const startIndex = IndexEntryBinarySearch.findFirstGreaterOrEqual(\n indexes.cellsSortedByRow,\n range.start.row\n );\n\n if (startIndex === -1) return; // No cells at or after start row\n\n // Process all cells from startIndex to end\n for (let i = startIndex; i < indexes.cellsSortedByRow.length; i++) {\n const cellEntry = indexes.cellsSortedByRow[i];\n if (!cellEntry) continue;\n\n const parsed = parseCellReference(cellEntry.key);\n\n // Check if cell is within column bounds\n if (parsed.colIndex < range.start.col) continue;\n\n if (\n range.end.col.type === \"number\" &&\n parsed.colIndex > range.end.col.value\n ) {\n continue; // Skip this cell but keep checking others in different rows\n }\n\n yield {\n rowIndex: parsed.rowIndex,\n colIndex: parsed.colIndex,\n sheetName: address.sheetName,\n workbookName: address.workbookName,\n };\n }\n }\n }\n\n getCellsInRange(address: RangeAddress): CellAddress[] {\n return Array.from(this.iterateCellsInRange(address));\n }\n\n private getCellContent(cellAddress: CellAddress): SerializedCellValue {\n const sheet = this.getSheet(cellAddress);\n if (!sheet) {\n throw new SheetNotFoundError(cellAddress.sheetName);\n }\n return sheet.content.get(getCellReference(cellAddress));\n }\n\n public getSerializedCellValue(cellAddress: CellAddress): SerializedCellValue {\n const sheet = this.getSheet(cellAddress);\n if (!sheet) {\n throw new SheetNotFoundError(cellAddress.sheetName);\n }\n return normalizeSerializedCellValue(\n sheet.content.get(getCellReference(cellAddress))\n );\n }\n\n public isCellEmpty(cellAddress: CellAddress): boolean {\n const content = this.getCellContent(cellAddress);\n return (\n content === undefined || (typeof content === \"string\" && content === \"\")\n );\n }\n public isFormulaCell(cellAddress: CellAddress): boolean {\n const content = this.getCellContent(cellAddress);\n return typeof content === \"string\" && content.startsWith(\"=\");\n }\n\n /**\n * Build evaluation order for a range\n * Delegates to the buildRangeEvalOrder function\n */\n public buildRangeEvalOrder(\n lookupOrder: \"row-major\" | \"col-major\",\n lookupRange: RangeAddress\n ) {\n // Import and call the function\n return buildRangeEvalOrder.call(this, lookupOrder, lookupRange);\n }\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "CBEBB8CDB5AA670364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/types.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Core type definitions for FormulaEngine\n * This file contains all fundamental types used throughout the engine\n */\n\nimport type {
|
|
5
|
+
"/**\n * Core type definitions for FormulaEngine\n * This file contains all fundamental types used throughout the engine\n */\n\nimport type { EvaluationContext } from \"../evaluator/evaluation-context.mjs\";\nimport type { FormulaEvaluator } from \"../evaluator/formula-evaluator.mjs\";\nimport type { FunctionNode } from \"../parser/ast.mjs\";\nimport type { DependencyNode } from \"./managers/dependency-node.mjs\";\nimport type { LookupOrder } from \"./managers/range-eval-order-builder.mjs\";\n\n// Cell addressing types\nexport interface CellAddress {\n sheetName: string;\n workbookName: string;\n colIndex: number;\n rowIndex: number;\n}\n\nexport interface RangeAddress {\n sheetName: string;\n workbookName: string;\n range: SpreadsheetRange;\n}\n\nexport interface LocalCellAddress {\n colIndex: number;\n rowIndex: number;\n}\n\nexport type ArethmeticEvaluator = (\n left: CellValue,\n right: CellValue,\n context: EvaluationContext\n) => CellValue | ErrorEvaluationResult;\n\nexport type PositiveInfinity = {\n type: \"infinity\";\n sign: \"positive\";\n};\n\nexport type CellInfinity = {\n type: \"infinity\";\n sign: \"positive\" | \"negative\";\n};\n\nexport type CellNumber = {\n type: \"number\";\n value: number;\n};\n\nexport type SpreadsheetRangeEnd = CellNumber | PositiveInfinity;\n\nexport type SpreadsheetRange = {\n start: {\n col: number;\n row: number;\n };\n end: {\n col: SpreadsheetRangeEnd;\n row: SpreadsheetRangeEnd;\n };\n};\n\nexport type RelativeRange = {\n start: {\n col: number;\n row: number;\n };\n width: SpreadsheetRangeEnd;\n height: SpreadsheetRangeEnd;\n};\n\nexport type FiniteSpreadsheetRange = {\n start: {\n col: number;\n row: number;\n };\n end: {\n col: number;\n row: number;\n };\n};\n\nexport type CellString = {\n type: \"string\";\n value: string;\n};\n\nexport type CellBoolean = {\n type: \"boolean\";\n value: boolean;\n};\n\n// Cell value types\nexport type CellValue = CellNumber | CellString | CellBoolean | CellInfinity;\n/**\n * undefined and \"\" are considered empty values\n * undefineds are converted to \"\" in the engine\n *\n * any empty values are deleted from the sheet content\n */\nexport type SerializedCellValue = string | number | boolean | undefined;\n\n// Named expressions\nexport interface NamedExpression {\n name: string;\n expression: string;\n}\n\nexport interface TableDefinition {\n name: string;\n start: {\n rowIndex: number;\n colIndex: number;\n };\n headers: Map<string, { name: string; index: number }>;\n endRow: SpreadsheetRangeEnd;\n sheetName: string;\n workbookName: string;\n}\n\n// Formula errors\nexport enum FormulaError {\n DIV0 = \"#DIV/0!\",\n NA = \"#N/A\",\n NAME = \"#NAME?\",\n NUM = \"#NUM!\",\n REF = \"#REF!\",\n VALUE = \"#VALUE!\",\n CYCLE = \"#CYCLE!\",\n ERROR = \"#ERROR!\",\n SPILL = \"#SPILL!\",\n}\n\n// Sheet structure\nexport interface Sheet {\n name: string;\n index: number; // 0-based index of the sheet\n content: Map<string, SerializedCellValue>;\n}\n\nexport interface Workbook {\n name: string;\n sheets: Map<string, Sheet>;\n}\n\nexport type ValueEvaluationResult = {\n type: \"value\";\n result: CellValue;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n};\n\nexport type AwaitingEvaluationResult = {\n type: \"awaiting-evaluation\";\n waitingFor: DependencyNode;\n errAddress: DependencyNode;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n};\n\nexport type DoesNotSpillResult = {\n type: \"does-not-spill\";\n};\n\nexport type ErrorEvaluationResult =\n | {\n type: \"error\";\n err: FormulaError;\n errAddress: DependencyNode;\n message: string;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n }\n | AwaitingEvaluationResult;\n\nexport type SingleEvaluationResult =\n | ValueEvaluationResult\n | ErrorEvaluationResult;\n\nexport type SpilledValuesEvaluator = (\n spillOffset: { x: number; y: number },\n context: EvaluationContext\n) => SingleEvaluationResult;\n\nexport type SpilledValuesEvaluationResult = {\n type: \"spilled-values\";\n\n /**\n * When a raw range is evaluated, we will add it to the sourceRange so it can be used e.g. for context dependent functions\n */\n sourceRange?: RangeAddress;\n\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n * sourceCell will only be defined on a spilledValue when a single value is looked up,\n */\n sourceCell?: CellAddress;\n\n spillArea: (origin: CellAddress) => SpreadsheetRange;\n /**\n * for debugging we add a source string to denote where the spilled values were created\n */\n source: string;\n evaluate: SpilledValuesEvaluator;\n /**\n * evaluateAllCells evaluates all non-empty cells in the spilled range.\n * Because a spilled range can be open-ended, we need to have logic for which cells we should evaluate.\n * e.g. when evaluating a range such as D:D only the cells in the current sheet residing in\n * column D should be evaluated and cells producing spilled values that spill onto D:D.\n *\n * In order to evaluate spilled cells in D:D the range evaluateAllCells need to get all cells in the\n * the intersection of the spilled range and D:D, for that reason evaluateAllCells gets an intersection parameter,\n * where the intersection is relative to the origin.\n *\n * #### Producers:\n * In e.g. SEQUENCE and evaluateRange we have logic for which cells in a spilled range we should evaluate,\n *\n * #### Nesting:\n * e.g. evaluation of scalar operators where we want to nest e.g. `5 * right.evaluate()`\n * can be implemented by calling\n * ```ts\n * const vals = child.evaluateAllCells.call(this, options);\n * return vals.map(val => ({ ...val, result: 5 * val.result }));\n * ```\n *\n * #### Consumers:\n * Only functions that need access to all spilled values in a range end up calling evaluateAllCells, e.g.\n * SUM, MIN, MAX, MATCH. Other types of functions like INDEX doesn't need to evaluate all cells in a range,\n * but does a lookup into a spilled range using the evaluate method.\n *\n */\n evaluateAllCells: (\n this: FormulaEvaluator,\n options: {\n /**\n * an intersection relative to the origin\n */\n intersection?: SpreadsheetRange;\n evaluate: SpilledValuesEvaluator;\n context: EvaluationContext;\n /**\n * origin is the cell address that the spilled range is spilled from\n * e.g. in A3=B2:B4 the origin is A3\n */\n origin: CellAddress;\n\n lookupOrder: LookupOrder;\n }\n ) => EvaluateAllCellsResult;\n};\n\nexport type EvaluateAllCellsResult =\n | ErrorEvaluationResult\n | {\n type: \"values\";\n values: CellInRangeResult[];\n };\n\nexport type CellInRangeResult = {\n result: SingleEvaluationResult;\n relativePos: { x: number; y: number };\n};\n\nexport type FunctionEvaluationResult =\n | SingleEvaluationResult\n | SpilledValuesEvaluationResult;\n\nexport type SpilledValue = {\n /**\n * spillOnto is the range that the spilled value is spilled onto\n */\n spillOnto: SpreadsheetRange;\n /**\n * origin is the cell address that the spilled value is spilled from\n */\n origin: CellAddress;\n};\n\n/**\n * Function definition\n */\nexport interface FunctionDefinition {\n name: string;\n evaluate: (\n this: FormulaEvaluator,\n node: FunctionNode,\n context: EvaluationContext\n ) => FunctionEvaluationResult;\n aliases?: string[];\n}\n\n/**\n * Evaluation result\n */\nexport type EvaluationResult = {\n dependencies: Set<string>;\n} & FunctionEvaluationResult;\n\nexport type SCC = {\n id: number;\n nodes: Set<DependencyNode>; // All nodes considering soft + hard edges\n evaluationOrder: DependencyNode[]; // Flat topologically ordered list\n resolved: boolean;\n hardEdgeSCCs: Set<DependencyNode>[]; // SCCs formed by only hard edges (regular dependencies)\n};\n\nexport type SCCDAG = {\n sccList: SCC[];\n sccGraph: Map<number, Set<number>>; // Adjacency list of SCC dependencies\n};\n\nexport type EvaluationOrder = {\n evaluationOrder: Set<DependencyNode>;\n hasCycle: boolean;\n cycleNodes?: Set<DependencyNode>;\n hash: string;\n sccDAG?: SCCDAG;\n};\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";
|
|
7
|
+
"mappings": ";AA2HO,IAAK;AAAA,CAAL,CAAK,kBAAL;AAAA,EACL,wBAAO;AAAA,EACP,sBAAK;AAAA,EACL,wBAAO;AAAA,EACP,uBAAM;AAAA,EACN,uBAAM;AAAA,EACN,yBAAQ;AAAA,EACR,yBAAQ;AAAA,EACR,yBAAQ;AAAA,EACR,yBAAQ;AAAA,GATE;",
|
|
8
8
|
"debugId": "0E7C5AF634572D3964756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/mjs/core/utils.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AwaitingEvaluationError,
|
|
4
4
|
EvaluationError
|
|
5
|
-
} from "
|
|
5
|
+
} from "../evaluator/evaluation-error.mjs";
|
|
6
6
|
import {
|
|
7
7
|
FormulaError
|
|
8
8
|
} from "./types.mjs";
|
|
@@ -406,4 +406,4 @@ export {
|
|
|
406
406
|
captureEvaluationErrors
|
|
407
407
|
};
|
|
408
408
|
|
|
409
|
-
//# debugId=
|
|
409
|
+
//# debugId=76D7EC912EF53F9A64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/core/utils.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {\n AwaitingEvaluationError,\n EvaluationError,\n} from \"src/evaluator/evaluation-error\";\nimport {\n FormulaError,\n type CellAddress,\n type ErrorEvaluationResult,\n type LocalCellAddress,\n type RangeAddress,\n type RelativeRange,\n type SpreadsheetRange,\n type SpreadsheetRangeEnd,\n} from \"./types.mjs\";\nimport type { DependencyNode } from \"./managers/dependency-node.mjs\";\n\n// Type narrowing utilities\nexport function isCellAddress(\n obj: RangeAddress | CellAddress\n): obj is CellAddress {\n return (\n obj &&\n typeof obj === \"object\" &&\n \"rowIndex\" in obj &&\n \"colIndex\" in obj &&\n \"sheetName\" in obj &&\n \"workbookName\" in obj &&\n !(\"range\" in obj)\n );\n}\n\nexport function isRangeAddress(\n obj: RangeAddress | CellAddress\n): obj is RangeAddress {\n return (\n obj &&\n typeof obj === \"object\" &&\n \"range\" in obj &&\n \"sheetName\" in obj &&\n \"workbookName\" in obj\n );\n}\n\n// Column utilities\nexport const columnToIndex = (col: string): number => {\n let result = 0;\n for (let i = 0; i < col.length; i++) {\n result = result * 26 + (col.charCodeAt(i) - 64); // A=1, B=2, etc.\n }\n return result - 1; // Convert to 0-based index\n};\n\nexport const indexToColumn = (index: number): string => {\n let result = \"\";\n let num = index + 1; // Convert to 1-based\n\n while (num > 0) {\n const remainder = (num - 1) % 26;\n result = String.fromCharCode(65 + remainder) + result;\n num = Math.floor((num - 1) / 26);\n }\n\n return result;\n};\n\nexport function getRowNumber(index: number): number {\n return index + 1;\n}\n\nexport function getCellReference({\n rowIndex,\n colIndex,\n}: {\n rowIndex: number;\n colIndex: number;\n}) {\n return `${indexToColumn(colIndex)}${getRowNumber(rowIndex)}`;\n}\n\n// Convert row number to letter(s) for reversed headers (1 -> A, 2 -> B, etc.)\nexport function rowToLetter(row: number): string {\n let result = \"\";\n let num = row - 1; // Convert to 0-based\n\n do {\n result = String.fromCharCode(65 + (num % 26)) + result;\n num = Math.floor(num / 26) - 1;\n } while (num >= 0);\n\n return result;\n}\n\n// Convert letter(s) to row number for reversed headers (A -> 1, B -> 2, etc.)\nexport function letterToRow(letter: string): number {\n let result = 0;\n for (let i = 0; i < letter.length; i++) {\n result = result * 26 + (letter.charCodeAt(i) - 64);\n }\n return result;\n}\n\nexport const parseCellReference = (\n cellReference: string\n): {\n colIndex: number;\n rowIndex: number;\n} => {\n const match = cellReference.match(/^([A-Z]+)(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid cell reference: ${cellReference}`);\n }\n\n const column = match[1];\n if (match[2] === undefined || column === undefined) {\n throw new Error(`Invalid cell reference: ${cellReference}`);\n }\n const row = parseInt(match[2], 10);\n const colIndex = columnToIndex(column);\n const rowIndex = row - 1; // Convert to 0-based index\n\n return {\n colIndex,\n rowIndex,\n };\n};\n\n/**\n * Returns true if the range is a single cell, false otherwise\n */\nexport function isRangeOneCell(range: SpreadsheetRange) {\n if (range.end.col.type === \"infinity\" || range.end.row.type === \"infinity\") {\n return false;\n }\n return (\n range.start.col === range.end.col.value &&\n range.start.row === range.end.row.value\n );\n}\n\nexport function isCellInRange(\n cellAddress: LocalCellAddress,\n range: SpreadsheetRange\n) {\n const endCol = range.end.col;\n const endRow = range.end.row;\n if (endCol.type === \"number\" && endRow.type === \"number\") {\n return (\n cellAddress.colIndex >= range.start.col &&\n cellAddress.colIndex <= endCol.value &&\n cellAddress.rowIndex >= range.start.row &&\n cellAddress.rowIndex <= endRow.value\n );\n } else if (endCol.type === \"infinity\" && endRow.type === \"number\") {\n return (\n cellAddress.colIndex >= range.start.col &&\n cellAddress.rowIndex >= range.start.row &&\n cellAddress.rowIndex <= endRow.value\n );\n } else if (endCol.type === \"number\" && endRow.type === \"infinity\") {\n return (\n cellAddress.rowIndex >= range.start.row &&\n cellAddress.colIndex >= range.start.col &&\n cellAddress.colIndex <= endCol.value\n );\n } else if (endCol.type === \"infinity\" && endRow.type === \"infinity\") {\n return (\n cellAddress.colIndex >= range.start.col &&\n cellAddress.rowIndex >= range.start.row\n );\n }\n return false;\n}\n\nexport function getRangeKey(range: SpreadsheetRange) {\n let rangeKey = \"\";\n rangeKey += getCellReference({\n rowIndex: range.start.row,\n colIndex: range.start.col,\n });\n rangeKey += \":\";\n if (range.end.col.type === \"number\" && range.end.row.type === \"number\") {\n rangeKey += getCellReference({\n rowIndex: range.end.row.value,\n colIndex: range.end.col.value,\n });\n } else if (\n range.end.col.type === \"infinity\" &&\n range.end.row.type === \"number\"\n ) {\n rangeKey += `${getRowNumber(range.end.row.value)}`;\n } else if (\n range.end.col.type === \"number\" &&\n range.end.row.type === \"infinity\"\n ) {\n rangeKey += indexToColumn(range.end.col.value);\n } else if (\n range.end.col.type === \"infinity\" &&\n range.end.row.type === \"infinity\"\n ) {\n rangeKey += \"INFINITY\";\n }\n return rangeKey;\n}\n\nexport function getRelativeRangeKey(range: RelativeRange): string {\n let rangeKey = \"\";\n rangeKey += getCellReference({\n rowIndex: range.start.row,\n colIndex: range.start.col,\n });\n rangeKey += \":\";\n\n if (range.width.type === \"number\") {\n rangeKey += range.width.value;\n } else {\n rangeKey += \"INFINITY\";\n }\n\n rangeKey += \"x\";\n\n if (range.height.type === \"number\") {\n rangeKey += range.height.value;\n } else {\n rangeKey += \"INFINITY\";\n }\n\n return rangeKey;\n}\n\nexport function getRelativeRange(\n range: SpreadsheetRange,\n relativeTo: LocalCellAddress\n): RelativeRange {\n const dx = range.start.col - relativeTo.colIndex;\n const dy = range.start.row - relativeTo.rowIndex;\n const relativeRange: RelativeRange = {\n start: {\n col: dx,\n row: dy,\n },\n width:\n range.end.col.type === \"number\"\n ? {\n type: \"number\",\n value: range.end.col.value - range.start.col + 1,\n }\n : range.end.col,\n height:\n range.end.row.type === \"number\"\n ? {\n type: \"number\",\n value: range.end.row.value - range.start.row + 1,\n }\n : range.end.row,\n };\n\n return relativeRange;\n}\n\nexport function getAbsoluteRange(\n range: RelativeRange,\n relativeTo: LocalCellAddress\n): SpreadsheetRange {\n return {\n start: {\n col: range.start.col + relativeTo.colIndex,\n row: range.start.row + relativeTo.rowIndex,\n },\n end: {\n col:\n range.width.type === \"number\"\n ? {\n type: \"number\",\n value:\n range.start.col + range.width.value - 1 + relativeTo.colIndex,\n }\n : range.width,\n row:\n range.height.type === \"number\"\n ? {\n type: \"number\",\n value:\n range.start.row + range.height.value - 1 + relativeTo.rowIndex,\n }\n : range.height,\n },\n };\n}\n\nexport function keyToCellAddress(key: string): CellAddress {\n const parts = key.split(\":\");\n\n if (parts.length < 2) {\n throw new Error(`Invalid dependency key format: ${key}`);\n }\n\n if (parts.length !== 4) {\n throw new Error(`Invalid cell key format: ${key}`);\n }\n const workbookName = parts[1];\n const sheetName = parts[2];\n const cellRef = parts[3];\n\n if (\n workbookName === undefined ||\n sheetName === undefined ||\n cellRef === undefined\n ) {\n throw new Error(`Invalid cell key format: ${key}`);\n }\n\n const { rowIndex, colIndex } = parseCellReference(cellRef);\n\n if (\n rowIndex === undefined ||\n colIndex === undefined ||\n Number.isNaN(rowIndex) ||\n Number.isNaN(colIndex)\n ) {\n throw new Error(`Invalid cell reference: ${cellRef}`);\n }\n\n return {\n workbookName,\n sheetName,\n colIndex,\n rowIndex,\n };\n}\n\n/**\n * For cache lookups, use dependencyManager.getCellNode(cellAddressToKey(cellAddress)).key\n * because it may be an empty cell node key\n */\nexport function cellAddressToKey(cellAddress: CellAddress): string {\n if (\n cellAddress.rowIndex === undefined ||\n cellAddress.colIndex === undefined\n ) {\n throw new Error(\n `Invalid cell address: rowIndex and colIndex must be defined (got rowIndex=${cellAddress.rowIndex}, colIndex=${cellAddress.colIndex})`\n );\n }\n const cellRef = getCellReference(cellAddress);\n return `cell-value:${cellAddress.workbookName}:${cellAddress.sheetName}:${cellRef}`;\n}\n\nexport function rangeAddressToKey(rangeAddress: RangeAddress): string {\n return `range:${rangeAddress.workbookName}:${rangeAddress.sheetName}:${getRangeKey(rangeAddress.range)}`;\n}\n\n/**\n *\n * @param key - the range key to parse e.g. range:workbook:sheet:A3:A5\n * @returns\n */\nexport function keyToRangeAddress(key: string): RangeAddress {\n const parts = key.split(\":\");\n if (parts.length !== 5) {\n throw new Error(`Invalid range key format: ${key}`);\n }\n const workbookName = parts[1];\n const sheetName = parts[2];\n const rangeKey = parts[3] + \":\" + parts[4];\n return {\n workbookName: workbookName!,\n sheetName: sheetName!,\n range: parseRangeKey(rangeKey!),\n };\n}\n\n/**\n *\n * @param key - the range key to parse e.g. A3:A5 or A4:INFINITY\n * @returns\n */\nfunction parseRangeKey(key: string): SpreadsheetRange {\n const parts = key.split(\":\");\n if (parts.length !== 2) {\n throw new Error(`Invalid range key format: ${key}`);\n }\n\n const start = parseCellReference(parts[0]!);\n const end = parts[1]!;\n\n let endRow: SpreadsheetRangeEnd;\n let endCol: SpreadsheetRangeEnd;\n\n if (end === \"INFINITY\") {\n endRow = {\n type: \"infinity\",\n sign: \"positive\",\n };\n endCol = {\n type: \"infinity\",\n sign: \"positive\",\n };\n } else if (!end?.match(/\\d/)) {\n endRow = {\n type: \"infinity\",\n sign: \"positive\",\n };\n endCol = {\n type: \"number\",\n value: columnToIndex(end!),\n };\n } else if (!end?.match(/[A-Z]/)) {\n endRow = {\n type: \"number\",\n value: parseInt(end!, 10) - 1,\n };\n endCol = {\n type: \"infinity\",\n sign: \"positive\",\n };\n } else {\n const { rowIndex, colIndex } = parseCellReference(end!);\n endRow = {\n type: \"number\",\n value: rowIndex,\n };\n endCol = {\n type: \"number\",\n value: colIndex,\n };\n }\n\n return {\n start: {\n col: start.colIndex,\n row: start.rowIndex,\n },\n end: {\n row: endRow,\n col: endCol,\n },\n };\n}\n\n/**\n * Check if two ranges intersect\n */\nexport function checkRangeIntersection(\n range1: SpreadsheetRange,\n range2: SpreadsheetRange\n): boolean {\n // Check if ranges don't intersect\n if (\n range1.end.col.type === \"number\" &&\n range2.start.col > range1.end.col.value\n )\n return false;\n if (\n range2.end.col.type === \"number\" &&\n range1.start.col > range2.end.col.value\n )\n return false;\n if (\n range1.end.row.type === \"number\" &&\n range2.start.row > range1.end.row.value\n )\n return false;\n if (\n range2.end.row.type === \"number\" &&\n range1.start.row > range2.end.row.value\n )\n return false;\n\n return true;\n}\n\n/**\n * Get the intersection of two ranges\n */\nexport function getRangeIntersection(\n range1: SpreadsheetRange,\n range2: SpreadsheetRange\n): SpreadsheetRange | null {\n if (!checkRangeIntersection(range1, range2)) {\n return null;\n }\n\n const startRow = Math.max(range1.start.row, range2.start.row);\n const startCol = Math.max(range1.start.col, range2.start.col);\n\n let endRow, endCol;\n\n // Handle end row\n if (\n range1.end.row.type === \"infinity\" &&\n range2.end.row.type === \"infinity\"\n ) {\n endRow = { type: \"infinity\" as const, sign: \"positive\" as const };\n } else if (\n range1.end.row.type === \"number\" &&\n range2.end.row.type === \"number\"\n ) {\n endRow = {\n type: \"number\" as const,\n value: Math.min(range1.end.row.value, range2.end.row.value),\n };\n } else {\n // One is finite, one is infinite\n endRow = range1.end.row.type === \"number\" ? range1.end.row : range2.end.row;\n }\n\n // Handle end col\n if (\n range1.end.col.type === \"infinity\" &&\n range2.end.col.type === \"infinity\"\n ) {\n endCol = { type: \"infinity\" as const, sign: \"positive\" as const };\n } else if (\n range1.end.col.type === \"number\" &&\n range2.end.col.type === \"number\"\n ) {\n endCol = {\n type: \"number\" as const,\n value: Math.min(range1.end.col.value, range2.end.col.value),\n };\n } else {\n // One is finite, one is infinite\n endCol = range1.end.col.type === \"number\" ? range1.end.col : range2.end.col;\n }\n\n return {\n start: { row: startRow, col: startCol },\n end: { row: endRow, col: endCol },\n };\n}\n\nexport function captureEvaluationErrors<T>(\n errAddress: DependencyNode,\n fn: () => T\n): T | ErrorEvaluationResult {\n try {\n return fn();\n } catch (error) {\n if (error instanceof EvaluationError) {\n return {\n type: \"error\",\n err: error.type,\n message: error.message,\n errAddress: error.errAddress ?? errAddress,\n };\n }\n if (error instanceof AwaitingEvaluationError) {\n return {\n type: \"awaiting-evaluation\",\n waitingFor: error.waitingFor,\n errAddress: error.errAddress,\n };\n }\n\n // Convert JavaScript errors to formula errors\n const formulaError =\n error instanceof Error\n ? mapJSErrorToFormulaError(error)\n : FormulaError.ERROR;\n\n return {\n type: \"error\",\n err: formulaError,\n message: (error as any)?.stack || \"An error was thrown\",\n errAddress,\n };\n }\n}\n\nfunction isFormulaError(value: string): value is FormulaError {\n if (typeof value !== \"string\") return false;\n\n // Check for all known formula errors\n const errors: FormulaError[] = Object.values(FormulaError);\n\n return errors.includes(value as FormulaError);\n}\n\n/**\n * Maps JavaScript errors to formula errors\n */\nfunction mapJSErrorToFormulaError(error: Error): FormulaError {\n const message = error.message.toLowerCase();\n\n if (isFormulaError(error.message)) {\n return error.message;\n }\n\n if (\n message.includes(\"division by zero\") ||\n message.includes(\"divide by zero\")\n ) {\n return FormulaError.DIV0;\n }\n if (message.includes(\"circular\") || message.includes(\"cycle\")) {\n return FormulaError.CYCLE;\n }\n if (\n message.includes(\"invalid reference\") ||\n (message.includes(\"reference\") && !message.includes(\"circular\"))\n ) {\n return FormulaError.REF;\n }\n if (\n message.includes(\"invalid name\") ||\n message.includes(\"unknown function\")\n ) {\n return FormulaError.NAME;\n }\n if (\n message.includes(\"invalid number\") ||\n message.includes(\"nan\") ||\n message.includes(\"infinity\")\n ) {\n return FormulaError.NUM;\n }\n if (message.includes(\"type\") || message.includes(\"invalid argument\")) {\n return FormulaError.VALUE;\n }\n if (message.includes(\"not available\") || message.includes(\"n/a\")) {\n return FormulaError.NA;\n }\n\n return FormulaError.ERROR;\n}\n"
|
|
5
|
+
"import {\n AwaitingEvaluationError,\n EvaluationError,\n} from \"../evaluator/evaluation-error.mjs\";\nimport {\n FormulaError,\n type CellAddress,\n type ErrorEvaluationResult,\n type LocalCellAddress,\n type RangeAddress,\n type RelativeRange,\n type SpreadsheetRange,\n type SpreadsheetRangeEnd,\n} from \"./types.mjs\";\nimport type { DependencyNode } from \"./managers/dependency-node.mjs\";\n\n// Type narrowing utilities\nexport function isCellAddress(\n obj: RangeAddress | CellAddress\n): obj is CellAddress {\n return (\n obj &&\n typeof obj === \"object\" &&\n \"rowIndex\" in obj &&\n \"colIndex\" in obj &&\n \"sheetName\" in obj &&\n \"workbookName\" in obj &&\n !(\"range\" in obj)\n );\n}\n\nexport function isRangeAddress(\n obj: RangeAddress | CellAddress\n): obj is RangeAddress {\n return (\n obj &&\n typeof obj === \"object\" &&\n \"range\" in obj &&\n \"sheetName\" in obj &&\n \"workbookName\" in obj\n );\n}\n\n// Column utilities\nexport const columnToIndex = (col: string): number => {\n let result = 0;\n for (let i = 0; i < col.length; i++) {\n result = result * 26 + (col.charCodeAt(i) - 64); // A=1, B=2, etc.\n }\n return result - 1; // Convert to 0-based index\n};\n\nexport const indexToColumn = (index: number): string => {\n let result = \"\";\n let num = index + 1; // Convert to 1-based\n\n while (num > 0) {\n const remainder = (num - 1) % 26;\n result = String.fromCharCode(65 + remainder) + result;\n num = Math.floor((num - 1) / 26);\n }\n\n return result;\n};\n\nexport function getRowNumber(index: number): number {\n return index + 1;\n}\n\nexport function getCellReference({\n rowIndex,\n colIndex,\n}: {\n rowIndex: number;\n colIndex: number;\n}) {\n return `${indexToColumn(colIndex)}${getRowNumber(rowIndex)}`;\n}\n\n// Convert row number to letter(s) for reversed headers (1 -> A, 2 -> B, etc.)\nexport function rowToLetter(row: number): string {\n let result = \"\";\n let num = row - 1; // Convert to 0-based\n\n do {\n result = String.fromCharCode(65 + (num % 26)) + result;\n num = Math.floor(num / 26) - 1;\n } while (num >= 0);\n\n return result;\n}\n\n// Convert letter(s) to row number for reversed headers (A -> 1, B -> 2, etc.)\nexport function letterToRow(letter: string): number {\n let result = 0;\n for (let i = 0; i < letter.length; i++) {\n result = result * 26 + (letter.charCodeAt(i) - 64);\n }\n return result;\n}\n\nexport const parseCellReference = (\n cellReference: string\n): {\n colIndex: number;\n rowIndex: number;\n} => {\n const match = cellReference.match(/^([A-Z]+)(\\d+)$/);\n if (!match) {\n throw new Error(`Invalid cell reference: ${cellReference}`);\n }\n\n const column = match[1];\n if (match[2] === undefined || column === undefined) {\n throw new Error(`Invalid cell reference: ${cellReference}`);\n }\n const row = parseInt(match[2], 10);\n const colIndex = columnToIndex(column);\n const rowIndex = row - 1; // Convert to 0-based index\n\n return {\n colIndex,\n rowIndex,\n };\n};\n\n/**\n * Returns true if the range is a single cell, false otherwise\n */\nexport function isRangeOneCell(range: SpreadsheetRange) {\n if (range.end.col.type === \"infinity\" || range.end.row.type === \"infinity\") {\n return false;\n }\n return (\n range.start.col === range.end.col.value &&\n range.start.row === range.end.row.value\n );\n}\n\nexport function isCellInRange(\n cellAddress: LocalCellAddress,\n range: SpreadsheetRange\n) {\n const endCol = range.end.col;\n const endRow = range.end.row;\n if (endCol.type === \"number\" && endRow.type === \"number\") {\n return (\n cellAddress.colIndex >= range.start.col &&\n cellAddress.colIndex <= endCol.value &&\n cellAddress.rowIndex >= range.start.row &&\n cellAddress.rowIndex <= endRow.value\n );\n } else if (endCol.type === \"infinity\" && endRow.type === \"number\") {\n return (\n cellAddress.colIndex >= range.start.col &&\n cellAddress.rowIndex >= range.start.row &&\n cellAddress.rowIndex <= endRow.value\n );\n } else if (endCol.type === \"number\" && endRow.type === \"infinity\") {\n return (\n cellAddress.rowIndex >= range.start.row &&\n cellAddress.colIndex >= range.start.col &&\n cellAddress.colIndex <= endCol.value\n );\n } else if (endCol.type === \"infinity\" && endRow.type === \"infinity\") {\n return (\n cellAddress.colIndex >= range.start.col &&\n cellAddress.rowIndex >= range.start.row\n );\n }\n return false;\n}\n\nexport function getRangeKey(range: SpreadsheetRange) {\n let rangeKey = \"\";\n rangeKey += getCellReference({\n rowIndex: range.start.row,\n colIndex: range.start.col,\n });\n rangeKey += \":\";\n if (range.end.col.type === \"number\" && range.end.row.type === \"number\") {\n rangeKey += getCellReference({\n rowIndex: range.end.row.value,\n colIndex: range.end.col.value,\n });\n } else if (\n range.end.col.type === \"infinity\" &&\n range.end.row.type === \"number\"\n ) {\n rangeKey += `${getRowNumber(range.end.row.value)}`;\n } else if (\n range.end.col.type === \"number\" &&\n range.end.row.type === \"infinity\"\n ) {\n rangeKey += indexToColumn(range.end.col.value);\n } else if (\n range.end.col.type === \"infinity\" &&\n range.end.row.type === \"infinity\"\n ) {\n rangeKey += \"INFINITY\";\n }\n return rangeKey;\n}\n\nexport function getRelativeRangeKey(range: RelativeRange): string {\n let rangeKey = \"\";\n rangeKey += getCellReference({\n rowIndex: range.start.row,\n colIndex: range.start.col,\n });\n rangeKey += \":\";\n\n if (range.width.type === \"number\") {\n rangeKey += range.width.value;\n } else {\n rangeKey += \"INFINITY\";\n }\n\n rangeKey += \"x\";\n\n if (range.height.type === \"number\") {\n rangeKey += range.height.value;\n } else {\n rangeKey += \"INFINITY\";\n }\n\n return rangeKey;\n}\n\nexport function getRelativeRange(\n range: SpreadsheetRange,\n relativeTo: LocalCellAddress\n): RelativeRange {\n const dx = range.start.col - relativeTo.colIndex;\n const dy = range.start.row - relativeTo.rowIndex;\n const relativeRange: RelativeRange = {\n start: {\n col: dx,\n row: dy,\n },\n width:\n range.end.col.type === \"number\"\n ? {\n type: \"number\",\n value: range.end.col.value - range.start.col + 1,\n }\n : range.end.col,\n height:\n range.end.row.type === \"number\"\n ? {\n type: \"number\",\n value: range.end.row.value - range.start.row + 1,\n }\n : range.end.row,\n };\n\n return relativeRange;\n}\n\nexport function getAbsoluteRange(\n range: RelativeRange,\n relativeTo: LocalCellAddress\n): SpreadsheetRange {\n return {\n start: {\n col: range.start.col + relativeTo.colIndex,\n row: range.start.row + relativeTo.rowIndex,\n },\n end: {\n col:\n range.width.type === \"number\"\n ? {\n type: \"number\",\n value:\n range.start.col + range.width.value - 1 + relativeTo.colIndex,\n }\n : range.width,\n row:\n range.height.type === \"number\"\n ? {\n type: \"number\",\n value:\n range.start.row + range.height.value - 1 + relativeTo.rowIndex,\n }\n : range.height,\n },\n };\n}\n\nexport function keyToCellAddress(key: string): CellAddress {\n const parts = key.split(\":\");\n\n if (parts.length < 2) {\n throw new Error(`Invalid dependency key format: ${key}`);\n }\n\n if (parts.length !== 4) {\n throw new Error(`Invalid cell key format: ${key}`);\n }\n const workbookName = parts[1];\n const sheetName = parts[2];\n const cellRef = parts[3];\n\n if (\n workbookName === undefined ||\n sheetName === undefined ||\n cellRef === undefined\n ) {\n throw new Error(`Invalid cell key format: ${key}`);\n }\n\n const { rowIndex, colIndex } = parseCellReference(cellRef);\n\n if (\n rowIndex === undefined ||\n colIndex === undefined ||\n Number.isNaN(rowIndex) ||\n Number.isNaN(colIndex)\n ) {\n throw new Error(`Invalid cell reference: ${cellRef}`);\n }\n\n return {\n workbookName,\n sheetName,\n colIndex,\n rowIndex,\n };\n}\n\n/**\n * For cache lookups, use dependencyManager.getCellNode(cellAddressToKey(cellAddress)).key\n * because it may be an empty cell node key\n */\nexport function cellAddressToKey(cellAddress: CellAddress): string {\n if (\n cellAddress.rowIndex === undefined ||\n cellAddress.colIndex === undefined\n ) {\n throw new Error(\n `Invalid cell address: rowIndex and colIndex must be defined (got rowIndex=${cellAddress.rowIndex}, colIndex=${cellAddress.colIndex})`\n );\n }\n const cellRef = getCellReference(cellAddress);\n return `cell-value:${cellAddress.workbookName}:${cellAddress.sheetName}:${cellRef}`;\n}\n\nexport function rangeAddressToKey(rangeAddress: RangeAddress): string {\n return `range:${rangeAddress.workbookName}:${rangeAddress.sheetName}:${getRangeKey(rangeAddress.range)}`;\n}\n\n/**\n *\n * @param key - the range key to parse e.g. range:workbook:sheet:A3:A5\n * @returns\n */\nexport function keyToRangeAddress(key: string): RangeAddress {\n const parts = key.split(\":\");\n if (parts.length !== 5) {\n throw new Error(`Invalid range key format: ${key}`);\n }\n const workbookName = parts[1];\n const sheetName = parts[2];\n const rangeKey = parts[3] + \":\" + parts[4];\n return {\n workbookName: workbookName!,\n sheetName: sheetName!,\n range: parseRangeKey(rangeKey!),\n };\n}\n\n/**\n *\n * @param key - the range key to parse e.g. A3:A5 or A4:INFINITY\n * @returns\n */\nfunction parseRangeKey(key: string): SpreadsheetRange {\n const parts = key.split(\":\");\n if (parts.length !== 2) {\n throw new Error(`Invalid range key format: ${key}`);\n }\n\n const start = parseCellReference(parts[0]!);\n const end = parts[1]!;\n\n let endRow: SpreadsheetRangeEnd;\n let endCol: SpreadsheetRangeEnd;\n\n if (end === \"INFINITY\") {\n endRow = {\n type: \"infinity\",\n sign: \"positive\",\n };\n endCol = {\n type: \"infinity\",\n sign: \"positive\",\n };\n } else if (!end?.match(/\\d/)) {\n endRow = {\n type: \"infinity\",\n sign: \"positive\",\n };\n endCol = {\n type: \"number\",\n value: columnToIndex(end!),\n };\n } else if (!end?.match(/[A-Z]/)) {\n endRow = {\n type: \"number\",\n value: parseInt(end!, 10) - 1,\n };\n endCol = {\n type: \"infinity\",\n sign: \"positive\",\n };\n } else {\n const { rowIndex, colIndex } = parseCellReference(end!);\n endRow = {\n type: \"number\",\n value: rowIndex,\n };\n endCol = {\n type: \"number\",\n value: colIndex,\n };\n }\n\n return {\n start: {\n col: start.colIndex,\n row: start.rowIndex,\n },\n end: {\n row: endRow,\n col: endCol,\n },\n };\n}\n\n/**\n * Check if two ranges intersect\n */\nexport function checkRangeIntersection(\n range1: SpreadsheetRange,\n range2: SpreadsheetRange\n): boolean {\n // Check if ranges don't intersect\n if (\n range1.end.col.type === \"number\" &&\n range2.start.col > range1.end.col.value\n )\n return false;\n if (\n range2.end.col.type === \"number\" &&\n range1.start.col > range2.end.col.value\n )\n return false;\n if (\n range1.end.row.type === \"number\" &&\n range2.start.row > range1.end.row.value\n )\n return false;\n if (\n range2.end.row.type === \"number\" &&\n range1.start.row > range2.end.row.value\n )\n return false;\n\n return true;\n}\n\n/**\n * Get the intersection of two ranges\n */\nexport function getRangeIntersection(\n range1: SpreadsheetRange,\n range2: SpreadsheetRange\n): SpreadsheetRange | null {\n if (!checkRangeIntersection(range1, range2)) {\n return null;\n }\n\n const startRow = Math.max(range1.start.row, range2.start.row);\n const startCol = Math.max(range1.start.col, range2.start.col);\n\n let endRow, endCol;\n\n // Handle end row\n if (\n range1.end.row.type === \"infinity\" &&\n range2.end.row.type === \"infinity\"\n ) {\n endRow = { type: \"infinity\" as const, sign: \"positive\" as const };\n } else if (\n range1.end.row.type === \"number\" &&\n range2.end.row.type === \"number\"\n ) {\n endRow = {\n type: \"number\" as const,\n value: Math.min(range1.end.row.value, range2.end.row.value),\n };\n } else {\n // One is finite, one is infinite\n endRow = range1.end.row.type === \"number\" ? range1.end.row : range2.end.row;\n }\n\n // Handle end col\n if (\n range1.end.col.type === \"infinity\" &&\n range2.end.col.type === \"infinity\"\n ) {\n endCol = { type: \"infinity\" as const, sign: \"positive\" as const };\n } else if (\n range1.end.col.type === \"number\" &&\n range2.end.col.type === \"number\"\n ) {\n endCol = {\n type: \"number\" as const,\n value: Math.min(range1.end.col.value, range2.end.col.value),\n };\n } else {\n // One is finite, one is infinite\n endCol = range1.end.col.type === \"number\" ? range1.end.col : range2.end.col;\n }\n\n return {\n start: { row: startRow, col: startCol },\n end: { row: endRow, col: endCol },\n };\n}\n\nexport function captureEvaluationErrors<T>(\n errAddress: DependencyNode,\n fn: () => T\n): T | ErrorEvaluationResult {\n try {\n return fn();\n } catch (error) {\n if (error instanceof EvaluationError) {\n return {\n type: \"error\",\n err: error.type,\n message: error.message,\n errAddress: error.errAddress ?? errAddress,\n };\n }\n if (error instanceof AwaitingEvaluationError) {\n return {\n type: \"awaiting-evaluation\",\n waitingFor: error.waitingFor,\n errAddress: error.errAddress,\n };\n }\n\n // Convert JavaScript errors to formula errors\n const formulaError =\n error instanceof Error\n ? mapJSErrorToFormulaError(error)\n : FormulaError.ERROR;\n\n return {\n type: \"error\",\n err: formulaError,\n message: (error as any)?.stack || \"An error was thrown\",\n errAddress,\n };\n }\n}\n\nfunction isFormulaError(value: string): value is FormulaError {\n if (typeof value !== \"string\") return false;\n\n // Check for all known formula errors\n const errors: FormulaError[] = Object.values(FormulaError);\n\n return errors.includes(value as FormulaError);\n}\n\n/**\n * Maps JavaScript errors to formula errors\n */\nfunction mapJSErrorToFormulaError(error: Error): FormulaError {\n const message = error.message.toLowerCase();\n\n if (isFormulaError(error.message)) {\n return error.message;\n }\n\n if (\n message.includes(\"division by zero\") ||\n message.includes(\"divide by zero\")\n ) {\n return FormulaError.DIV0;\n }\n if (message.includes(\"circular\") || message.includes(\"cycle\")) {\n return FormulaError.CYCLE;\n }\n if (\n message.includes(\"invalid reference\") ||\n (message.includes(\"reference\") && !message.includes(\"circular\"))\n ) {\n return FormulaError.REF;\n }\n if (\n message.includes(\"invalid name\") ||\n message.includes(\"unknown function\")\n ) {\n return FormulaError.NAME;\n }\n if (\n message.includes(\"invalid number\") ||\n message.includes(\"nan\") ||\n message.includes(\"infinity\")\n ) {\n return FormulaError.NUM;\n }\n if (message.includes(\"type\") || message.includes(\"invalid argument\")) {\n return FormulaError.VALUE;\n }\n if (message.includes(\"not available\") || message.includes(\"n/a\")) {\n return FormulaError.NA;\n }\n\n return FormulaError.ERROR;\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
8
|
+
"debugId": "76D7EC912EF53F9A64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|