@univerjs-pro/engine-formula-rust 1.0.0-insiders.20260809-70eefdd → 1.0.0-rc.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.
package/lib/cjs/index.cjs CHANGED
@@ -246,7 +246,7 @@ var import_core2 = require("@univerjs/core");
246
246
 
247
247
  // src/services/rust-formula-projection.service.ts
248
248
  var import_core = require("@univerjs/core");
249
- var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
249
+ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_SET_SUPER_TABLES_MUTATION_ID = "formula.mutation.set-super-tables", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
250
250
  "sheet.mutation.set-range-values",
251
251
  "sheet.mutation.insert-row",
252
252
  "sheet.mutation.insert-col",
@@ -273,6 +273,7 @@ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_P
273
273
  "formula.mutation.set-image-formula-data",
274
274
  "formula.mutation.set-other-formula",
275
275
  "formula.mutation.set-super-table",
276
+ RUST_SET_SUPER_TABLES_MUTATION_ID,
276
277
  "formula.mutation.set-super-table-option",
277
278
  "formula.mutation.remove-defined-name",
278
279
  "formula.mutation.remove-feature-calculation",
@@ -508,13 +509,26 @@ var RustFormulaProjectionService = class {
508
509
  }
509
510
  };
510
511
  function normalizeRustEngineMutation(mutation) {
511
- let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.params ?? {};
512
+ let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.id === RUST_SET_SUPER_TABLES_MUTATION_ID ? normalizeSetSuperTablesMutationParams(mutation.params) : mutation.params ?? {};
512
513
  return {
513
514
  id: mutation.id,
514
515
  params,
515
516
  dirtyRanges: Array.isArray(mutation.dirtyRanges) ? mutation.dirtyRanges : []
516
517
  };
517
518
  }
519
+ function normalizeSetSuperTablesMutationParams(params) {
520
+ return !isRecord(params) || typeof params.unitId != "string" || !Array.isArray(params.tables) ? params ?? {} : {
521
+ ...params,
522
+ tables: params.tables.map((entry) => {
523
+ let normalized = normalizeSetSuperTableMutationParams(
524
+ isRecord(entry) ? { ...entry, unitId: params.unitId } : entry
525
+ );
526
+ if (!isRecord(normalized)) return normalized;
527
+ let { unitId: _unitId, ...table } = normalized;
528
+ return table;
529
+ })
530
+ };
531
+ }
518
532
  function normalizeSetSuperTableMutationParams(params) {
519
533
  if (!isRecord(params) || !isRecord(params.reference))
520
534
  return params ?? {};
@@ -1082,6 +1096,7 @@ var RustEngineSyncController = class extends import_core2.Disposable {
1082
1096
  _nativeReady = !1;
1083
1097
  _snapshotLoaded = !1;
1084
1098
  _projectionLoadPromise = null;
1099
+ _projectionDeltaBarrier = Promise.resolve();
1085
1100
  _pendingMutations = [];
1086
1101
  _projectionManagedTableUnits = /* @__PURE__ */ new Set();
1087
1102
  _initialize() {
@@ -1102,6 +1117,13 @@ var RustEngineSyncController = class extends import_core2.Disposable {
1102
1117
  }
1103
1118
  async waitForProjectionLoad() {
1104
1119
  await this._projectionLoadPromise;
1120
+ let barrier;
1121
+ do
1122
+ barrier = this._projectionDeltaBarrier, await barrier;
1123
+ while (barrier !== this._projectionDeltaBarrier);
1124
+ }
1125
+ setProjectionDeltaBarrier(barrier) {
1126
+ this._projectionDeltaBarrier = barrier;
1105
1127
  }
1106
1128
  async _loadProjection(workbooks) {
1107
1129
  await loadNativeBinding(), this._nativeReady = !0;
@@ -1133,7 +1155,7 @@ var RustEngineSyncController = class extends import_core2.Disposable {
1133
1155
  }
1134
1156
  }
1135
1157
  dispose() {
1136
- if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, !this._nativeReady) {
1158
+ if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, this._projectionDeltaBarrier = Promise.resolve(), !this._nativeReady) {
1137
1159
  this._snapshotLoaded = !1;
1138
1160
  return;
1139
1161
  }
@@ -1267,7 +1289,7 @@ async function analyzeNumberFormatBatch(input) {
1267
1289
  var import_core5 = require("@univerjs/core"), import_engine_formula4 = require("@univerjs/engine-formula"), import_engine_formula5 = require("@univerjs-pro/engine-formula");
1268
1290
 
1269
1291
  // package.json
1270
- var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-insiders.20260809-70eefdd";
1292
+ var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-rc.0";
1271
1293
 
1272
1294
  // src/controllers/rust-same-runtime-projection.controller.ts
1273
1295
  var import_core3 = require("@univerjs/core"), import_engine_formula = require("@univerjs/engine-formula");
@@ -1290,11 +1312,12 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1290
1312
  _activeDirtyManagerService;
1291
1313
  _initialProjectionLoad = Promise.resolve();
1292
1314
  _projectionDeltaQueue = Promise.resolve();
1315
+ _baseProjectionStructures = /* @__PURE__ */ new Map();
1293
1316
  _initialize() {
1294
1317
  for (let base of this._univerInstanceService.getAllUnitsForType(
1295
1318
  import_core3.UniverInstanceType.UNIVER_BASE
1296
1319
  ))
1297
- this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId());
1320
+ this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId()), this._rememberBaseProjectionStructure(base.getSnapshot());
1298
1321
  this._initialProjectionLoad = this._loadAllWorkbookProjections(), this.disposeWithMe(
1299
1322
  (0, import_core3.toDisposable)(
1300
1323
  this._univerInstanceService.getTypeOfUnitAdded$(import_core3.UniverInstanceType.UNIVER_SHEET).subscribe(({ unit }) => {
@@ -1309,7 +1332,7 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1309
1332
  ), this.disposeWithMe(
1310
1333
  (0, import_core3.toDisposable)(
1311
1334
  this._univerInstanceService.getTypeOfUnitAdded$(import_core3.UniverInstanceType.UNIVER_BASE).subscribe(({ unit }) => {
1312
- this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1335
+ this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._rememberBaseProjectionStructure(unit.getSnapshot()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1313
1336
  })
1314
1337
  )
1315
1338
  ), this.disposeWithMe(
@@ -1319,7 +1342,7 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1319
1342
  ), this.disposeWithMe(
1320
1343
  (0, import_core3.toDisposable)(
1321
1344
  this._univerInstanceService.getTypeOfUnitDisposed$(import_core3.UniverInstanceType.UNIVER_BASE).subscribe((base) => {
1322
- this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1345
+ this._baseProjectionStructures.delete(base.getUnitId()), this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1323
1346
  () => this._rustEngineSyncController.disposeProjectionWorkbook(base.getUnitId())
1324
1347
  );
1325
1348
  })
@@ -1343,7 +1366,7 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1343
1366
  if (!this._disposed)
1344
1367
  return task();
1345
1368
  }).catch(() => {
1346
- });
1369
+ }), this._rustEngineSyncController.setProjectionDeltaBarrier(this._projectionDeltaQueue);
1347
1370
  }
1348
1371
  async _syncBaseMutation(command) {
1349
1372
  if (!hasUnitId2(command.params)) return;
@@ -1352,7 +1375,65 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1352
1375
  this._applyBaseDirtyRanges(unitId, dirtyRanges);
1353
1376
  return;
1354
1377
  }
1355
- await this._upsertBaseProjection(unitId);
1378
+ hasStructureInvalidation && this._isBaseProjectionStructureUnchanged(unitId) || hasStructureInvalidation && this._applyBaseFieldInsertion(unitId, dirtyRanges) || await this._upsertBaseProjection(unitId);
1379
+ }
1380
+ _applyBaseFieldInsertion(unitId, dirtyRanges) {
1381
+ let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1382
+ if (!base || !previous) return !1;
1383
+ let snapshot = base.getSnapshot(), insertion = findSingleBaseFieldInsertion(previous, snapshot);
1384
+ if (!insertion) return !1;
1385
+ let projectionData = this._getBaseProjectionData(unitId), sheet = projectionData?.calculationData.allUnitData[unitId]?.[insertion.tableId];
1386
+ if (!projectionData || !sheet) return !1;
1387
+ let formulaData = projectionData.formulaData[unitId]?.[insertion.tableId], cellValue = {};
1388
+ for (let row = 0; row < sheet.rowCount; row++) {
1389
+ let value = sheet.cellData.getValue(row, insertion.column), formula = formulaData?.[row]?.[insertion.column];
1390
+ cellValue[row] = {
1391
+ [insertion.column]: formula ? { ...value, ...formula } : value ?? null
1392
+ };
1393
+ }
1394
+ let insertedRange = {
1395
+ startRow: 0,
1396
+ endRow: Math.max(0, sheet.rowCount - 1),
1397
+ startColumn: insertion.column,
1398
+ endColumn: insertion.column
1399
+ };
1400
+ return this._rustEngineSyncController.applyProjectionMutation({
1401
+ id: "sheet.mutation.insert-col",
1402
+ params: {
1403
+ unitId,
1404
+ subUnitId: insertion.tableId,
1405
+ range: insertedRange
1406
+ },
1407
+ dirtyRanges: []
1408
+ }), this._rustEngineSyncController.applyProjectionMutation({
1409
+ id: "sheet.mutation.set-range-values",
1410
+ params: {
1411
+ unitId,
1412
+ subUnitId: insertion.tableId,
1413
+ cellValue
1414
+ },
1415
+ dirtyRanges: [
1416
+ {
1417
+ unitId,
1418
+ sheetId: insertion.tableId,
1419
+ range: insertedRange
1420
+ }
1421
+ ]
1422
+ }), this._rustEngineSyncController.applyProjectionMutation({
1423
+ id: RUST_SET_SUPER_TABLES_MUTATION_ID,
1424
+ params: {
1425
+ unitId,
1426
+ tables: this._getBaseTableEntries(
1427
+ insertion.tableId,
1428
+ snapshot,
1429
+ (0, import_core3.createBaseFormulaTableNameMap)(snapshot)
1430
+ ).map(([tableName, reference]) => ({
1431
+ tableName,
1432
+ reference
1433
+ }))
1434
+ },
1435
+ dirtyRanges: []
1436
+ }), dirtyRanges.length > 0 && this._applyBaseDirtyRanges(unitId, dirtyRanges), this._rememberBaseProjectionStructure(snapshot), !0;
1356
1437
  }
1357
1438
  _applyBaseDirtyRanges(unitId, dirtyRanges) {
1358
1439
  let projectionData = this._getBaseProjectionData(unitId);
@@ -1394,6 +1475,15 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1394
1475
  formulaData
1395
1476
  );
1396
1477
  workbook && this._rustEngineSyncController.upsertProjectionWorkbook(workbook);
1478
+ let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE);
1479
+ base && this._rememberBaseProjectionStructure(base.getSnapshot());
1480
+ }
1481
+ _rememberBaseProjectionStructure(snapshot) {
1482
+ this._baseProjectionStructures.set(snapshot.id, createBaseProjectionStructure(snapshot));
1483
+ }
1484
+ _isBaseProjectionStructureUnchanged(unitId) {
1485
+ let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1486
+ return !!(base && previous && isSameBaseProjectionStructure(previous, createBaseProjectionStructure(base.getSnapshot())));
1397
1487
  }
