@odoo/o-spreadsheet 17.2.0-alpha.8 → 17.3.0-alpha.0

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.
@@ -3,9 +3,9 @@
3
3
  /**
4
4
  * This file is generated by o-spreadsheet build tools. Do not edit it.
5
5
  * @see https://github.com/odoo/o-spreadsheet
6
- * @version 17.2.0-alpha.8
7
- * @date 2024-03-15T11:01:01.388Z
8
- * @hash 11cf381
6
+ * @version 17.3.0-alpha.0
7
+ * @date 2024-03-20T13:42:32.042Z
8
+ * @hash 073e154
9
9
  */
10
10
 
11
11
  'use strict';
@@ -642,21 +642,6 @@ function isConsecutive(iterable) {
642
642
  }
643
643
  return true;
644
644
  }
645
- class JetSet extends Set {
646
- addMany(iterable) {
647
- for (const element of iterable) {
648
- super.add(element);
649
- }
650
- return this;
651
- }
652
- deleteMany(iterable) {
653
- let wasDeleted = false;
654
- for (const element of iterable) {
655
- wasDeleted ||= super.delete(element);
656
- }
657
- return wasDeleted;
658
- }
659
- }
660
645
  /**
661
646
  * Creates a version of the function that's memoized on the value of its first
662
647
  * argument, if any.
@@ -4102,14 +4087,6 @@ function positions(zone) {
4102
4087
  }
4103
4088
  return positions;
4104
4089
  }
4105
- function forEachPositionsInZone(zone, callback) {
4106
- const { left, right, top, bottom } = zone;
4107
- for (let col = left; col <= right; col++) {
4108
- for (let row = top; row <= bottom; row++) {
4109
- callback(col, row);
4110
- }
4111
- }
4112
- }
4113
4090
  /**
4114
4091
  * This function returns a zone with coordinates modified according to the change
4115
4092
  * applied to the zone. It may be possible to change the zone by resizing or moving
@@ -10055,6 +10032,9 @@ function makeArg(str, description) {
10055
10032
  result.default = true;
10056
10033
  result.defaultValue = defaultValue;
10057
10034
  }
10035
+ if (types.some((t) => t.startsWith("RANGE"))) {
10036
+ result.acceptMatrix = true;
10037
+ }
10058
10038
  return result;
10059
10039
  }
10060
10040
  /**
@@ -18625,11 +18605,27 @@ class FunctionRegistry extends Registry {
18625
18605
  }
18626
18606
  const descr = addMetaInfoFromArg(addDescr);
18627
18607
  validateArguments(descr.args);
18628
- this.mapping[name] = addErrorHandling(addResultHandling(descr.compute), name);
18608
+ this.mapping[name] = addErrorHandling(addResultHandling(addInputHandling(descr)), name);
18629
18609
  super.add(name, descr);
18630
18610
  return this;
18631
18611
  }
18632
18612
  }
18613
+ function addInputHandling(descr) {
18614
+ function computeWithInputHandling(...args) {
18615
+ for (let i = 0; i < args.length; i++) {
18616
+ const argDefinition = descr.args[descr.getArgToFocus(i + 1) - 1];
18617
+ const arg = args[i];
18618
+ if (isMatrix(arg) && !argDefinition.acceptMatrix) {
18619
+ if (arg.length !== 1 || arg[0].length !== 1) {
18620
+ throw new EvaluationError(_t("Function [[FUNCTION_NAME]] expects the parameter '%s' to be a single value or a single cell reference, not a range.", argDefinition.name));
18621
+ }
18622
+ args[i] = arg[0][0];
18623
+ }
18624
+ }
18625
+ return descr.compute.apply(this, args);
18626
+ }
18627
+ return computeWithInputHandling;
18628
+ }
18633
18629
  function addErrorHandling(compute, functionName) {
18634
18630
  return function (...args) {
18635
18631
  try {
@@ -45851,6 +45847,41 @@ class CompilationParametersBuilder {
45851
45847
  }
45852
45848
  }
45853
45849
 
45850
+ class PositionMap {
45851
+ map = {};
45852
+ set({ sheetId, col, row }, value) {
45853
+ const map = this.map;
45854
+ if (!map[sheetId]) {
45855
+ map[sheetId] = {};
45856
+ }
45857
+ if (!map[sheetId][col]) {
45858
+ map[sheetId][col] = {};
45859
+ }
45860
+ map[sheetId][col][row] = value;
45861
+ }
45862
+ get({ sheetId, col, row }) {
45863
+ return this.map[sheetId]?.[col]?.[row];
45864
+ }
45865
+ has({ sheetId, col, row }) {
45866
+ return this.map[sheetId]?.[col]?.[row] !== undefined;
45867
+ }
45868
+ delete({ sheetId, col, row }) {
45869
+ delete this.map[sheetId]?.[col]?.[row];
45870
+ }
45871
+ keys() {
45872
+ const map = this.map;
45873
+ const keys = [];
45874
+ for (const sheetId in map) {
45875
+ for (const col in map[sheetId]) {
45876
+ for (const row in map[sheetId][col]) {
45877
+ keys.push({ sheetId, col: parseInt(col), row: parseInt(row) });
45878
+ }
45879
+ }
45880
+ }
45881
+ return keys;
45882
+ }
45883
+ }
45884
+
45854
45885
  function quickselect(arr, k, left, right, compare) {
45855
45886
  quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
45856
45887
  }
@@ -46577,26 +46608,26 @@ class ZoneRBush extends RBush {
46577
46608
  * It uses an R-Tree data structure to efficiently find dependent cells.
46578
46609
  */
