@bilig/headless 0.1.101 → 0.2.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/dist/change-order.js +11 -0
- package/dist/change-order.js.map +1 -1
- package/dist/initial-sheet-load.js +3 -6
- package/dist/initial-sheet-load.js.map +1 -1
- package/dist/tracked-cell-index-changes.d.ts +12 -0
- package/dist/tracked-cell-index-changes.js +211 -0
- package/dist/tracked-cell-index-changes.js.map +1 -0
- package/dist/tracked-engine-event-refs.d.ts +40 -1
- package/dist/tracked-engine-event-refs.js +10 -3
- package/dist/tracked-engine-event-refs.js.map +1 -1
- package/dist/work-paper-runtime.d.ts +6 -2
- package/dist/work-paper-runtime.js +248 -77
- package/dist/work-paper-runtime.js.map +1 -1
- package/dist/work-paper-types.d.ts +2 -0
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SpreadsheetEngine, makeCellKey } from '@bilig/core';
|
|
1
|
+
import { SpreadsheetEngine, attachRuntimeSnapshot, makeCellKey, readRuntimeSnapshot, } from '@bilig/core';
|
|
2
2
|
import { ErrorCode, MAX_COLS, MAX_ROWS, ValueTag, } from '@bilig/protocol';
|
|
3
3
|
import { excelSerialToDateParts, formatAddress, formatRangeAddress, installExternalFunctionAdapter, isArrayValue, isCellReferenceText, parseCellAddress, parseFormula, parseRangeAddress, serializeFormula, translateFormulaReferences, } from '@bilig/formula';
|
|
4
4
|
import { loadInitialMixedSheet, tryLoadInitialLiteralSheet } from './initial-sheet-load.js';
|
|
@@ -6,6 +6,7 @@ import { orderWorkPaperCellChanges } from './change-order.js';
|
|
|
6
6
|
import { WorkPaperConfigValueTooBigError, WorkPaperConfigValueTooSmallError, WorkPaperEvaluationSuspendedError, WorkPaperExpectedValueOfTypeError, WorkPaperOperationError, WorkPaperParseError, WorkPaperSheetError, WorkPaperExpectedOneOfValuesError, WorkPaperFunctionPluginValidationError, WorkPaperInvalidArgumentsError, WorkPaperLanguageAlreadyRegisteredError, WorkPaperLanguageNotRegisteredError, WorkPaperNamedExpressionDoesNotExistError, WorkPaperNamedExpressionNameIsAlreadyTakenError, WorkPaperNamedExpressionNameIsInvalidError, WorkPaperNoOperationToRedoError, WorkPaperNoOperationToUndoError, WorkPaperNoRelativeAddressesAllowedError, WorkPaperNoSheetWithIdError, WorkPaperNoSheetWithNameError, WorkPaperNotAFormulaError, WorkPaperNothingToPasteError, WorkPaperSheetNameAlreadyTakenError, WorkPaperSheetSizeLimitExceededError, WorkPaperUnableToParseError, } from './work-paper-errors.js';
|
|
7
7
|
import { buildMatrixMutationPlan } from './matrix-mutation-plan.js';
|
|
8
8
|
import { captureTrackedEngineEvent } from './tracked-engine-event-refs.js';
|
|
9
|
+
import { materializeTrackedIndexChangesWithMetadata } from './tracked-cell-index-changes.js';
|
|
9
10
|
import { calculateWorkPaperFormulaInScratchWorkbook } from './work-paper-scratch-evaluator.js';
|
|
10
11
|
import { replaceWorkPaperSheetContent } from './work-paper-sheet-replacement.js';
|
|
11
12
|
const EMPTY_NAMED_EXPRESSION_VALUES = new Map();
|
|
@@ -810,10 +811,18 @@ export class WorkPaper {
|
|
|
810
811
|
}
|
|
811
812
|
static buildFromSheets(sheets, configInput = {}, namedExpressions = []) {
|
|
812
813
|
const workbook = new WorkPaper(configInput);
|
|
814
|
+
const runtimeSnapshot = namedExpressions.length === 0 ? readRuntimeSnapshot(sheets) : undefined;
|
|
815
|
+
const runtimeSnapshotMatchesSheets = runtimeSnapshot !== undefined &&
|
|
816
|
+
runtimeSnapshot.sheets.length === Object.keys(sheets).length &&
|
|
817
|
+
runtimeSnapshot.sheets.every((sheet) => Object.prototype.hasOwnProperty.call(sheets, sheet.name));
|
|
813
818
|
Object.entries(sheets).forEach(([sheetName, sheet]) => {
|
|
814
819
|
validateSheetWithinLimits(sheetName, sheet, workbook.config);
|
|
815
820
|
});
|
|
816
821
|
workbook.withEngineEventCaptureDisabled(() => {
|
|
822
|
+
if (runtimeSnapshot && runtimeSnapshotMatchesSheets) {
|
|
823
|
+
workbook.engine.importSnapshot(runtimeSnapshot);
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
817
826
|
Object.keys(sheets).forEach((sheetName) => {
|
|
818
827
|
workbook.engine.createSheet(sheetName);
|
|
819
828
|
});
|
|
@@ -1023,6 +1032,16 @@ export class WorkPaper {
|
|
|
1023
1032
|
lastMetrics: structuredClone(this.engine.getLastMetrics()),
|
|
1024
1033
|
};
|
|
1025
1034
|
}
|
|
1035
|
+
getPerformanceCounters() {
|
|
1036
|
+
this.assertNotDisposed();
|
|
1037
|
+
const counterAwareEngine = this.engine;
|
|
1038
|
+
return structuredClone(counterAwareEngine.getPerformanceCounters());
|
|
1039
|
+
}
|
|
1040
|
+
resetPerformanceCounters() {
|
|
1041
|
+
this.assertNotDisposed();
|
|
1042
|
+
const counterAwareEngine = this.engine;
|
|
1043
|
+
counterAwareEngine.resetPerformanceCounters();
|
|
1044
|
+
}
|
|
1026
1045
|
rebuildAndRecalculate() {
|
|
1027
1046
|
this.assertNotDisposed();
|
|
1028
1047
|
if (this.shouldSuppressEvents()) {
|
|
@@ -1312,7 +1331,9 @@ export class WorkPaper {
|
|
|
1312
1331
|
return Object.fromEntries(this.listSheetRecords().map((sheet) => [sheet.name, this.getSheetFormulas(sheet.id)]));
|
|
1313
1332
|
}
|
|
1314
1333
|
getAllSheetsSerialized() {
|
|
1315
|
-
|
|
1334
|
+
const serialized = Object.fromEntries(this.listSheetRecords().map((sheet) => [sheet.name, this.getSheetSerialized(sheet.id)]));
|
|
1335
|
+
attachRuntimeSnapshot(serialized, this.engine.exportSnapshot());
|
|
1336
|
+
return serialized;
|
|
1316
1337
|
}
|
|
1317
1338
|
getAllSheetsDimensions() {
|
|
1318
1339
|
return Object.fromEntries(this.listSheetRecords().map((sheet) => [sheet.name, this.getSheetDimensions(sheet.id)]));
|
|
@@ -1864,6 +1885,12 @@ export class WorkPaper {
|
|
|
1864
1885
|
if (!this.isItPossibleToAddRows(sheetId, ...indexes)) {
|
|
1865
1886
|
throw new WorkPaperOperationError('Rows cannot be added');
|
|
1866
1887
|
}
|
|
1888
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
1889
|
+
const [start, amount] = indexes[0];
|
|
1890
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1891
|
+
this.engine.insertRows(this.sheetName(sheetId), start, amount);
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1867
1894
|
return this.batchStructuralChanges(() => {
|
|
1868
1895
|
indexes.forEach(([start, amount]) => {
|
|
1869
1896
|
this.engine.insertRows(this.sheetName(sheetId), start, amount);
|
|
@@ -1875,6 +1902,12 @@ export class WorkPaper {
|
|
|
1875
1902
|
if (!this.isItPossibleToRemoveRows(sheetId, ...indexes)) {
|
|
1876
1903
|
throw new WorkPaperOperationError('Rows cannot be removed');
|
|
1877
1904
|
}
|
|
1905
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
1906
|
+
const [start, amount] = indexes[0];
|
|
1907
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1908
|
+
this.engine.deleteRows(this.sheetName(sheetId), start, amount);
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1878
1911
|
return this.batchStructuralChanges(() => {
|
|
1879
1912
|
indexes
|
|
1880
1913
|
.toSorted((left, right) => right[0] - left[0])
|
|
@@ -1888,6 +1921,12 @@ export class WorkPaper {
|
|
|
1888
1921
|
if (!this.isItPossibleToAddColumns(sheetId, ...indexes)) {
|
|
1889
1922
|
throw new WorkPaperOperationError('Columns cannot be added');
|
|
1890
1923
|
}
|
|
1924
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
1925
|
+
const [start, amount] = indexes[0];
|
|
1926
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1927
|
+
this.engine.insertColumns(this.sheetName(sheetId), start, amount);
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1891
1930
|
return this.batchStructuralChanges(() => {
|
|
1892
1931
|
indexes.forEach(([start, amount]) => {
|
|
1893
1932
|
this.engine.insertColumns(this.sheetName(sheetId), start, amount);
|
|
@@ -1899,6 +1938,12 @@ export class WorkPaper {
|
|
|
1899
1938
|
if (!this.isItPossibleToRemoveColumns(sheetId, ...indexes)) {
|
|
1900
1939
|
throw new WorkPaperOperationError('Columns cannot be removed');
|
|
1901
1940
|
}
|
|
1941
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
1942
|
+
const [start, amount] = indexes[0];
|
|
1943
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1944
|
+
this.engine.deleteColumns(this.sheetName(sheetId), start, amount);
|
|
1945
|
+
});
|
|
1946
|
+
}
|
|
1902
1947
|
return this.batchStructuralChanges(() => {
|
|
1903
1948
|
indexes
|
|
1904
1949
|
.toSorted((left, right) => right[0] - left[0])
|
|
@@ -1926,7 +1971,7 @@ export class WorkPaper {
|
|
|
1926
1971
|
throw new WorkPaperOperationError('Rows cannot be moved');
|
|
1927
1972
|
}
|
|
1928
1973
|
return this.canUseTrackedStructuralFastPath()
|
|
1929
|
-
? this.
|
|
1974
|
+
? this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1930
1975
|
this.engine.moveRows(this.sheetName(sheetId), start, count, target);
|
|
1931
1976
|
})
|
|
1932
1977
|
: this.captureChanges(undefined, () => {
|
|
@@ -1938,7 +1983,7 @@ export class WorkPaper {
|
|
|
1938
1983
|
throw new WorkPaperOperationError('Columns cannot be moved');
|
|
1939
1984
|
}
|
|
1940
1985
|
return this.canUseTrackedStructuralFastPath()
|
|
1941
|
-
? this.
|
|
1986
|
+
? this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1942
1987
|
this.engine.moveColumns(this.sheetName(sheetId), start, count, target);
|
|
1943
1988
|
})
|
|
1944
1989
|
: this.captureChanges(undefined, () => {
|
|
@@ -2480,30 +2525,85 @@ export class WorkPaper {
|
|
|
2480
2525
|
});
|
|
2481
2526
|
return orderWorkPaperCellChanges(cellChanges, this.listSheetRecords());
|
|
2482
2527
|
}
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
if (sheetId === undefined || row === undefined || col === undefined) {
|
|
2488
|
-
return undefined;
|
|
2528
|
+
materializeTrackedEventChanges(event) {
|
|
2529
|
+
if (event.patches && event.patches.length > 0) {
|
|
2530
|
+
const cellPatches = event.patches.filter((patch) => patch.kind === 'cell');
|
|
2531
|
+
return { changes: cellPatches, canReusePublicChanges: false, ordered: false };
|
|
2489
2532
|
}
|
|
2490
|
-
const
|
|
2491
|
-
|
|
2533
|
+
const materialized = materializeTrackedIndexChangesWithMetadata(this.engine, event.changedCellIndices, {
|
|
2534
|
+
explicitChangedCount: event.explicitChangedCount,
|
|
2535
|
+
});
|
|
2536
|
+
return {
|
|
2537
|
+
changes: materialized.changes,
|
|
2538
|
+
canReusePublicChanges: true,
|
|
2539
|
+
ordered: materialized.ordered,
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
readSingleTrackedCellChange(cellIndex) {
|
|
2543
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
2544
|
+
const sheetId = cellStore.sheetIds[cellIndex];
|
|
2545
|
+
if (sheetId === undefined) {
|
|
2492
2546
|
return undefined;
|
|
2493
2547
|
}
|
|
2548
|
+
const sheet = this.engine.workbook.getSheetById(sheetId);
|
|
2549
|
+
const sheetName = sheet?.name ?? this.engine.workbook.getSheetNameById(sheetId);
|
|
2550
|
+
let row;
|
|
2551
|
+
let col;
|
|
2552
|
+
if (!sheet || sheet.structureVersion === 1) {
|
|
2553
|
+
row = cellStore.rows[cellIndex];
|
|
2554
|
+
col = cellStore.cols[cellIndex];
|
|
2555
|
+
}
|
|
2556
|
+
else {
|
|
2557
|
+
const position = this.engine.workbook.getCellPosition(cellIndex);
|
|
2558
|
+
if (!position) {
|
|
2559
|
+
return undefined;
|
|
2560
|
+
}
|
|
2561
|
+
row = position.row;
|
|
2562
|
+
col = position.col;
|
|
2563
|
+
}
|
|
2564
|
+
const tag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
|
|
2565
|
+
let newValue;
|
|
2566
|
+
switch (tag) {
|
|
2567
|
+
case ValueTag.Number:
|
|
2568
|
+
newValue = { tag: ValueTag.Number, value: cellStore.numbers[cellIndex] ?? 0 };
|
|
2569
|
+
break;
|
|
2570
|
+
case ValueTag.Boolean:
|
|
2571
|
+
newValue = { tag: ValueTag.Boolean, value: (cellStore.numbers[cellIndex] ?? 0) !== 0 };
|
|
2572
|
+
break;
|
|
2573
|
+
case ValueTag.String:
|
|
2574
|
+
newValue = cellStore.getValue(cellIndex, (stringId) => this.engine.strings.get(stringId));
|
|
2575
|
+
break;
|
|
2576
|
+
case ValueTag.Error:
|
|
2577
|
+
newValue = { tag: ValueTag.Error, code: cellStore.errors[cellIndex] };
|
|
2578
|
+
break;
|
|
2579
|
+
case ValueTag.Empty:
|
|
2580
|
+
default:
|
|
2581
|
+
newValue = { tag: ValueTag.Empty };
|
|
2582
|
+
break;
|
|
2583
|
+
}
|
|
2494
2584
|
return {
|
|
2495
2585
|
kind: 'cell',
|
|
2496
2586
|
address: { sheet: sheetId, row, col },
|
|
2497
2587
|
sheetName,
|
|
2498
2588
|
a1: formatAddress(row, col),
|
|
2499
|
-
newValue
|
|
2589
|
+
newValue,
|
|
2500
2590
|
};
|
|
2501
2591
|
}
|
|
2502
|
-
computeCellChangesFromTrackedEvents(beforeVisibility, events) {
|
|
2592
|
+
computeCellChangesFromTrackedEvents(beforeVisibility, events, updateVisibility = true) {
|
|
2503
2593
|
if (events.some((event) => event.invalidation === 'full')) {
|
|
2504
2594
|
return null;
|
|
2505
2595
|
}
|
|
2506
2596
|
const nextVisibility = beforeVisibility;
|
|
2597
|
+
const sheetOrders = new Map();
|
|
2598
|
+
const sheetOrderFor = (sheetId) => {
|
|
2599
|
+
const existing = sheetOrders.get(sheetId);
|
|
2600
|
+
if (existing !== undefined) {
|
|
2601
|
+
return existing;
|
|
2602
|
+
}
|
|
2603
|
+
const order = this.sheetRecord(sheetId).order;
|
|
2604
|
+
sheetOrders.set(sheetId, order);
|
|
2605
|
+
return order;
|
|
2606
|
+
};
|
|
2507
2607
|
const ensureMutableSheet = (sheetId, sheetName) => {
|
|
2508
2608
|
const existing = nextVisibility.get(sheetId);
|
|
2509
2609
|
if (existing) {
|
|
@@ -2520,79 +2620,96 @@ export class WorkPaper {
|
|
|
2520
2620
|
};
|
|
2521
2621
|
if (events.length === 1) {
|
|
2522
2622
|
const event = events[0];
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2623
|
+
if (event.invalidation !== 'full' &&
|
|
2624
|
+
event.patches === undefined &&
|
|
2625
|
+
event.changedCellIndices.length === 1 &&
|
|
2626
|
+
event.explicitChangedCount === 1 &&
|
|
2627
|
+
event.changedInputCount === 1 &&
|
|
2628
|
+
!event.hasInvalidatedRanges &&
|
|
2629
|
+
!event.hasInvalidatedRows &&
|
|
2630
|
+
!event.hasInvalidatedColumns) {
|
|
2631
|
+
const change = this.readSingleTrackedCellChange(event.changedCellIndices[0]);
|
|
2632
|
+
if (change) {
|
|
2633
|
+
if (updateVisibility) {
|
|
2634
|
+
const sheet = ensureMutableSheet(change.address.sheet, change.sheetName);
|
|
2635
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
2636
|
+
if (change.newValue.tag === ValueTag.Empty) {
|
|
2637
|
+
sheet.cells.delete(cellKey);
|
|
2535
2638
|
}
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
break;
|
|
2639
|
+
else {
|
|
2640
|
+
sheet.cells.set(cellKey, change.newValue);
|
|
2539
2641
|
}
|
|
2540
2642
|
}
|
|
2541
|
-
|
|
2542
|
-
break;
|
|
2543
|
-
}
|
|
2643
|
+
return { changes: [change], nextVisibility };
|
|
2544
2644
|
}
|
|
2545
2645
|
}
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2646
|
+
const materializedEventChanges = this.materializeTrackedEventChanges(event);
|
|
2647
|
+
const eventChanges = materializedEventChanges.changes;
|
|
2648
|
+
if (!updateVisibility && materializedEventChanges.canReusePublicChanges && materializedEventChanges.ordered) {
|
|
2649
|
+
return {
|
|
2650
|
+
changes: [...eventChanges],
|
|
2651
|
+
nextVisibility,
|
|
2652
|
+
};
|
|
2653
|
+
}
|
|
2654
|
+
const directChanges = [];
|
|
2655
|
+
const seenCellKeys = eventChanges.length > 4 && eventChanges.length <= 64 ? new Set() : undefined;
|
|
2656
|
+
const smallCellKeys = eventChanges.length > 1 && eventChanges.length <= 4 ? [] : undefined;
|
|
2657
|
+
let hasDuplicateCellKey = false;
|
|
2658
|
+
let alreadySorted = true;
|
|
2659
|
+
let previousSheetOrder = -1;
|
|
2660
|
+
let previousRow = -1;
|
|
2661
|
+
let previousCol = -1;
|
|
2662
|
+
for (let index = 0; index < eventChanges.length; index += 1) {
|
|
2663
|
+
const change = eventChanges[index];
|
|
2664
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
2665
|
+
if (seenCellKeys) {
|
|
2554
2666
|
if (seenCellKeys.has(cellKey)) {
|
|
2555
2667
|
hasDuplicateCellKey = true;
|
|
2556
2668
|
break;
|
|
2557
2669
|
}
|
|
2558
2670
|
seenCellKeys.add(cellKey);
|
|
2559
2671
|
}
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
let previousCol = -1;
|
|
2567
|
-
for (let index = 0; index < event.changedCellIndices.length; index += 1) {
|
|
2568
|
-
const change = this.readTrackedCellChange(event.changedCellIndices[index]);
|
|
2569
|
-
if (!change) {
|
|
2570
|
-
continue;
|
|
2672
|
+
else if (smallCellKeys) {
|
|
2673
|
+
for (let priorIndex = 0; priorIndex < index; priorIndex += 1) {
|
|
2674
|
+
if (smallCellKeys[priorIndex] === cellKey) {
|
|
2675
|
+
hasDuplicateCellKey = true;
|
|
2676
|
+
break;
|
|
2677
|
+
}
|
|
2571
2678
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
if (sheet.order < previousSheetOrder ||
|
|
2575
|
-
(sheet.order === previousSheetOrder &&
|
|
2576
|
-
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
2577
|
-
alreadySorted = false;
|
|
2679
|
+
if (hasDuplicateCellKey) {
|
|
2680
|
+
break;
|
|
2578
2681
|
}
|
|
2682
|
+
smallCellKeys[index] = cellKey;
|
|
2683
|
+
}
|
|
2684
|
+
const sheet = updateVisibility ? ensureMutableSheet(change.address.sheet, change.sheetName) : undefined;
|
|
2685
|
+
const sheetOrder = sheet?.order ?? sheetOrderFor(change.address.sheet);
|
|
2686
|
+
if (sheetOrder < previousSheetOrder ||
|
|
2687
|
+
(sheetOrder === previousSheetOrder &&
|
|
2688
|
+
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
2689
|
+
alreadySorted = false;
|
|
2690
|
+
}
|
|
2691
|
+
if (sheet) {
|
|
2579
2692
|
if (change.newValue.tag === ValueTag.Empty) {
|
|
2580
2693
|
sheet.cells.delete(cellKey);
|
|
2581
2694
|
}
|
|
2582
2695
|
else {
|
|
2583
2696
|
sheet.cells.set(cellKey, change.newValue);
|
|
2584
2697
|
}
|
|
2585
|
-
|
|
2698
|
+
}
|
|
2699
|
+
directChanges[index] = materializedEventChanges.canReusePublicChanges
|
|
2700
|
+
? change
|
|
2701
|
+
: {
|
|
2586
2702
|
kind: 'cell',
|
|
2587
2703
|
address: change.address,
|
|
2588
2704
|
sheetName: change.sheetName,
|
|
2589
2705
|
a1: change.a1,
|
|
2590
2706
|
newValue: change.newValue,
|
|
2591
2707
|
};
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2708
|
+
previousSheetOrder = sheetOrder;
|
|
2709
|
+
previousRow = change.address.row;
|
|
2710
|
+
previousCol = change.address.col;
|
|
2711
|
+
}
|
|
2712
|
+
if (!hasDuplicateCellKey) {
|
|
2596
2713
|
return {
|
|
2597
2714
|
changes: alreadySorted
|
|
2598
2715
|
? directChanges
|
|
@@ -2603,11 +2720,9 @@ export class WorkPaper {
|
|
|
2603
2720
|
}
|
|
2604
2721
|
const latestChangesByKey = new Map();
|
|
2605
2722
|
for (const event of events) {
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
continue;
|
|
2610
|
-
}
|
|
2723
|
+
const eventChanges = this.materializeTrackedEventChanges(event).changes;
|
|
2724
|
+
for (let index = 0; index < eventChanges.length; index += 1) {
|
|
2725
|
+
const change = eventChanges[index];
|
|
2611
2726
|
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
2612
2727
|
latestChangesByKey.delete(cellKey);
|
|
2613
2728
|
latestChangesByKey.set(cellKey, {
|
|
@@ -2619,14 +2734,16 @@ export class WorkPaper {
|
|
|
2619
2734
|
});
|
|
2620
2735
|
}
|
|
2621
2736
|
}
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2737
|
+
if (updateVisibility) {
|
|
2738
|
+
for (const change of latestChangesByKey.values()) {
|
|
2739
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
2740
|
+
const sheet = ensureMutableSheet(change.address.sheet, change.sheetName);
|
|
2741
|
+
if (change.newValue.tag === ValueTag.Empty) {
|
|
2742
|
+
sheet.cells.delete(cellKey);
|
|
2743
|
+
}
|
|
2744
|
+
else {
|
|
2745
|
+
sheet.cells.set(cellKey, change.newValue);
|
|
2746
|
+
}
|
|
2630
2747
|
}
|
|
2631
2748
|
}
|
|
2632
2749
|
const directChanges = [...latestChangesByKey.values()];
|
|
@@ -2670,7 +2787,7 @@ export class WorkPaper {
|
|
|
2670
2787
|
this.batchUsesTrackedFastPath = false;
|
|
2671
2788
|
}
|
|
2672
2789
|
computeTrackedChangesWithoutVisibilityCache(events) {
|
|
2673
|
-
const fastPath = this.computeCellChangesFromTrackedEvents(new Map(), events);
|
|
2790
|
+
const fastPath = this.computeCellChangesFromTrackedEvents(new Map(), events, false);
|
|
2674
2791
|
if (!fastPath) {
|
|
2675
2792
|
throw new WorkPaperOperationError('Mutation emitted an unsupported invalidation pattern for tracked changes');
|
|
2676
2793
|
}
|
|
@@ -2828,15 +2945,69 @@ export class WorkPaper {
|
|
|
2828
2945
|
return record.ops;
|
|
2829
2946
|
case 'single-op':
|
|
2830
2947
|
return [record.op];
|
|
2948
|
+
case 'cell-mutations':
|
|
2949
|
+
return record.refs.flatMap((ref) => {
|
|
2950
|
+
const sheetName = this.getSheetName(ref.sheetId);
|
|
2951
|
+
if (!sheetName) {
|
|
2952
|
+
return [];
|
|
2953
|
+
}
|
|
2954
|
+
const address = formatAddress(ref.mutation.row, ref.mutation.col);
|
|
2955
|
+
switch (ref.mutation.kind) {
|
|
2956
|
+
case 'setCellValue':
|
|
2957
|
+
return [
|
|
2958
|
+
{
|
|
2959
|
+
kind: 'setCellValue',
|
|
2960
|
+
sheetName,
|
|
2961
|
+
address,
|
|
2962
|
+
value: ref.mutation.value,
|
|
2963
|
+
},
|
|
2964
|
+
];
|
|
2965
|
+
case 'setCellFormula':
|
|
2966
|
+
return [
|
|
2967
|
+
{
|
|
2968
|
+
kind: 'setCellFormula',
|
|
2969
|
+
sheetName,
|
|
2970
|
+
address,
|
|
2971
|
+
formula: ref.mutation.formula,
|
|
2972
|
+
},
|
|
2973
|
+
];
|
|
2974
|
+
case 'clearCell':
|
|
2975
|
+
return [
|
|
2976
|
+
{
|
|
2977
|
+
kind: 'clearCell',
|
|
2978
|
+
sheetName,
|
|
2979
|
+
address,
|
|
2980
|
+
},
|
|
2981
|
+
];
|
|
2982
|
+
}
|
|
2983
|
+
});
|
|
2831
2984
|
}
|
|
2832
2985
|
}
|
|
2986
|
+
tryMergeTypedCellMutationHistory(entries) {
|
|
2987
|
+
if (entries.length === 0 ||
|
|
2988
|
+
entries.some((entry) => entry.forward.kind !== 'cell-mutations' || entry.inverse.kind !== 'cell-mutations')) {
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2991
|
+
return {
|
|
2992
|
+
forward: {
|
|
2993
|
+
kind: 'cell-mutations',
|
|
2994
|
+
refs: entries.flatMap((entry) => (entry.forward.kind === 'cell-mutations' ? entry.forward.refs : [])),
|
|
2995
|
+
potentialNewCells: sumNumbers(entries.map((entry) => entry.forward.potentialNewCells)),
|
|
2996
|
+
},
|
|
2997
|
+
inverse: {
|
|
2998
|
+
kind: 'cell-mutations',
|
|
2999
|
+
refs: entries.toReversed().flatMap((entry) => (entry.inverse.kind === 'cell-mutations' ? entry.inverse.refs : [])),
|
|
3000
|
+
potentialNewCells: sumNumbers(entries.map((entry) => entry.inverse.potentialNewCells)),
|
|
3001
|
+
},
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
2833
3004
|
mergeUndoHistory(startIndex) {
|
|
2834
3005
|
const undoStack = this.getUndoStack();
|
|
2835
3006
|
if (undoStack.length - startIndex <= 1) {
|
|
2836
3007
|
return;
|
|
2837
3008
|
}
|
|
2838
3009
|
const entries = undoStack.splice(startIndex);
|
|
2839
|
-
const merged = {
|
|
3010
|
+
const merged = this.tryMergeTypedCellMutationHistory(entries) ?? {
|
|
2840
3011
|
forward: {
|
|
2841
3012
|
kind: 'ops',
|
|
2842
3013
|
ops: entries.flatMap((entry) => this.historyTransactionOps(entry.forward)),
|