1398
1488
  _getBaseProjectionData(unitId) {
1399
1489
  let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE);
@@ -1403,9 +1493,9 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1403
1493
  name: snapshot.name,
1404
1494
  unitType: import_core3.UniverInstanceType.UNIVER_BASE
1405
1495
  }
1406
- }, formulaData = { [unitId]: {} };
1496
+ }, formulaData = { [unitId]: {} }, formulaTableNames = (0, import_core3.createBaseFormulaTableNameMap)(snapshot), normalizeFormulaTableReferences = (0, import_core3.createBaseFormulaTableReferenceNormalizer)(snapshot, formulaTableNames);
1407
1497
  for (let table of Object.values(snapshot.tables)) {
1408
- let fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null);
1498
+ let fieldOrder = table.fieldOrder;
1409
1499
  allUnitData[unitId][table.id] = {
1410
1500
  cellData: new import_core3.ObjectMatrix(
1411
1501
  buildBaseRuntimeCellData(table)
@@ -1414,18 +1504,25 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1414
1504
  columnCount: fieldOrder.length,
1415
1505
  rowData: {},
1416
1506
  columnData: {}
1417
- }, unitSheetNameMap[unitId][table.name] = table.id;
1418
- let tableFormulaData = {}, recordOrder = baseRecordOrder(table);
1507
+ }, unitSheetNameMap[unitId][formulaTableNames.get(table.id)] = table.id;
1508
+ let tableFormulaData = {}, normalizedFormulaByFieldId = /* @__PURE__ */ new Map(), recordOrder = baseRecordOrder(table);
1419
1509
  for (let row = 0; row < recordOrder.length; row++)
1420
1510
  if (table.records[recordOrder[row]])
1421
1511
  for (let column = 0; column < fieldOrder.length; column++) {
1422
1512
  let field = table.fields[fieldOrder[column]];
1423
1513
  if (!field || field.type !== "formula") continue;
1424
1514
  let formula = String(field.config?.formula ?? "").trim();
1425
- formula && ((tableFormulaData[row] ??= {})[column] = {
1426
- f: normalizeBaseFormulaForEngine(formula, table, snapshot),
1515
+ if (!formula) continue;
1516
+ let normalizedFormula = normalizedFormulaByFieldId.get(field.id);
1517
+ normalizedFormula == null && (normalizedFormula = normalizeBaseFormulaForEngine(
1518
+ formula,
1519
+ table,
1520
+ formulaTableNames,
1521
+ normalizeFormulaTableReferences
1522
+ ), normalizedFormulaByFieldId.set(field.id, normalizedFormula)), (tableFormulaData[row] ??= {})[column] = {
1523
+ f: normalizedFormula,
1427
1524
  si: field.id
1428
- });
1525
+ };
1429
1526
  }