46579
46610
  class FormulaDependencyGraph {
46580
- encoder;
46581
- dependencies = new Map();
46611
+ createEmptyPositionSet;
46612
+ dependencies = new PositionMap();
46582
46613
  rTree;
46583
- constructor(encoder, data = []) {
46584
- this.encoder = encoder;
46614
+ constructor(createEmptyPositionSet, data = []) {
46615
+ this.createEmptyPositionSet = createEmptyPositionSet;
46585
46616
  this.rTree = new SpreadsheetRTree(data);
46586
46617
  }
46587
- removeAllDependencies(formulaPositionId) {
46588
- const ranges = this.dependencies.get(formulaPositionId);
46618
+ removeAllDependencies(formulaPosition) {
46619
+ const ranges = this.dependencies.get(formulaPosition);
46589
46620
  if (!ranges) {
46590
46621
  return;
46591
46622
  }
46592
46623
  for (const range of ranges) {
46593
46624
  this.rTree.remove(range);
46594
46625
  }
46595
- this.dependencies.delete(formulaPositionId);
46626
+ this.dependencies.delete(formulaPosition);
46596
46627
  }
46597
- addDependencies(formulaPositionId, dependencies) {
46628
+ addDependencies(formulaPosition, dependencies) {
46598
46629
  const rTreeItems = dependencies.map(({ sheetId, zone }) => ({
46599
- data: formulaPositionId,
46630
+ data: formulaPosition,
46600
46631
  boundingBox: {
46601
46632
  zone,
46602
46633
  sheetId,
@@ -46605,12 +46636,12 @@ class FormulaDependencyGraph {
46605
46636
  for (const item of rTreeItems) {
46606
46637
  this.rTree.insert(item);
46607
46638
  }
46608
- const existingDependencies = this.dependencies.get(formulaPositionId);
46639
+ const existingDependencies = this.dependencies.get(formulaPosition);
46609
46640
  if (existingDependencies) {
46610
46641
  existingDependencies.push(...rTreeItems);
46611
46642
  }
46612
46643
  else {
46613
- this.dependencies.set(formulaPositionId, rTreeItems);
46644
+ this.dependencies.set(formulaPosition, rTreeItems);
46614
46645
  }
46615
46646
  }
46616
46647
  /**
@@ -46619,23 +46650,194 @@ class FormulaDependencyGraph {
46619
46650
  * This is called a topological ordering (excluding cycles)
46620
46651
  */
46621
46652
  getCellsDependingOn(ranges) {
46622
- const visited = new JetSet();
46653
+ const visited = this.createEmptyPositionSet();
46623
46654
  const queue = Array.from(ranges).reverse();
46624
46655
  while (queue.length > 0) {
46625
46656
  const range = queue.pop();
46626
- visited.addMany(this.encoder.encodeBoundingBox(range));
46627
- const impactedPositionIds = this.rTree.search(range).map((dep) => dep.data);
46628
- for (const positionId of impactedPositionIds) {
46629
- if (!visited.has(positionId)) {
46630
- queue.push(this.encoder.decodeToBoundingBox(positionId));
46657
+ visited.addMany(positions(range.zone).map((position) => ({ sheetId: range.sheetId, ...position })));
46658
+ const impactedPositions = this.rTree.search(range).map((dep) => dep.data);
46659
+ for (const position of impactedPositions) {
46660
+ if (!visited.has(position)) {
46661
+ queue.push({ sheetId: position.sheetId, zone: positionToZone(position) });
46631
46662
  }
46632
46663
  }
46633
46664
  }
46634
- visited.deleteMany(ranges.flatMap((r) => this.encoder.encodeBoundingBox(r)));
46665
+ visited.deleteMany(ranges.flatMap((r) => positions(r.zone).map((position) => ({ sheetId: r.sheetId, ...position }))));
46635
46666
  return visited;
46636
46667
  }
46637
46668
  }
46638
46669
 
46670
+ /**
46671
+ * Implements a fixed-sized grid or 2D matrix of bits.
46672
+ * based on https://github.com/zandaqo/structurae
46673
+ *
46674
+ * The grid is implemented as a 1D array of 32-bit integers, where each bit represents a cell in the grid.
46675
+ * It follows row-major order, with each row stored consecutively in 32-bit blocks.
46676
+ * Pads the number of columns to the next power of 2 to allow quick lookups with bitwise operations.
46677
+ *
46678
+ * Key terminology:
46679
+ * - bucket: Index of an item in the Uint32Array, a 32-bit integer.
46680
+ * - bitPosition: The position of a bit within the bucket 32-bit integer.
46681
+ */
46682
+ class BinaryGrid extends Uint32Array {
46683
+ columnOffset = 0;
46684
+ cols = 0;
46685
+ rows = 0;
46686
+ /**
46687
+ * Creates a binary grid of specified dimensions.
46688
+ */
46689
+ static create(rows, columns) {
46690
+ const columnOffset = log2Ceil(columns);
46691
+ const length = (rows << columnOffset) >> 5;
46692
+ const grid = new this(length + 1);
46693
+ grid.columnOffset = columnOffset;
46694
+ grid.cols = columns;
46695
+ grid.rows = rows;
46696
+ return grid;
46697
+ }
46698
+ /**
46699
+ * Returns the bit at given coordinates.
46700
+ */
46701
+ getValue(position) {
46702
+ const [bucket, bitPosition] = this.getCoordinates(position);
46703
+ return ((this[bucket] >> bitPosition) & 1);
46704
+ }
46705
+ /**
46706
+ * Sets the bit at given coordinates.
46707
+ */
46708
+ setValue(position, value) {
46709
+ const [bucket, bitPosition] = this.getCoordinates(position);
46710
+ const currentValue = (this[bucket] >> bitPosition) & 1;
46711
+ const hasBeenInserted = currentValue === 0 && value === 1;
46712
+ this[bucket] = (this[bucket] & ~(1 << bitPosition)) | (value << bitPosition);
46713
+ return hasBeenInserted;
46714
+ // Let's breakdown of the above line:
46715
+ // with an example with a 4-bit integer (instead of 32-bit).
46716
+ //
46717
+ // Let's say we want to set the bit at position 2 to 1 and the existing
46718
+ // bit sequence this[bucket] is 1001. The final bit sequence should be 1101.
46719
+ //
46720
+ // First, we clear the bit at position 2 by AND-ing this[bucket] with a
46721
+ // mask having all 1s except a 0 at the bit position (~ (1 << bitPosition)).
46722
+ // 1 << bitPosition is 0100 (shifting 0001 to the left by 2)
46723
+ // Inverting the bits with ~ gives the final mask ~(1 << bitPosition): 1011
46724
+ //
46725
+ // Then, we shift the value by the bit position (value << bitPosition: 0100)
46726
+ // and OR the result with the previous step's result:
46727
+ // (1001 & 1011) | 0100 = 1101
46728
+ }
46729
+ isEmpty() {
46730
+ return !this.some((bucket) => bucket !== 0);
46731
+ }
46732
+ fillAllPositions() {
46733
+ const thirtyTwoOnes = -1 >>> 0; // same as 2 ** 32 - 1, a 32-bit number with all bits set to 1
46734
+ this.fill(thirtyTwoOnes);
46735
+ }
46736
+ clear() {
46737
+ this.fill(0);
46738
+ }
46739
+ getCoordinates(position) {
46740
+ const { row, col } = position;
46741
+ const index = (row << this.columnOffset) + col;
46742
+ const bucket = index >> 5;
46743
+ return [bucket, index - (bucket << 5)];
46744
+ }
46745
+ }
46746
+ function log2Ceil(value) {
46747
+ // A faster version of Math.ceil(Math.log2(value)).
46748
+ if (value === 0) {
46749
+ return -Infinity;
46750
+ }
46751
+ else if (value < 0) {
46752
+ return NaN;
46753
+ }
46754
+ // --value handles the case where value is a power of 2
46755
+ return 32 - Math.clz32(--value);
46756
+ }
46757
+
46758
+ class PositionSet {
46759
+ sheets = {};
46760
+ /**
46761
+ * List of positions in the order they were inserted.
46762
+ */
46763
+ insertions = [];
46764
+ maxSize = 0;
46765
+ constructor(sheetSizes) {
46766
+ for (const sheetId in sheetSizes) {
46767
+ const cols = sheetSizes[sheetId].cols;
46768
+ const rows = sheetSizes[sheetId].rows;
46769
+ this.maxSize += cols * rows;
46770
+ this.sheets[sheetId] = BinaryGrid.create(rows, cols);
46771
+ }
46772
+ }
46773
+ add(position) {
46774
+ const hasBeenInserted = this.sheets[position.sheetId].setValue(position, 1);
46775
+ if (hasBeenInserted) {
46776
+ this.insertions.push(position);
46777
+ }
46778
+ }
46779
+ addMany(positions) {
46780
+ for (const position of positions) {
46781
+ this.add(position);
46782
+ }
46783
+ }
46784
+ delete(position) {
46785
+ this.sheets[position.sheetId].setValue(position, 0);
46786
+ }
46787
+ deleteMany(positions) {
46788
+ for (const position of positions) {
46789
+ this.delete(position);
46790
+ }
46791
+ }
46792
+ has(position) {
46793
+ return this.sheets[position.sheetId].getValue(position) === 1;
46794
+ }
46795
+ clear() {
46796
+ const insertions = this.insertions;
46797
+ this.insertions = [];
46798
+ for (const sheetId in this.sheets) {
46799
+ this.sheets[sheetId].clear();
46800
+ }
46801
+ return insertions;
46802
+ }
46803
+ isEmpty() {
46804
+ if (this.insertions.length === 0) {
46805
+ return true;
46806
+ }
46807
+ for (const sheetId in this.sheets) {
46808
+ if (!this.sheets[sheetId].isEmpty()) {
46809
+ return false;
46810
+ }
46811
+ }
46812
+ return true;
46813
+ }
46814
+ fillAllPositions() {
46815
+ this.insertions = new Array(this.maxSize);
46816
+ let index = 0;
46817
+ for (const sheetId in this.sheets) {
46818
+ const grid = this.sheets[sheetId];
46819
+ grid.fillAllPositions();
46820
+ for (let i = 0; i < grid.rows; i++) {
46821
+ for (let j = 0; j < grid.cols; j++) {
46822
+ this.insertions[index++] = { sheetId, row: i, col: j };
46823
+ }
46824
+ }
46825
+ }
46826
+ }
46827
+ /**
46828
+ * Iterate over the positions in the order of insertion.
46829
+ * Note that the same position may be yielded multiple times if the value was added
46830
+ * to the set then removed and then added again.
46831
+ */
46832
+ *[Symbol.iterator]() {
46833
+ for (const position of this.insertions) {
46834
+ if (this.sheets[position.sheetId].getValue(position) === 1) {
46835
+ yield position;
46836
+ }
46837
+ }
46838
+ }
46839
+ }
46840
+
46639
46841
  /**
46640
46842
  * Contains, for each cell, the array
46641
46843
  * formulas that could potentially spread on it
@@ -46649,6 +46851,7 @@ class FormulaDependencyGraph {
46649
46851
  *
46650
46852
  */
46651
46853
  class SpreadingRelation {
46854
+ createEmptyPositionSet;
46652
46855
  /**
46653
46856
  * Internal structure:
46654
46857
  * For something like
@@ -46677,39 +46880,42 @@ class SpreadingRelation {
46677
46880
  * - (B1) --> (B2, B3, B4) meaning B1 spreads on B2, B3 and B4
46678
46881
  *
46679
46882
  */
46680
- resultsToArrayFormulas = new Map();
46681
- arrayFormulasToResults = new Map();
46682
- getFormulaPositionsSpreadingOn(resultPositionId) {
46683
- return this.resultsToArrayFormulas.get(resultPositionId) || EMPTY_ARRAY;
46883
+ resultsToArrayFormulas = new PositionMap();
46884
+ arrayFormulasToResults = new PositionMap();
46885
+ constructor(createEmptyPositionSet) {
46886
+ this.createEmptyPositionSet = createEmptyPositionSet;
46887
+ }
46888
+ getFormulaPositionsSpreadingOn(resultPosition) {
46889
+ return this.resultsToArrayFormulas.get(resultPosition) || EMPTY_ARRAY;
46684
46890
  }
46685
- getArrayResultPositionIds(formulasPositionId) {
46686
- return this.arrayFormulasToResults.get(formulasPositionId) || EMPTY_ARRAY;
46891
+ getArrayResultPositions(formulasPosition) {
46892
+ return this.arrayFormulasToResults.get(formulasPosition) || EMPTY_ARRAY;
46687
46893
  }
46688
46894
  /**
46689
46895
  * Remove a node, also remove it from other nodes adjacency list
46690
46896
  */
46691
- removeNode(positionId) {
46692
- this.resultsToArrayFormulas.delete(positionId);
46693
- this.arrayFormulasToResults.delete(positionId);
46897
+ removeNode(position) {
46898
+ this.resultsToArrayFormulas.delete(position);
46899
+ this.arrayFormulasToResults.delete(position);
46694
46900
  }
46695
46901
  /**
46696
46902
  * Create a spreading relation between two cells
46697
46903
  */
46698
- addRelation({ arrayFormulaPositionId, resultPositionId, }) {
46699
- if (!this.resultsToArrayFormulas.has(resultPositionId)) {
46700
- this.resultsToArrayFormulas.set(resultPositionId, new Set());
46904
+ addRelation({ arrayFormulaPosition, resultPosition, }) {
46905
+ if (!this.resultsToArrayFormulas.has(resultPosition)) {
46906
+ this.resultsToArrayFormulas.set(resultPosition, this.createEmptyPositionSet());
46701
46907
  }
46702
- this.resultsToArrayFormulas.get(resultPositionId)?.add(arrayFormulaPositionId);
46703
- if (!this.arrayFormulasToResults.has(arrayFormulaPositionId)) {
46704
- this.arrayFormulasToResults.set(arrayFormulaPositionId, new Set());
46908
+ this.resultsToArrayFormulas.get(resultPosition)?.add(arrayFormulaPosition);
46909
+ if (!this.arrayFormulasToResults.has(arrayFormulaPosition)) {
46910
+ this.arrayFormulasToResults.set(arrayFormulaPosition, this.createEmptyPositionSet());
46705
46911
  }
46706
- this.arrayFormulasToResults.get(arrayFormulaPositionId)?.add(resultPositionId);
46912
+ this.arrayFormulasToResults.get(arrayFormulaPosition)?.add(resultPosition);
46707
46913
  }
46708
- hasArrayFormulaResult(positionId) {
46709
- return this.resultsToArrayFormulas.has(positionId);
46914
+ hasArrayFormulaResult(position) {
46915
+ return this.resultsToArrayFormulas.has(position);
46710
46916
  }
46711
- isArrayFormula(positionId) {
46712
- return this.arrayFormulasToResults.has(positionId);
46917
+ isArrayFormula(position) {
46918
+ return this.arrayFormulasToResults.has(position);
46713
46919
  }
46714
46920
  }
46715
46921
  const EMPTY_ARRAY = [];
@@ -46721,50 +46927,41 @@ class Evaluator {
46721
46927
  context;
46722
46928
  getters;
46723
46929
  compilationParams;
46724
- encoder = new PositionBitsEncoder();
46725
- evaluatedCells = new Map();
46726
- formulaDependencies = lazy(new FormulaDependencyGraph(this.encoder));
46727
- blockedArrayFormulas = new Set();
46728
- spreadingRelations = new SpreadingRelation();
46930
+ evaluatedCells = new PositionMap();
46931
+ formulaDependencies = lazy(new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this)));
46932
+ blockedArrayFormulas = new PositionSet({});
46933
+ spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
46729
46934
  constructor(context, getters) {
46730
46935
  this.context = context;
46731
46936
  this.getters = getters;
46732
46937
  this.compilationParams = buildCompilationParameters(this.context, this.getters, this.computeAndSave.bind(this));
46733
46938
  }
46734
46939
  getEvaluatedCell(position) {
46735
- return this.evaluatedCells.get(this.encoder.encode(position)) || EMPTY_CELL;
46940
+ return this.evaluatedCells.get(position) || EMPTY_CELL;
46736
46941
  }
46737
46942
  getSpreadPositionsOf(position) {
46738
- const positionId = this.encoder.encode(position);
46739
- if (!this.spreadingRelations.isArrayFormula(positionId)) {
46943
+ if (!this.spreadingRelations.isArrayFormula(position)) {
46740
46944
  return [];
46741
46945
  }
46742
- return Array.from(this.spreadingRelations.getArrayResultPositionIds(positionId)).map((positionId) => this.encoder.decode(positionId));
46743
- }
46744
- getArrayFormulaSpreadingOn(position) {
46745
- const positionId = this.encoder.encode(position);
46746
- const formulaPosition = this.getArrayFormulaSpreadingOnId(positionId);
46747
- return formulaPosition !== undefined ? this.encoder.decode(formulaPosition) : undefined;
46946
+ return Array.from(this.spreadingRelations.getArrayResultPositions(position));
46748
46947
  }
46749
46948
  getEvaluatedPositions() {
46750
- return [...this.evaluatedCells.keys()].map((p) => this.encoder.decode(p));
46949
+ return this.evaluatedCells.keys();
46751
46950
  }
46752
- getArrayFormulaSpreadingOnId(positionId) {
46753
- if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
46951
+ getArrayFormulaSpreadingOn(position) {
46952
+ if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
46754
46953
  return undefined;
46755
46954
  }
46756
- const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
46757
- return Array.from(arrayFormulas).find((positionId) => !this.blockedArrayFormulas.has(positionId));
46955
+ const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
46956
+ return Array.from(arrayFormulas).find((position) => !this.blockedArrayFormulas.has(position));
46758
46957
  }
46759
46958
  updateDependencies(position) {
46760
- const positionId = this.encoder.encode(position);
46761
- this.formulaDependencies().removeAllDependencies(positionId);
46762
- const dependencies = this.getDirectDependencies(positionId);
46763
- this.formulaDependencies().addDependencies(positionId, dependencies);
46959
+ this.formulaDependencies().removeAllDependencies(position);
46960
+ const dependencies = this.getDirectDependencies(position);
46961
+ this.formulaDependencies().addDependencies(position, dependencies);
46764
46962
  }
46765
46963
  addDependencies(position, dependencies) {
46766
- const positionId = this.encoder.encode(position);
46767
- this.formulaDependencies().addDependencies(positionId, dependencies);
46964
+ this.formulaDependencies().addDependencies(position, dependencies);
46768
46965
  }
46769
46966
  updateCompilationParameters() {
46770
46967
  // rebuild the compilation parameters (with a clean cache)
@@ -46772,47 +46969,57 @@ class Evaluator {
46772
46969
  this.compilationParams.evalContext.updateDependencies = this.updateDependencies.bind(this);
46773
46970
  this.compilationParams.evalContext.addDependencies = this.addDependencies.bind(this);
46774
46971
  }
46972
+ createEmptyPositionSet() {
46973
+ const sheetSizes = {};
46974
+ for (const sheetId of this.getters.getSheetIds()) {
46975
+ sheetSizes[sheetId] = {
46976
+ rows: this.getters.getNumberRows(sheetId),
46977
+ cols: this.getters.getNumberCols(sheetId),
46978
+ };
46979
+ }
46980
+ return new PositionSet(sheetSizes);
46981
+ }
46775
46982
  evaluateCells(positions) {
46776
- const cells = positions.map((p) => this.encoder.encode(p));
46777
- const cellsToCompute = new JetSet(cells);
46778
- const arrayFormulasPositionIds = this.getArrayFormulasImpactedByChangesOf(cells);
46779
- cellsToCompute.addMany(this.getCellsDependingOn(cells));
46780
- cellsToCompute.addMany(arrayFormulasPositionIds);
46781
- cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositionIds));
46983
+ const cellsToCompute = this.createEmptyPositionSet();
46984
+ cellsToCompute.addMany(positions);
46985
+ const arrayFormulasPositions = this.getArrayFormulasImpactedByChangesOf(positions);
46986
+ cellsToCompute.addMany(this.getCellsDependingOn(positions));
46987
+ cellsToCompute.addMany(arrayFormulasPositions);
46988
+ cellsToCompute.addMany(this.getCellsDependingOn(arrayFormulasPositions));
46782
46989
  this.evaluate(cellsToCompute);
46783
46990
  }
46784
- getArrayFormulasImpactedByChangesOf(positionIds) {
46785
- const impactedPositionIds = new JetSet();
46786
- for (const positionId of positionIds) {
46787
- const content = this.getCell(positionId)?.content;
46788
- const arrayFormulaPositionId = this.getArrayFormulaSpreadingOnId(positionId);
46789
- if (arrayFormulaPositionId !== undefined) {
46991
+ getArrayFormulasImpactedByChangesOf(positions) {
46992
+ const impactedPositions = this.createEmptyPositionSet();
46993
+ for (const position of positions) {
46994
+ const content = this.getters.getCell(position)?.content;
46995
+ const arrayFormulaPosition = this.getArrayFormulaSpreadingOn(position);
46996
+ if (arrayFormulaPosition !== undefined) {
46790
46997
  // take into account new collisions.
46791
- impactedPositionIds.add(arrayFormulaPositionId);
46998
+ impactedPositions.add(arrayFormulaPosition);
46792
46999
  }
46793
47000
  if (!content) {
46794
47001
  // The previous content could have blocked some array formulas
46795
- impactedPositionIds.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(positionId));
47002
+ impactedPositions.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(position));
46796
47003
  }
46797
47004
  }
46798
- return impactedPositionIds;
47005
+ return impactedPositions;
46799
47006
  }
46800
47007
  buildDependencyGraph() {
46801
- this.blockedArrayFormulas = new Set();
46802
- this.spreadingRelations = new SpreadingRelation();
47008
+ this.blockedArrayFormulas = this.createEmptyPositionSet();
47009
+ this.spreadingRelations = new SpreadingRelation(this.createEmptyPositionSet.bind(this));
46803
47010
  this.formulaDependencies = lazy(() => {
46804
- const dependencies = [...this.getAllCells()].flatMap((positionId) => this.getDirectDependencies(positionId).map((range) => ({
46805
- data: positionId,
47011
+ const dependencies = [...this.getAllCells()].flatMap((position) => this.getDirectDependencies(position).map((range) => ({
47012
+ data: position,
46806
47013
  boundingBox: {
46807
47014
  zone: range.zone,
46808
47015
  sheetId: range.sheetId,
46809
47016
  },
46810
47017
  })));
46811
- return new FormulaDependencyGraph(this.encoder, dependencies);
47018
+ return new FormulaDependencyGraph(this.createEmptyPositionSet.bind(this), dependencies);
46812
47019
  });
46813
47020
  }
46814
47021
  evaluateAllCells() {
46815
- this.evaluatedCells = new Map();
47022
+ this.evaluatedCells = new PositionMap();
46816
47023
  this.evaluate(this.getAllCells());
46817
47024
  }
46818
47025
  evaluateFormula(sheetId, formulaString) {
@@ -46829,57 +47036,50 @@ class Evaluator {
46829
47036
  return result.value;
46830
47037
  }
46831
47038
  getAllCells() {
46832
- const positionIds = new JetSet();
46833
- for (const sheetId of this.getters.getSheetIds()) {
46834
- const cellIds = this.getters.getCells(sheetId);
46835
- for (const cellId in cellIds) {
46836
- positionIds.add(this.encoder.encode(this.getters.getCellPosition(cellId)));
46837
- }
46838
- }
46839
- return positionIds;
47039
+ const positions = this.createEmptyPositionSet();
47040
+ positions.fillAllPositions();
47041
+ return positions;
46840
47042
  }
46841
- getArrayFormulasBlockedByOrSpreadingOn(positionId) {
46842
- if (!this.spreadingRelations.hasArrayFormulaResult(positionId)) {
47043
+ getArrayFormulasBlockedByOrSpreadingOn(position) {
47044
+ if (!this.spreadingRelations.hasArrayFormulaResult(position)) {
46843
47045
  return [];
46844
47046
  }
46845
- const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(positionId);
46846
- const cells = new JetSet(arrayFormulas);
46847
- cells.addMany(this.getCellsDependingOn(arrayFormulas));
46848
- return cells;
47047
+ const arrayFormulas = this.spreadingRelations.getFormulaPositionsSpreadingOn(position);
47048
+ const positions = this.createEmptyPositionSet();
47049
+ positions.addMany(arrayFormulas);
47050
+ positions.addMany(this.getCellsDependingOn(arrayFormulas));
47051
+ return positions;
46849
47052
  }
46850
- nextPositionsToUpdate = new JetSet();
47053
+ nextPositionsToUpdate = new PositionSet({});
46851
47054
  cellsBeingComputed = new Set();
46852
- evaluate(cells) {
47055
+ evaluate(positions) {
46853
47056
  this.cellsBeingComputed = new Set();
46854
- this.nextPositionsToUpdate = cells;
47057
+ this.nextPositionsToUpdate = positions;
46855
47058
  let currentIteration = 0;
46856
- while (this.nextPositionsToUpdate.size && currentIteration++ < MAX_ITERATION) {
47059
+ while (!this.nextPositionsToUpdate.isEmpty() && currentIteration++ < MAX_ITERATION) {
46857
47060
  this.updateCompilationParameters();
46858
- const positionIds = Array.from(this.nextPositionsToUpdate);
46859
- this.nextPositionsToUpdate.clear();
46860
- for (let i = 0; i < positionIds.length; ++i) {
46861
- const cell = positionIds[i];
46862
- this.evaluatedCells.delete(cell);
47061
+ const positions = this.nextPositionsToUpdate.clear();
47062
+ for (let i = 0; i < positions.length; ++i) {
47063
+ this.evaluatedCells.delete(positions[i]);
46863
47064
  }
46864
- for (let i = 0; i < positionIds.length; ++i) {
46865
- const cell = positionIds[i];
46866
- this.setEvaluatedCell(cell, this.computeCell(cell));
47065
+ for (let i = 0; i < positions.length; ++i) {
47066
+ const position = positions[i];
47067
+ const evaluatedCell = this.computeCell(position);
47068
+ if (evaluatedCell !== EMPTY_CELL) {
47069
+ this.evaluatedCells.set(position, evaluatedCell);
47070
+ }
46867
47071
  }
46868
47072
  }
46869
47073
  }
46870
- setEvaluatedCell(positionId, evaluatedCell) {
46871
- this.evaluatedCells.set(positionId, evaluatedCell);
46872
- }
46873
- computeCell(positionId) {
46874
- const evaluation = this.evaluatedCells.get(positionId);
47074
+ computeCell(position) {
47075
+ const evaluation = this.evaluatedCells.get(position);
46875
47076
  if (evaluation) {
46876
47077
  return evaluation; // already computed
46877
47078
  }
46878
- if (!this.blockedArrayFormulas.has(positionId)) {
46879
- this.invalidateSpreading(positionId);
47079
+ if (!this.blockedArrayFormulas.has(position)) {
47080
+ this.invalidateSpreading(position);
46880
47081
  }
46881
- const cellPosition = this.encoder.decode(positionId);
46882
- const cell = this.getters.getCell(cellPosition);
47082
+ const cell = this.getters.getCell(position);
46883
47083
  if (cell === undefined) {
46884
47084
  return EMPTY_CELL;
46885
47085
  }
@@ -46891,7 +47091,7 @@ class Evaluator {
46891
47091
  }
46892
47092
  this.cellsBeingComputed.add(cellId);
46893
47093
  return cell.isFormula
46894
- ? this.computeFormulaCell(cellPosition.sheetId, cell)
47094
+ ? this.computeFormulaCell(position.sheetId, cell)
46895
47095
  : evaluateLiteral(cell.content, localeFormat);
46896
47096
  }
46897
47097
  catch (e) {
@@ -46904,10 +47104,9 @@ class Evaluator {
46904
47104
  }
46905
47105
  }
46906
47106
  computeAndSave(position) {
46907
- const positionId = this.encoder.encode(position);
46908
- const evaluatedCell = this.computeCell(positionId);
46909
- if (!this.evaluatedCells.has(positionId)) {
46910
- this.setEvaluatedCell(positionId, evaluatedCell);
47107
+ const evaluatedCell = this.computeCell(position);
47108
+ if (!this.evaluatedCells.has(position)) {
47109
+ this.evaluatedCells.set(position, evaluatedCell);
46911
47110
  }
46912
47111
  return evaluatedCell;
46913
47112
  }
@@ -46945,24 +47144,23 @@ class Evaluator {
46945
47144
  throw new EvaluationError(_t("Result couldn't be automatically expanded. Please insert more columns and rows."));
46946
47145
  }
46947
47146
  updateSpreadRelation({ sheetId, col, row, }) {
46948
- const arrayFormulaPositionId = this.encoder.encode({ sheetId, col, row });
47147
+ const arrayFormulaPosition = { sheetId, col, row };
46949
47148
  return (i, j) => {
46950
47149
  const position = { sheetId, col: i + col, row: j + row };
46951
- const resultPositionId = this.encoder.encode(position);
46952
- this.spreadingRelations.addRelation({ resultPositionId, arrayFormulaPositionId });
47150
+ this.spreadingRelations.addRelation({ resultPosition: position, arrayFormulaPosition });
46953
47151
  };
46954
47152
  }
46955
- checkCollision({ sheetId, col, row }) {
46956
- const formulaPositionId = this.encoder.encode({ sheetId, col, row });
47153
+ checkCollision(formulaPosition) {
47154
+ const { sheetId, col, row } = formulaPosition;
46957
47155
  return (i, j) => {
46958
47156
  const position = { sheetId: sheetId, col: i + col, row: j + row };
46959
47157
  const rawCell = this.getters.getCell(position);
46960
47158
  if (rawCell?.content ||
46961
47159
  this.getters.getEvaluatedCell(position).type !== CellValueType.empty) {
46962
- this.blockedArrayFormulas.add(formulaPositionId);
47160
+ this.blockedArrayFormulas.add(formulaPosition);
46963
47161
  throw new EvaluationError(_t("Array result was not expanded because it would overwrite data in %s.", toXC(position.col, position.row)));
46964
47162
  }
46965
- this.blockedArrayFormulas.delete(formulaPositionId);
47163
+ this.blockedArrayFormulas.delete(formulaPosition);
46966
47164
  };
46967
47165
  }
46968
47166
  spreadValues({ sheetId, col, row }, matrixResult) {
@@ -46970,19 +47168,18 @@ class Evaluator {
46970
47168
  const position = { sheetId, col: i + col, row: j + row };
46971
47169
  const cell = this.getters.getCell(position);
46972
47170
  const evaluatedCell = createEvaluatedCell(nullValueToZeroValue(matrixResult[i][j]), this.getters.getLocale(), cell);
46973
- const positionId = this.encoder.encode(position);
46974
- this.setEvaluatedCell(positionId, evaluatedCell);
47171
+ this.evaluatedCells.set(position, evaluatedCell);
46975
47172
  // check if formula dependencies present in the spread zone
46976
47173
  // if so, they need to be recomputed
46977
- this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([positionId]));
47174
+ this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([position]));
46978
47175
  };
46979
47176
  }
46980
- invalidateSpreading(positionId) {
46981
- if (!this.spreadingRelations.isArrayFormula(positionId)) {
47177
+ invalidateSpreading(position) {
47178
+ if (!this.spreadingRelations.isArrayFormula(position)) {
46982
47179
  return;
46983
47180
  }
46984
- for (const child of this.spreadingRelations.getArrayResultPositionIds(positionId)) {
46985
- const content = this.getCell(child)?.content;
47181
+ for (const child of this.spreadingRelations.getArrayResultPositions(position)) {
47182
+ const content = this.getters.getCell(child)?.content;
46986
47183
  if (content) {
46987
47184
  // there's no point at re-evaluating overlapping array formulas,
46988
47185
  // there's still a collision
@@ -46992,28 +47189,25 @@ class Evaluator {
46992
47189
  this.nextPositionsToUpdate.addMany(this.getCellsDependingOn([child]));
46993
47190
  this.nextPositionsToUpdate.addMany(this.getArrayFormulasBlockedByOrSpreadingOn(child));
46994
47191
  }
46995
- this.spreadingRelations.removeNode(positionId);
47192
+ this.spreadingRelations.removeNode(position);
46996
47193
  }
46997
47194
  // ----------------------------------------------------------
46998
47195
  // COMMON FUNCTIONALITY
46999
47196
  // ----------------------------------------------------------
47000
- getDirectDependencies(positionId) {
47001
- const cell = this.getCell(positionId);
47197
+ getDirectDependencies(position) {
47198
+ const cell = this.getters.getCell(position);
47002
47199
  if (!cell?.isFormula) {
47003
47200
  return [];
47004
47201
  }
47005
47202
  return cell.compiledFormula.dependencies;
47006
47203
  }
47007
- getCellsDependingOn(positionIds) {
47204
+ getCellsDependingOn(positions) {
47008
47205
  const ranges = [];
47009
- for (const positionId of positionIds) {
47010
- ranges.push(this.encoder.decodeToBoundingBox(positionId));
47206
+ for (const position of positions) {
47207
+ ranges.push({ sheetId: position.sheetId, zone: positionToZone(position) });
47011
47208
  }
47012
47209
  return this.formulaDependencies().getCellsDependingOn(ranges);
47013
47210
  }
47014
- getCell(positionId) {
47015
- return this.getters.getCell(this.encoder.decode(positionId));
47016
- }
47017
47211
  }
47018
47212
  function forEachSpreadPositionInMatrix(nbColumns, nbRows, callback) {
47019
47213
  for (let i = 0; i < nbColumns; ++i) {
@@ -47039,85 +47233,6 @@ function nullValueToZeroValue(fPayload) {
47039
47233
  }
47040
47234
  return fPayload;
47041
47235
  }
47042
- /**
47043
- * Encode (and decode) cell positions { sheetId, col, row }
47044
- * to a single integer.
47045
- *
47046
- * `col` and `row` values are encoded on 21 bits each (max 2^21 = 2_097_152),
47047
- * An incremental integer id is assigned to each different sheet id, starting at 0.
47048
- *
47049
- * e.g.
47050
- * Given { col: 10, row: 4, sheetId: "abcde" }
47051
- * we have:
47052
- * - row "4" encoded on 21 bits: 000000000000000000100
47053
- * - col "10" encoded on 21 bits: 000000000000000001010
47054
- * - sheetId: let's say it's the 4th sheetId met, encoded to: 11
47055
- *
47056
- * The final encoded value is found by concatenating the 3 bit sequences:
47057
- *
47058
- * sheetId: 11
47059
- * col: 000000000000000001010
47060
- * row: 000000000000000000100
47061
- * => 11000000000000000001010000000000000000000100
47062
- *
47063
- * this binary sequence is the integer 13194160504836
47064
- */
47065
- class PositionBitsEncoder {
47066
- sheetMapping = {};
47067
- inverseSheetMapping = new Map();
47068
- constructor() {
47069
- try {
47070
- // @ts-ignore
47071
- o_spreadsheet.__DEBUG__ = o_spreadsheet.__DEBUG__ || {};
47072
- // @ts-ignore
47073
- o_spreadsheet.__DEBUG__.decodePosition = this.decode.bind(this);
47074
- // @ts-ignore
47075
- o_spreadsheet.__DEBUG__.encodePosition = this.encode.bind(this);
47076
- }
47077
- catch (error) { }
47078
- }
47079
- /**
47080
- * Encode a cell position to a single integer.
47081
- */
47082
- encode({ sheetId, col, row }) {
47083
- return (this.encodeSheet(sheetId) << 42n) | (BigInt(col) << 21n) | BigInt(row);
47084
- }
47085
- encodeBoundingBox({ sheetId, zone }) {
47086
- const positions = [];
47087
- forEachPositionsInZone(zone, (col, row) => {
47088
- positions.push(this.encode({ sheetId, col, row }));
47089
- });
47090
- return positions;
47091
- }
47092
- decode(id) {
47093
- // keep only the last 21 bits by AND-ing the bit sequence with 21 ones
47094
- const row = Number(id & 2097151n);
47095
- const col = Number((id >> 21n) & 2097151n);
47096
- const sheetId = this.decodeSheet(id >> 42n);
47097
- return { sheetId, col, row };
47098
- }
47099
- decodeToBoundingBox(id) {
47100
- const { sheetId, col, row } = this.decode(id);
47101
- return { sheetId, zone: { left: col, top: row, right: col, bottom: row } };
47102
- }
47103
- encodeSheet(sheetId) {
47104
- const sheetKey = this.sheetMapping[sheetId];
47105
- if (sheetKey === undefined) {
47106
- const newSheetKey = BigInt(Object.keys(this.sheetMapping).length);
47107
- this.sheetMapping[sheetId] = newSheetKey;
47108
- this.inverseSheetMapping.set(newSheetKey, sheetId);
47109
- return newSheetKey;
47110
- }
47111
- return sheetKey;
47112
- }
47113
- decodeSheet(sheetKey) {
47114
- const sheetId = this.inverseSheetMapping.get(sheetKey);
47115
- if (sheetId === undefined) {
47116
- throw new Error("Sheet id not found");
47117
- }
47118
- return sheetId;
47119
- }
47120
- }
47121
47236
  function updateEvalContextAndExecute(compiledFormula, compilationParams, sheetId, cellId) {
47122
47237
  compilationParams.evalContext.__originCellXC = lazy(() => {
47123
47238
  if (!cellId) {
@@ -47264,6 +47379,10 @@ class EvaluationPlugin extends UIPlugin {
47264
47379
  this.evaluator.updateDependencies(cmd);
47265
47380
  }
47266
47381
  break;
47382
+ case "DUPLICATE_SHEET":
47383
+ case "CREATE_SHEET":
47384
+ this.shouldRebuildDependenciesGraph = true;
47385
+ break;
47267
47386
  case "EVALUATE_CELLS":
47268
47387
  this.evaluator.evaluateAllCells();
47269
47388
  break;
@@ -60048,6 +60167,6 @@ exports.tokenColors = tokenColors;
60048
60167
  exports.tokenize = tokenize;
60049
60168
 
60050
60169
 
60051
- __info__.version = "17.2.0-alpha.8";
60052
- __info__.date = "2024-03-15T11:01:01.388Z";
60053
- __info__.hash = "11cf381";
60170
+ __info__.version = "17.3.0-alpha.0";
60171
+ __info__.date = "2024-03-20T13:42:32.042Z";
60172
+ __info__.hash = "073e154";