1430
1527
  formulaData[unitId][table.id] = tableFormulaData;
1431
1528
  }
@@ -1494,25 +1591,17 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1494
1591
  _createBaseTableResource(unitId) {
1495
1592
  let base = this._univerInstanceService.getUnit(unitId, import_core3.UniverInstanceType.UNIVER_BASE);
1496
1593
  if (base) {
1497
- let snapshot = base.getSnapshot(), tables = Object.values(snapshot.tables), data2 = {};
1498
- for (let table of tables) {
1499
- let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), tableNames = /* @__PURE__ */ new Set([table.id, getEngineBaseTableName(table, snapshot)]);
1500
- isUniqueBaseTableName(table, snapshot) && tableNames.add(table.name);
1501
- for (let name2 of tableNames)
1594
+ let snapshot = base.getSnapshot(), tables = Object.values(snapshot.tables), formulaTableNames = (0, import_core3.createBaseFormulaTableNameMap)(snapshot), data2 = {};
1595
+ for (let table of tables)
1596
+ for (let [name2, reference] of this._getBaseTableEntries(table.id, snapshot, formulaTableNames)) {
1597
+ let columns = Array.from(reference.titleMap.entries()).toSorted((left, right) => left[1] - right[1]).map(([displayName]) => ({ displayName }));
1502
1598
  (data2[table.id] ??= { tables: [] }).tables.push({
1503
1599
  name: name2,
1504
- range: {
1505
- startRow: 0,
1506
- endRow: Math.max(0, recordCount - 1),
1507
- startColumn: 0,
1508
- endColumn: Math.max(0, fieldOrder.length - 1)
1509
- },
1510
- columns: fieldOrder.map((fieldId) => ({
1511
- displayName: table.fields[fieldId]?.name ?? fieldId
1512
- })),
1513
- options: { showHeader: !1 }
1600
+ range: reference.range,
1601
+ columns,
1602
+ options: { showHeader: reference.showHeader !== !1 }
1514
1603
  });
1515
- }
1604
+ }
1516
1605
  return [
1517
1606
  {
1518
1607
  name: "SHEET_TABLE_PLUGIN",
@@ -1539,6 +1628,22 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1539
1628
  }
1540
1629
  ];
1541
1630
  }
1631
+ _getBaseTableEntries(tableId, snapshot, formulaTableNames) {
1632
+ let table = snapshot.tables[tableId];
1633
+ if (!table) return [];
1634
+ let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder, reference = {
1635
+ sheetId: table.id,
1636
+ range: {
1637
+ startRow: 0,
1638
+ endRow: Math.max(0, recordCount - 1),
1639
+ startColumn: 0,
1640
+ endColumn: Math.max(0, fieldOrder.length - 1)
1641
+ },
1642
+ titleMap: new Map(fieldOrder.map((fieldId, index) => [table.fields[fieldId]?.name ?? fieldId, index])),
1643
+ showHeader: !1
1644
+ };
1645
+ return [[formulaTableNames.get(table.id), reference]];
1646
+ }
1542
1647
  _rawCellData(cellData) {
1543
1648
  let raw = cellData.getMatrix(), result = {};
1544
1649
  for (let [row, values] of Object.entries(raw ?? {})) {
@@ -1549,16 +1654,58 @@ var RustSameRuntimeProjectionController = class extends import_core3.Disposable
1549
1654
  }
1550
1655
  return result;
1551
1656
  }
1552
- }, BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_REFERENCE_PREFIX = /\b[A-Z_][\w.]*\[/gi, BASE_SCOPED_FIELD_REF_PATTERN = /(^|[^\w[])\[\[\s*#(This Row|Data)\s*\],\s*\[([^\]]+)\]\]/gi, BASE_CURRENT_ROW_FIELD_REF_PATTERN = /(^|[^\w[])\[@\[([^\]]+)\]\](?!\])/g, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1553
- function normalizeBaseFormulaForEngine(formula, currentTable, snapshot) {
1554
- let references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1555
- return protectBaseTableReferences(protectBaseExternalReferences(formula, hold), hold).replace(
1657
+ };
1658
+ function createBaseProjectionStructure(snapshot) {
1659
+ let formulaTableNames = (0, import_core3.createBaseFormulaTableNameMap)(snapshot);
1660
+ return {
1661
+ unitName: snapshot.name,
1662
+ tables: Object.fromEntries(
1663
+ Object.values(snapshot.tables).map((table) => [
1664
+ table.id,
1665
+ {
1666
+ formulaName: formulaTableNames.get(table.id),
1667
+ fieldOrder: [...table.fieldOrder],
1668
+ recordOrder: baseRecordOrder(table),
1669
+ fieldSignatures: Object.fromEntries(
1670
+ Object.entries(table.fields).map(([fieldId, field]) => [fieldId, JSON.stringify(field)])
1671
+ )
1672
+ }
1673
+ ])
1674
+ )
1675
+ };
1676
+ }
1677
+ function isSameBaseProjectionStructure(left, right) {
1678
+ return JSON.stringify(left) === JSON.stringify(right);
1679
+ }
1680
+ function findSingleBaseFieldInsertion(previous, snapshot) {
1681
+ let current = createBaseProjectionStructure(snapshot), previousTableIds = Object.keys(previous.tables).toSorted(), currentTableIds = Object.keys(current.tables).toSorted();
1682
+ if (JSON.stringify(previousTableIds) !== JSON.stringify(currentTableIds)) return null;
1683
+ let insertion = null;
1684
+ for (let tableId of currentTableIds) {
1685
+ let before = previous.tables[tableId], after = current.tables[tableId];
1686
+ if (!before || !after || before.formulaName !== after.formulaName || JSON.stringify(before.recordOrder) !== JSON.stringify(after.recordOrder)) return null;
1687
+ for (let fieldId of before.fieldOrder)
1688
+ if (before.fieldSignatures[fieldId] !== after.fieldSignatures[fieldId]) return null;
1689
+ if (JSON.stringify(before.fieldOrder) === JSON.stringify(after.fieldOrder)) continue;
1690
+ if (insertion || after.fieldOrder.length !== before.fieldOrder.length + 1) return null;
1691
+ let column = after.fieldOrder.findIndex((fieldId, index) => fieldId !== before.fieldOrder[index]);
1692
+ if (column < 0) return null;
1693
+ let withoutInserted = after.fieldOrder.filter((_, index) => index !== column);
1694
+ if (JSON.stringify(withoutInserted) !== JSON.stringify(before.fieldOrder)) return null;
1695
+ insertion = { tableId, column };
1696
+ }
1697
+ return insertion;
1698
+ }
1699
+ var BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_REFERENCE_PREFIX = /\b[A-Z_][\w.]*\[/gi, BASE_SCOPED_FIELD_REF_PATTERN = /(^|[^\w[])\[\[\s*#(This Row|Data)\s*\],\s*\[([^\]]+)\]\]/gi, BASE_CURRENT_ROW_FIELD_REF_PATTERN = /(^|[^\w[])\[@\[([^\]]+)\]\](?!\])/g, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1700
+ function normalizeBaseFormulaForEngine(formula, currentTable, formulaTableNames, normalizeFormulaTableReferences) {
1701
+ let canonicalFormula = normalizeFormulaTableReferences(formula), references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1702
+ return protectBaseTableReferences(protectBaseExternalReferences(canonicalFormula, hold), hold).replace(
1556
1703
  BASE_LEGACY_FIELD_REF_PATTERN,
1557
- (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, snapshot))
1704
+ (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))
1558
1705
  ).replace(BASE_SCOPED_FIELD_REF_PATTERN, (_match, prefix, scope, fieldName) => {
1559
- let tableName = getEngineBaseTableName(currentTable, snapshot);
1706
+ let tableName = formulaTableNames.get(currentTable.id) ?? currentTable.name;
1560
1707
  return `${prefix}${hold(`${tableName}[[#${scope}],[${fieldName}]]`)}`;
1561
- }).replace(BASE_CURRENT_ROW_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, snapshot))}`).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, snapshot))}`).replace(
1708
+ }).replace(BASE_CURRENT_ROW_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(
1562
1709
  /__BASE_FORMULA_REF_(\d+)__/g,
1563
1710
  (_match, index) => references[Number(index)] ?? ""
1564
1711
  );
@@ -1607,37 +1754,21 @@ function isInsideBaseFormulaString(formula, position) {
1607
1754
  }
1608
1755
  return inString;
1609
1756
  }
1610
- function createEngineThisRowRef(table, fieldName, snapshot) {
1611
- return `${getEngineBaseTableName(table, snapshot)}[[#This Row],[${fieldName}]]`;
1612
- }
1613
- function getEngineBaseTableName(table, snapshot) {
1614
- return isUniqueBaseTableName(table, snapshot) && isExcelTableName(table.name) ? table.name : createStableExcelTableName(table.id);
1615
- }
1616
- function isUniqueBaseTableName(table, snapshot) {
1617
- let normalizedName = table.name.trim().toLowerCase();
1618
- return Object.values(snapshot.tables).filter((candidate) => candidate.name.trim().toLowerCase() === normalizedName).length === 1;
1619
- }
1620
- function isExcelTableName(name2) {
1621
- return name2.length === 0 || name2.length > 255 || !/^[A-Za-z_][A-Za-z0-9_.]*$/.test(name2) ? !1 : !/^[RC]$/i.test(name2) && !/^[A-Za-z]{1,3}[1-9]\d*$/.test(name2) && !/^R(?:\d+)?C(?:\d+)?$/i.test(name2);
1622
- }
1623
- function createStableExcelTableName(tableId) {
1624
- return `_T_${Array.from(
1625
- tableId,
1626
- (character) => /[A-Za-z0-9]/.test(character) ? character : `_x${character.codePointAt(0)?.toString(16) ?? "0"}_`
1627
- ).join("")}`;
1757
+ function createEngineThisRowRef(table, fieldName, formulaTableNames) {
1758
+ return `${formulaTableNames.get(table.id) ?? table.name}[[#This Row],[${fieldName}]]`;
1628
1759
  }
1629
1760
  function baseRecordOrder(table) {
1630
1761
  return table.recordOrder ? table.recordOrder.filter((recordId) => table.records[recordId] != null) : Object.values(table.records ?? {}).toSorted((left, right) => left.orderKey.localeCompare(right.orderKey)).map((record) => record.id);
1631
1762
  }
1632
1763
  function buildBaseRuntimeCellData(table) {
1633
- let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), recordOrder = baseRecordOrder(table);
1764
+ let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder, recordOrder = baseRecordOrder(table);
1634
1765
  for (let row = 0; row < recordOrder.length; row++) {
1635
1766
  let record = table.records[recordOrder[row]];
1636
1767
  if (record) {
1637
1768
  cellData[row] = { ...cellData[row] };
1638
1769
  for (let column = 0; column < fieldOrder.length; column++) {
1639
1770
  let fieldId = fieldOrder[column];
1640
- Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1771
+ table.fields[fieldId] && Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1641
1772
  }
1642
1773
  }
1643
1774
  }
package/lib/es/index.js CHANGED
@@ -179,7 +179,7 @@ import {
179
179
  CellValueType,
180
180
  ObjectMatrix
181
181
  } from "@univerjs/core";
182
- var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
182
+ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_PLUGIN", EXTERNAL_DATA_RESOURCE = "SHEET_EXTERNAL_DATA_PLUGIN", EXTERNAL_DATA_SIGNATURE = "external_data", RUST_UPSERT_REFERENCE_DATA_MUTATION_ID = "formula.mutation.upsert-reference-data", RUST_UPSERT_PROJECTION_WORKBOOK_MUTATION_ID = "formula.mutation.upsert-projection-workbook", RUST_SET_SUPER_TABLES_MUTATION_ID = "formula.mutation.set-super-tables", RUST_FORMULA_RELEVANT_MUTATIONS = /* @__PURE__ */ new Set([
183
183
  "sheet.mutation.set-range-values",
184
184
  "sheet.mutation.insert-row",
185
185
  "sheet.mutation.insert-col",
@@ -206,6 +206,7 @@ var XLSX_WORKBOOK_CALCULATION_RESOURCE = "UNIVER_CLI_XLSX_WORKBOOK_CALCULATION_P
206
206
  "formula.mutation.set-image-formula-data",
207
207
  "formula.mutation.set-other-formula",
208
208
  "formula.mutation.set-super-table",
209
+ RUST_SET_SUPER_TABLES_MUTATION_ID,
209
210
  "formula.mutation.set-super-table-option",
210
211
  "formula.mutation.remove-defined-name",
211
212
  "formula.mutation.remove-feature-calculation",
@@ -441,13 +442,26 @@ var RustFormulaProjectionService = class {
441
442
  }
442
443
  };
443
444
  function normalizeRustEngineMutation(mutation) {
444
- let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.params ?? {};
445
+ let params = mutation.id === "formula.mutation.set-super-table" ? normalizeSetSuperTableMutationParams(mutation.params) : mutation.id === RUST_SET_SUPER_TABLES_MUTATION_ID ? normalizeSetSuperTablesMutationParams(mutation.params) : mutation.params ?? {};
445
446
  return {
446
447
  id: mutation.id,
447
448
  params,
448
449
  dirtyRanges: Array.isArray(mutation.dirtyRanges) ? mutation.dirtyRanges : []
449
450
  };
450
451
  }
452
+ function normalizeSetSuperTablesMutationParams(params) {
453
+ return !isRecord(params) || typeof params.unitId != "string" || !Array.isArray(params.tables) ? params ?? {} : {
454
+ ...params,
455
+ tables: params.tables.map((entry) => {
456
+ let normalized = normalizeSetSuperTableMutationParams(
457
+ isRecord(entry) ? { ...entry, unitId: params.unitId } : entry
458
+ );
459
+ if (!isRecord(normalized)) return normalized;
460
+ let { unitId: _unitId, ...table } = normalized;
461
+ return table;
462
+ })
463
+ };
464
+ }
451
465
  function normalizeSetSuperTableMutationParams(params) {
452
466
  if (!isRecord(params) || !isRecord(params.reference))
453
467
  return params ?? {};
@@ -1015,6 +1029,7 @@ var RustEngineSyncController = class extends Disposable {
1015
1029
  _nativeReady = !1;
1016
1030
  _snapshotLoaded = !1;
1017
1031
  _projectionLoadPromise = null;
1032
+ _projectionDeltaBarrier = Promise.resolve();
1018
1033
  _pendingMutations = [];
1019
1034
  _projectionManagedTableUnits = /* @__PURE__ */ new Set();
1020
1035
  _initialize() {
@@ -1035,6 +1050,13 @@ var RustEngineSyncController = class extends Disposable {
1035
1050
  }
1036
1051
  async waitForProjectionLoad() {
1037
1052
  await this._projectionLoadPromise;
1053
+ let barrier;
1054
+ do
1055
+ barrier = this._projectionDeltaBarrier, await barrier;
1056
+ while (barrier !== this._projectionDeltaBarrier);
1057
+ }
1058
+ setProjectionDeltaBarrier(barrier) {
1059
+ this._projectionDeltaBarrier = barrier;
1038
1060
  }
1039
1061
  async _loadProjection(workbooks) {
1040
1062
  await loadNativeBinding(), this._nativeReady = !0;
@@ -1066,7 +1088,7 @@ var RustEngineSyncController = class extends Disposable {
1066
1088
  }
1067
1089
  }
1068
1090
  dispose() {
1069
- if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, !this._nativeReady) {
1091
+ if (super.dispose(), this._pendingMutations.length = 0, this._projectionManagedTableUnits.clear(), this._projectionLoadPromise = null, this._projectionDeltaBarrier = Promise.resolve(), !this._nativeReady) {
1070
1092
  this._snapshotLoaded = !1;
1071
1093
  return;
1072
1094
  }
@@ -1202,12 +1224,14 @@ import { ICalculateFormulaService } from "@univerjs/engine-formula";
1202
1224
  import { UniverProFormulaEnginePlugin } from "@univerjs-pro/engine-formula";
1203
1225
 
1204
1226
  // package.json
1205
- var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-insiders.20260809-70eefdd";
1227
+ var name = "@univerjs-pro/engine-formula-rust", version = "1.0.0-rc.0";
1206
1228
 
1207
1229
  // src/controllers/rust-same-runtime-projection.controller.ts
1208
1230
  import {
1209
1231
  CellValueType as CellValueType2,
1210
1232
  CommandType as CommandType2,
1233
+ createBaseFormulaTableReferenceNormalizer,
1234
+ createBaseFormulaTableNameMap,
1211
1235
  Disposable as Disposable2,
1212
1236
  ICommandService as ICommandService2,
1213
1237
  IUniverInstanceService,
@@ -1242,11 +1266,12 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1242
1266
  _activeDirtyManagerService;
1243
1267
  _initialProjectionLoad = Promise.resolve();
1244
1268
  _projectionDeltaQueue = Promise.resolve();
1269
+ _baseProjectionStructures = /* @__PURE__ */ new Map();
1245
1270
  _initialize() {
1246
1271
  for (let base of this._univerInstanceService.getAllUnitsForType(
1247
1272
  UniverInstanceType.UNIVER_BASE
1248
1273
  ))
1249
- this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId());
1274
+ this._rustEngineSyncController.setProjectionManagedTableUnit(base.getUnitId()), this._rememberBaseProjectionStructure(base.getSnapshot());
1250
1275
  this._initialProjectionLoad = this._loadAllWorkbookProjections(), this.disposeWithMe(
1251
1276
  toDisposable(
1252
1277
  this._univerInstanceService.getTypeOfUnitAdded$(UniverInstanceType.UNIVER_SHEET).subscribe(({ unit }) => {
@@ -1261,7 +1286,7 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1261
1286
  ), this.disposeWithMe(
1262
1287
  toDisposable(
1263
1288
  this._univerInstanceService.getTypeOfUnitAdded$(UniverInstanceType.UNIVER_BASE).subscribe(({ unit }) => {
1264
- this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1289
+ this._rustEngineSyncController.setProjectionManagedTableUnit(unit.getUnitId()), this._rememberBaseProjectionStructure(unit.getSnapshot()), this._enqueueProjectionDelta(() => this._upsertBaseProjection(unit.getUnitId()));
1265
1290
  })
1266
1291
  )
1267
1292
  ), this.disposeWithMe(
@@ -1271,7 +1296,7 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1271
1296
  ), this.disposeWithMe(
1272
1297
  toDisposable(
1273
1298
  this._univerInstanceService.getTypeOfUnitDisposed$(UniverInstanceType.UNIVER_BASE).subscribe((base) => {
1274
- this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1299
+ this._baseProjectionStructures.delete(base.getUnitId()), this._rustEngineSyncController.unsetProjectionManagedTableUnit(base.getUnitId()), this._enqueueProjectionDelta(
1275
1300
  () => this._rustEngineSyncController.disposeProjectionWorkbook(base.getUnitId())
1276
1301
  );
1277
1302
  })
@@ -1295,7 +1320,7 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1295
1320
  if (!this._disposed)
1296
1321
  return task();
1297
1322
  }).catch(() => {
1298
- });
1323
+ }), this._rustEngineSyncController.setProjectionDeltaBarrier(this._projectionDeltaQueue);
1299
1324
  }
1300
1325
  async _syncBaseMutation(command) {
1301
1326
  if (!hasUnitId2(command.params)) return;
@@ -1304,7 +1329,65 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1304
1329
  this._applyBaseDirtyRanges(unitId, dirtyRanges);
1305
1330
  return;
1306
1331
  }
1307
- await this._upsertBaseProjection(unitId);
1332
+ hasStructureInvalidation && this._isBaseProjectionStructureUnchanged(unitId) || hasStructureInvalidation && this._applyBaseFieldInsertion(unitId, dirtyRanges) || await this._upsertBaseProjection(unitId);
1333
+ }
1334
+ _applyBaseFieldInsertion(unitId, dirtyRanges) {
1335
+ let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1336
+ if (!base || !previous) return !1;
1337
+ let snapshot = base.getSnapshot(), insertion = findSingleBaseFieldInsertion(previous, snapshot);
1338
+ if (!insertion) return !1;
1339
+ let projectionData = this._getBaseProjectionData(unitId), sheet = projectionData?.calculationData.allUnitData[unitId]?.[insertion.tableId];
1340
+ if (!projectionData || !sheet) return !1;
1341
+ let formulaData = projectionData.formulaData[unitId]?.[insertion.tableId], cellValue = {};
1342
+ for (let row = 0; row < sheet.rowCount; row++) {
1343
+ let value = sheet.cellData.getValue(row, insertion.column), formula = formulaData?.[row]?.[insertion.column];
1344
+ cellValue[row] = {
1345
+ [insertion.column]: formula ? { ...value, ...formula } : value ?? null
1346
+ };
1347
+ }
1348
+ let insertedRange = {
1349
+ startRow: 0,
1350
+ endRow: Math.max(0, sheet.rowCount - 1),
1351
+ startColumn: insertion.column,
1352
+ endColumn: insertion.column
1353
+ };
1354
+ return this._rustEngineSyncController.applyProjectionMutation({
1355
+ id: "sheet.mutation.insert-col",
1356
+ params: {
1357
+ unitId,
1358
+ subUnitId: insertion.tableId,
1359
+ range: insertedRange
1360
+ },
1361
+ dirtyRanges: []
1362
+ }), this._rustEngineSyncController.applyProjectionMutation({
1363
+ id: "sheet.mutation.set-range-values",
1364
+ params: {
1365
+ unitId,
1366
+ subUnitId: insertion.tableId,
1367
+ cellValue
1368
+ },
1369
+ dirtyRanges: [
1370
+ {
1371
+ unitId,
1372
+ sheetId: insertion.tableId,
1373
+ range: insertedRange
1374
+ }
1375
+ ]
1376
+ }), this._rustEngineSyncController.applyProjectionMutation({
1377
+ id: RUST_SET_SUPER_TABLES_MUTATION_ID,
1378
+ params: {
1379
+ unitId,
1380
+ tables: this._getBaseTableEntries(
1381
+ insertion.tableId,
1382
+ snapshot,
1383
+ createBaseFormulaTableNameMap(snapshot)
1384
+ ).map(([tableName, reference]) => ({
1385
+ tableName,
1386
+ reference
1387
+ }))
1388
+ },
1389
+ dirtyRanges: []
1390
+ }), dirtyRanges.length > 0 && this._applyBaseDirtyRanges(unitId, dirtyRanges), this._rememberBaseProjectionStructure(snapshot), !0;
1308
1391
  }
1309
1392
  _applyBaseDirtyRanges(unitId, dirtyRanges) {
1310
1393
  let projectionData = this._getBaseProjectionData(unitId);
@@ -1346,6 +1429,15 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1346
1429
  formulaData
1347
1430
  );
1348
1431
  workbook && this._rustEngineSyncController.upsertProjectionWorkbook(workbook);
1432
+ let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE);
1433
+ base && this._rememberBaseProjectionStructure(base.getSnapshot());
1434
+ }
1435
+ _rememberBaseProjectionStructure(snapshot) {
1436
+ this._baseProjectionStructures.set(snapshot.id, createBaseProjectionStructure(snapshot));
1437
+ }
1438
+ _isBaseProjectionStructureUnchanged(unitId) {
1439
+ let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE), previous = this._baseProjectionStructures.get(unitId);
1440
+ return !!(base && previous && isSameBaseProjectionStructure(previous, createBaseProjectionStructure(base.getSnapshot())));
1349
1441
  }
1350
1442
  _getBaseProjectionData(unitId) {
1351
1443
  let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE);
@@ -1355,9 +1447,9 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1355
1447
  name: snapshot.name,
1356
1448
  unitType: UniverInstanceType.UNIVER_BASE
1357
1449
  }
1358
- }, formulaData = { [unitId]: {} };
1450
+ }, formulaData = { [unitId]: {} }, formulaTableNames = createBaseFormulaTableNameMap(snapshot), normalizeFormulaTableReferences = createBaseFormulaTableReferenceNormalizer(snapshot, formulaTableNames);
1359
1451
  for (let table of Object.values(snapshot.tables)) {
1360
- let fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null);
1452
+ let fieldOrder = table.fieldOrder;
1361
1453
  allUnitData[unitId][table.id] = {
1362
1454
  cellData: new ObjectMatrix2(
1363
1455
  buildBaseRuntimeCellData(table)
@@ -1366,18 +1458,25 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1366
1458
  columnCount: fieldOrder.length,
1367
1459
  rowData: {},
1368
1460
  columnData: {}
1369
- }, unitSheetNameMap[unitId][table.name] = table.id;
1370
- let tableFormulaData = {}, recordOrder = baseRecordOrder(table);
1461
+ }, unitSheetNameMap[unitId][formulaTableNames.get(table.id)] = table.id;
1462
+ let tableFormulaData = {}, normalizedFormulaByFieldId = /* @__PURE__ */ new Map(), recordOrder = baseRecordOrder(table);
1371
1463
  for (let row = 0; row < recordOrder.length; row++)
1372
1464
  if (table.records[recordOrder[row]])
1373
1465
  for (let column = 0; column < fieldOrder.length; column++) {
1374
1466
  let field = table.fields[fieldOrder[column]];
1375
1467
  if (!field || field.type !== "formula") continue;
1376
1468
  let formula = String(field.config?.formula ?? "").trim();
1377
- formula && ((tableFormulaData[row] ??= {})[column] = {
1378
- f: normalizeBaseFormulaForEngine(formula, table, snapshot),
1469
+ if (!formula) continue;
1470
+ let normalizedFormula = normalizedFormulaByFieldId.get(field.id);
1471
+ normalizedFormula == null && (normalizedFormula = normalizeBaseFormulaForEngine(
1472
+ formula,
1473
+ table,
1474
+ formulaTableNames,
1475
+ normalizeFormulaTableReferences
1476
+ ), normalizedFormulaByFieldId.set(field.id, normalizedFormula)), (tableFormulaData[row] ??= {})[column] = {
1477
+ f: normalizedFormula,
1379
1478
  si: field.id
1380
- });
1479
+ };
1381
1480
  }
1382
1481
  formulaData[unitId][table.id] = tableFormulaData;
1383
1482
  }
@@ -1446,25 +1545,17 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1446
1545
  _createBaseTableResource(unitId) {
1447
1546
  let base = this._univerInstanceService.getUnit(unitId, UniverInstanceType.UNIVER_BASE);
1448
1547
  if (base) {
1449
- let snapshot = base.getSnapshot(), tables = Object.values(snapshot.tables), data2 = {};
1450
- for (let table of tables) {
1451
- let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), tableNames = /* @__PURE__ */ new Set([table.id, getEngineBaseTableName(table, snapshot)]);
1452
- isUniqueBaseTableName(table, snapshot) && tableNames.add(table.name);
1453
- for (let name2 of tableNames)
1548
+ let snapshot = base.getSnapshot(), tables = Object.values(snapshot.tables), formulaTableNames = createBaseFormulaTableNameMap(snapshot), data2 = {};
1549
+ for (let table of tables)
1550
+ for (let [name2, reference] of this._getBaseTableEntries(table.id, snapshot, formulaTableNames)) {
1551
+ let columns = Array.from(reference.titleMap.entries()).toSorted((left, right) => left[1] - right[1]).map(([displayName]) => ({ displayName }));
1454
1552
  (data2[table.id] ??= { tables: [] }).tables.push({
1455
1553
  name: name2,
1456
- range: {
1457
- startRow: 0,
1458
- endRow: Math.max(0, recordCount - 1),
1459
- startColumn: 0,
1460
- endColumn: Math.max(0, fieldOrder.length - 1)
1461
- },
1462
- columns: fieldOrder.map((fieldId) => ({
1463
- displayName: table.fields[fieldId]?.name ?? fieldId
1464
- })),
1465
- options: { showHeader: !1 }
1554
+ range: reference.range,
1555
+ columns,
1556
+ options: { showHeader: reference.showHeader !== !1 }
1466
1557
  });
1467
- }
1558
+ }
1468
1559
  return [
1469
1560
  {
1470
1561
  name: "SHEET_TABLE_PLUGIN",
@@ -1491,6 +1582,22 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1491
1582
  }
1492
1583
  ];
1493
1584
  }
1585
+ _getBaseTableEntries(tableId, snapshot, formulaTableNames) {
1586
+ let table = snapshot.tables[tableId];
1587
+ if (!table) return [];
1588
+ let recordCount = baseRecordOrder(table).length, fieldOrder = table.fieldOrder, reference = {
1589
+ sheetId: table.id,
1590
+ range: {
1591
+ startRow: 0,
1592
+ endRow: Math.max(0, recordCount - 1),
1593
+ startColumn: 0,
1594
+ endColumn: Math.max(0, fieldOrder.length - 1)
1595
+ },
1596
+ titleMap: new Map(fieldOrder.map((fieldId, index) => [table.fields[fieldId]?.name ?? fieldId, index])),
1597
+ showHeader: !1
1598
+ };
1599
+ return [[formulaTableNames.get(table.id), reference]];
1600
+ }
1494
1601
  _rawCellData(cellData) {
1495
1602
  let raw = cellData.getMatrix(), result = {};
1496
1603
  for (let [row, values] of Object.entries(raw ?? {})) {
@@ -1501,16 +1608,58 @@ var RustSameRuntimeProjectionController = class extends Disposable2 {
1501
1608
  }
1502
1609
  return result;
1503
1610
  }
1504
- }, BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_REFERENCE_PREFIX = /\b[A-Z_][\w.]*\[/gi, BASE_SCOPED_FIELD_REF_PATTERN = /(^|[^\w[])\[\[\s*#(This Row|Data)\s*\],\s*\[([^\]]+)\]\]/gi, BASE_CURRENT_ROW_FIELD_REF_PATTERN = /(^|[^\w[])\[@\[([^\]]+)\]\](?!\])/g, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1505
- function normalizeBaseFormulaForEngine(formula, currentTable, snapshot) {
1506
- let references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1507
- return protectBaseTableReferences(protectBaseExternalReferences(formula, hold), hold).replace(
1611
+ };
1612
+ function createBaseProjectionStructure(snapshot) {
1613
+ let formulaTableNames = createBaseFormulaTableNameMap(snapshot);
1614
+ return {
1615
+ unitName: snapshot.name,
1616
+ tables: Object.fromEntries(
1617
+ Object.values(snapshot.tables).map((table) => [
1618
+ table.id,
1619
+ {
1620
+ formulaName: formulaTableNames.get(table.id),
1621
+ fieldOrder: [...table.fieldOrder],
1622
+ recordOrder: baseRecordOrder(table),
1623
+ fieldSignatures: Object.fromEntries(
1624
+ Object.entries(table.fields).map(([fieldId, field]) => [fieldId, JSON.stringify(field)])
1625
+ )
1626
+ }
1627
+ ])
1628
+ )
1629
+ };
1630
+ }
1631
+ function isSameBaseProjectionStructure(left, right) {
1632
+ return JSON.stringify(left) === JSON.stringify(right);
1633
+ }
1634
+ function findSingleBaseFieldInsertion(previous, snapshot) {
1635
+ let current = createBaseProjectionStructure(snapshot), previousTableIds = Object.keys(previous.tables).toSorted(), currentTableIds = Object.keys(current.tables).toSorted();
1636
+ if (JSON.stringify(previousTableIds) !== JSON.stringify(currentTableIds)) return null;
1637
+ let insertion = null;
1638
+ for (let tableId of currentTableIds) {
1639
+ let before = previous.tables[tableId], after = current.tables[tableId];
1640
+ if (!before || !after || before.formulaName !== after.formulaName || JSON.stringify(before.recordOrder) !== JSON.stringify(after.recordOrder)) return null;
1641
+ for (let fieldId of before.fieldOrder)
1642
+ if (before.fieldSignatures[fieldId] !== after.fieldSignatures[fieldId]) return null;
1643
+ if (JSON.stringify(before.fieldOrder) === JSON.stringify(after.fieldOrder)) continue;
1644
+ if (insertion || after.fieldOrder.length !== before.fieldOrder.length + 1) return null;
1645
+ let column = after.fieldOrder.findIndex((fieldId, index) => fieldId !== before.fieldOrder[index]);
1646
+ if (column < 0) return null;
1647
+ let withoutInserted = after.fieldOrder.filter((_, index) => index !== column);
1648
+ if (JSON.stringify(withoutInserted) !== JSON.stringify(before.fieldOrder)) return null;
1649
+ insertion = { tableId, column };
1650
+ }
1651
+ return insertion;
1652
+ }
1653
+ var BASE_LEGACY_FIELD_REF_PATTERN = /\{([^}]+)\}/g, BASE_TABLE_REFERENCE_PREFIX = /\b[A-Z_][\w.]*\[/gi, BASE_SCOPED_FIELD_REF_PATTERN = /(^|[^\w[])\[\[\s*#(This Row|Data)\s*\],\s*\[([^\]]+)\]\]/gi, BASE_CURRENT_ROW_FIELD_REF_PATTERN = /(^|[^\w[])\[@\[([^\]]+)\]\](?!\])/g, BASE_BRACKET_FIELD_REF_PATTERN = /(^|[^A-Za-z0-9_[\]])\[([^\]]+)\]/g, BASE_EXTERNAL_A1_REF_PATTERN = /(?:'\[[^\]]+\](?:[^']|'')+'|\[[^\]]+\][^\s'!]+)!\$?[A-Z]{1,3}\$?\d+(?::\$?[A-Z]{1,3}\$?\d+)?/gi, BASE_EXTERNAL_STRUCTURED_REFERENCE_PREFIX = /(?:'((?:[^']|'')+)'|\[[^\]]+\]|[A-Za-z0-9_.-]+)![^\s![\]]+\[/g;
1654
+ function normalizeBaseFormulaForEngine(formula, currentTable, formulaTableNames, normalizeFormulaTableReferences) {
1655
+ let canonicalFormula = normalizeFormulaTableReferences(formula), references = [], hold = (reference) => `__BASE_FORMULA_REF_${references.push(reference) - 1}__`;
1656
+ return protectBaseTableReferences(protectBaseExternalReferences(canonicalFormula, hold), hold).replace(
1508
1657
  BASE_LEGACY_FIELD_REF_PATTERN,
1509
- (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, snapshot))
1658
+ (_match, fieldName) => hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))
1510
1659
  ).replace(BASE_SCOPED_FIELD_REF_PATTERN, (_match, prefix, scope, fieldName) => {
1511
- let tableName = getEngineBaseTableName(currentTable, snapshot);
1660
+ let tableName = formulaTableNames.get(currentTable.id) ?? currentTable.name;
1512
1661
  return `${prefix}${hold(`${tableName}[[#${scope}],[${fieldName}]]`)}`;
1513
- }).replace(BASE_CURRENT_ROW_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, snapshot))}`).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, snapshot))}`).replace(
1662
+ }).replace(BASE_CURRENT_ROW_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(BASE_BRACKET_FIELD_REF_PATTERN, (_match, prefix, fieldName) => `${prefix}${hold(createEngineThisRowRef(currentTable, fieldName, formulaTableNames))}`).replace(
1514
1663
  /__BASE_FORMULA_REF_(\d+)__/g,
1515
1664
  (_match, index) => references[Number(index)] ?? ""
1516
1665
  );
@@ -1559,37 +1708,21 @@ function isInsideBaseFormulaString(formula, position) {
1559
1708
  }
1560
1709
  return inString;
1561
1710
  }
1562
- function createEngineThisRowRef(table, fieldName, snapshot) {
1563
- return `${getEngineBaseTableName(table, snapshot)}[[#This Row],[${fieldName}]]`;
1564
- }
1565
- function getEngineBaseTableName(table, snapshot) {
1566
- return isUniqueBaseTableName(table, snapshot) && isExcelTableName(table.name) ? table.name : createStableExcelTableName(table.id);
1567
- }
1568
- function isUniqueBaseTableName(table, snapshot) {
1569
- let normalizedName = table.name.trim().toLowerCase();
1570
- return Object.values(snapshot.tables).filter((candidate) => candidate.name.trim().toLowerCase() === normalizedName).length === 1;
1571
- }
1572
- function isExcelTableName(name2) {
1573
- return name2.length === 0 || name2.length > 255 || !/^[A-Za-z_][A-Za-z0-9_.]*$/.test(name2) ? !1 : !/^[RC]$/i.test(name2) && !/^[A-Za-z]{1,3}[1-9]\d*$/.test(name2) && !/^R(?:\d+)?C(?:\d+)?$/i.test(name2);
1574
- }
1575
- function createStableExcelTableName(tableId) {
1576
- return `_T_${Array.from(
1577
- tableId,
1578
- (character) => /[A-Za-z0-9]/.test(character) ? character : `_x${character.codePointAt(0)?.toString(16) ?? "0"}_`
1579
- ).join("")}`;
1711
+ function createEngineThisRowRef(table, fieldName, formulaTableNames) {
1712
+ return `${formulaTableNames.get(table.id) ?? table.name}[[#This Row],[${fieldName}]]`;
1580
1713
  }
1581
1714
  function baseRecordOrder(table) {
1582
1715
  return table.recordOrder ? table.recordOrder.filter((recordId) => table.records[recordId] != null) : Object.values(table.records ?? {}).toSorted((left, right) => left.orderKey.localeCompare(right.orderKey)).map((record) => record.id);
1583
1716
  }
1584
1717
  function buildBaseRuntimeCellData(table) {
1585
- let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder.filter((fieldId) => table.fields[fieldId] != null), recordOrder = baseRecordOrder(table);
1718
+ let cellData = { ...table.cellData }, fieldOrder = table.fieldOrder, recordOrder = baseRecordOrder(table);
1586
1719
  for (let row = 0; row < recordOrder.length; row++) {
1587
1720
  let record = table.records[recordOrder[row]];
1588
1721
  if (record) {
1589
1722
  cellData[row] = { ...cellData[row] };
1590
1723
  for (let column = 0; column < fieldOrder.length; column++) {
1591
1724
  let fieldId = fieldOrder[column];
1592
- Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1725
+ table.fields[fieldId] && Object.prototype.hasOwnProperty.call(record.values ?? {}, fieldId) && (cellData[row][column] = toBaseRuntimeCellData(record.values[fieldId], table.fields[fieldId]));
1593
1726
  }
1594
1727
  }
1595
1728
  }
@@ -587,12 +587,14 @@ declare class RustEngineSyncController extends Disposable {
587
587
  private _nativeReady;
588
588
  private _snapshotLoaded;
589
589
  private _projectionLoadPromise;
590
+ private _projectionDeltaBarrier;
590
591
  private readonly _pendingMutations;
591
592
  private readonly _projectionManagedTableUnits;
592
593
  constructor(_commandService: ICommandService);
593
594
  private _initialize;
594
595
  loadProjection(workbooks: readonly IRustProjectionWorkbookInput[]): Promise<void>;
595
596
  waitForProjectionLoad(): Promise<void>;
597
+ setProjectionDeltaBarrier(barrier: Promise<void>): void;
596
598
  private _loadProjection;
597
599
  applyProjectionMutation(mutation: IRustEngineMutation): void;
598
600
  upsertProjectionWorkbook(workbook: IRustProjectionWorkbookInput): void;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "author": "DreamNum Co., Ltd. <developer@univer.ai>",
3
3
  "dependencies": {
4
- "@univerjs-pro/engine-formula": "1.0.0-insiders.20260809-70eefdd",
5
- "@univerjs-pro/engine-formula-rust-binding": "1.0.0-insiders.20260805-b15e7f3",
6
- "@univerjs/core": "1.0.0-insiders.20260809-70eefdd",
7
- "@univerjs/engine-formula": "1.0.0-insiders.20260809-70eefdd",
8
- "@univerjs/protocol": "1.0.0-insiders.20260809-70eefdd"
4
+ "@univerjs-pro/engine-formula": "1.0.0-rc.0",
5
+ "@univerjs-pro/engine-formula-rust-binding": "1.0.0-insiders.20260910-22fe9c7",
6
+ "@univerjs/core": "1.0.0-rc.0",
7
+ "@univerjs/engine-formula": "1.0.0-rc.0",
8
+ "@univerjs/protocol": "1.0.0-rc.0"
9
9
  },
10
10
  "description": "Rust-backed formula engine plugin for Univer",
11
11
  "files": [
@@ -20,7 +20,7 @@
20
20
  "url": "https://github.com/dream-num/univer-rs"
21
21
  },
22
22
  "type": "module",
23
- "version": "1.0.0-insiders.20260809-70eefdd",
23
+ "version": "1.0.0-rc.0",
24
24
  "main": "./lib/cjs/index.cjs",
25
25
  "module": "./lib/es/index.js",
26
26
  "types": "./lib/types/index.d.ts",