@bilig/headless 0.1.102 → 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/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/work-paper-runtime.d.ts +1 -1
- package/dist/work-paper-runtime.js +171 -75
- package/dist/work-paper-runtime.js.map +1 -1
- package/package.json +4 -4
|
@@ -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();
|
|
@@ -1884,6 +1885,12 @@ export class WorkPaper {
|
|
|
1884
1885
|
if (!this.isItPossibleToAddRows(sheetId, ...indexes)) {
|
|
1885
1886
|
throw new WorkPaperOperationError('Rows cannot be added');
|
|
1886
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
|
+
}
|
|
1887
1894
|
return this.batchStructuralChanges(() => {
|
|
1888
1895
|
indexes.forEach(([start, amount]) => {
|
|
1889
1896
|
this.engine.insertRows(this.sheetName(sheetId), start, amount);
|
|
@@ -1895,6 +1902,12 @@ export class WorkPaper {
|
|
|
1895
1902
|
if (!this.isItPossibleToRemoveRows(sheetId, ...indexes)) {
|
|
1896
1903
|
throw new WorkPaperOperationError('Rows cannot be removed');
|
|
1897
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
|
+
}
|
|
1898
1911
|
return this.batchStructuralChanges(() => {
|
|
1899
1912
|
indexes
|
|
1900
1913
|
.toSorted((left, right) => right[0] - left[0])
|
|
@@ -1908,6 +1921,12 @@ export class WorkPaper {
|
|
|
1908
1921
|
if (!this.isItPossibleToAddColumns(sheetId, ...indexes)) {
|
|
1909
1922
|
throw new WorkPaperOperationError('Columns cannot be added');
|
|
1910
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
|
+
}
|
|
1911
1930
|
return this.batchStructuralChanges(() => {
|
|
1912
1931
|
indexes.forEach(([start, amount]) => {
|
|
1913
1932
|
this.engine.insertColumns(this.sheetName(sheetId), start, amount);
|
|
@@ -1919,6 +1938,12 @@ export class WorkPaper {
|
|
|
1919
1938
|
if (!this.isItPossibleToRemoveColumns(sheetId, ...indexes)) {
|
|
1920
1939
|
throw new WorkPaperOperationError('Columns cannot be removed');
|
|
1921
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
|
+
}
|
|
1922
1947
|
return this.batchStructuralChanges(() => {
|
|
1923
1948
|
indexes
|
|
1924
1949
|
.toSorted((left, right) => right[0] - left[0])
|
|
@@ -1946,7 +1971,7 @@ export class WorkPaper {
|
|
|
1946
1971
|
throw new WorkPaperOperationError('Rows cannot be moved');
|
|
1947
1972
|
}
|
|
1948
1973
|
return this.canUseTrackedStructuralFastPath()
|
|
1949
|
-
? this.
|
|
1974
|
+
? this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1950
1975
|
this.engine.moveRows(this.sheetName(sheetId), start, count, target);
|
|
1951
1976
|
})
|
|
1952
1977
|
: this.captureChanges(undefined, () => {
|
|
@@ -1958,7 +1983,7 @@ export class WorkPaper {
|
|
|
1958
1983
|
throw new WorkPaperOperationError('Columns cannot be moved');
|
|
1959
1984
|
}
|
|
1960
1985
|
return this.canUseTrackedStructuralFastPath()
|
|
1961
|
-
? this.
|
|
1986
|
+
? this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1962
1987
|
this.engine.moveColumns(this.sheetName(sheetId), start, count, target);
|
|
1963
1988
|
})
|
|
1964
1989
|
: this.captureChanges(undefined, () => {
|
|
@@ -2500,44 +2525,85 @@ export class WorkPaper {
|
|
|
2500
2525
|
});
|
|
2501
2526
|
return orderWorkPaperCellChanges(cellChanges, this.listSheetRecords());
|
|
2502
2527
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
if (sheetId === undefined || row === undefined || col === undefined) {
|
|
2508
|
-
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 };
|
|
2509
2532
|
}
|
|
2510
|
-
const
|
|
2511
|
-
|
|
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) {
|
|
2512
2546
|
return undefined;
|
|
2513
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
|
+
}
|
|
2514
2584
|
return {
|
|
2515
2585
|
kind: 'cell',
|
|
2516
2586
|
address: { sheet: sheetId, row, col },
|
|
2517
2587
|
sheetName,
|
|
2518
2588
|
a1: formatAddress(row, col),
|
|
2519
|
-
newValue
|
|
2589
|
+
newValue,
|
|
2520
2590
|
};
|
|
2521
2591
|
}
|
|
2522
|
-
|
|
2523
|
-
if (event.patches && event.patches.length > 0) {
|
|
2524
|
-
const cellPatches = event.patches.filter((patch) => patch.kind === 'cell');
|
|
2525
|
-
return cellPatches;
|
|
2526
|
-
}
|
|
2527
|
-
const changes = [];
|
|
2528
|
-
for (let index = 0; index < event.changedCellIndices.length; index += 1) {
|
|
2529
|
-
const change = this.readTrackedCellChange(event.changedCellIndices[index]);
|
|
2530
|
-
if (change) {
|
|
2531
|
-
changes.push(change);
|
|
2532
|
-
}
|
|
2533
|
-
}
|
|
2534
|
-
return changes;
|
|
2535
|
-
}
|
|
2536
|
-
computeCellChangesFromTrackedEvents(beforeVisibility, events) {
|
|
2592
|
+
computeCellChangesFromTrackedEvents(beforeVisibility, events, updateVisibility = true) {
|
|
2537
2593
|
if (events.some((event) => event.invalidation === 'full')) {
|
|
2538
2594
|
return null;
|
|
2539
2595
|
}
|
|
2540
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
|
+
};
|
|
2541
2607
|
const ensureMutableSheet = (sheetId, sheetName) => {
|
|
2542
2608
|
const existing = nextVisibility.get(sheetId);
|
|
2543
2609
|
if (existing) {
|
|
@@ -2554,68 +2620,96 @@ export class WorkPaper {
|
|
|
2554
2620
|
};
|
|
2555
2621
|
if (events.length === 1) {
|
|
2556
2622
|
const event = events[0];
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
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);
|
|
2638
|
+
}
|
|
2639
|
+
else {
|
|
2640
|
+
sheet.cells.set(cellKey, change.newValue);
|
|
2568
2641
|
}
|
|
2569
2642
|
}
|
|
2570
|
-
|
|
2571
|
-
break;
|
|
2572
|
-
}
|
|
2643
|
+
return { changes: [change], nextVisibility };
|
|
2573
2644
|
}
|
|
2574
2645
|
}
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
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) {
|
|
2580
2666
|
if (seenCellKeys.has(cellKey)) {
|
|
2581
2667
|
hasDuplicateCellKey = true;
|
|
2582
2668
|
break;
|
|
2583
2669
|
}
|
|
2584
2670
|
seenCellKeys.add(cellKey);
|
|
2585
2671
|
}
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
let previousCol = -1;
|
|
2593
|
-
for (let index = 0; index < eventChanges.length; index += 1) {
|
|
2594
|
-
const change = eventChanges[index];
|
|
2595
|
-
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
2596
|
-
const sheet = ensureMutableSheet(change.address.sheet, change.sheetName);
|
|
2597
|
-
if (sheet.order < previousSheetOrder ||
|
|
2598
|
-
(sheet.order === previousSheetOrder &&
|
|
2599
|
-
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
2600
|
-
alreadySorted = false;
|
|
2672
|
+
else if (smallCellKeys) {
|
|
2673
|
+
for (let priorIndex = 0; priorIndex < index; priorIndex += 1) {
|
|
2674
|
+
if (smallCellKeys[priorIndex] === cellKey) {
|
|
2675
|
+
hasDuplicateCellKey = true;
|
|
2676
|
+
break;
|
|
2677
|
+
}
|
|
2601
2678
|
}
|
|
2679
|
+
if (hasDuplicateCellKey) {
|
|
2680
|
+
break;
|
|
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) {
|
|
2602
2692
|
if (change.newValue.tag === ValueTag.Empty) {
|
|
2603
2693
|
sheet.cells.delete(cellKey);
|
|
2604
2694
|
}
|
|
2605
2695
|
else {
|
|
2606
2696
|
sheet.cells.set(cellKey, change.newValue);
|
|
2607
2697
|
}
|
|
2608
|
-
|
|
2698
|
+
}
|
|
2699
|
+
directChanges[index] = materializedEventChanges.canReusePublicChanges
|
|
2700
|
+
? change
|
|
2701
|
+
: {
|
|
2609
2702
|
kind: 'cell',
|
|
2610
2703
|
address: change.address,
|
|
2611
2704
|
sheetName: change.sheetName,
|
|
2612
2705
|
a1: change.a1,
|
|
2613
2706
|
newValue: change.newValue,
|
|
2614
2707
|
};
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2708
|
+
previousSheetOrder = sheetOrder;
|
|
2709
|
+
previousRow = change.address.row;
|
|
2710
|
+
previousCol = change.address.col;
|
|
2711
|
+
}
|
|
2712
|
+
if (!hasDuplicateCellKey) {
|
|
2619
2713
|
return {
|
|
2620
2714
|
changes: alreadySorted
|
|
2621
2715
|
? directChanges
|
|
@@ -2626,7 +2720,7 @@ export class WorkPaper {
|
|
|
2626
2720
|
}
|
|
2627
2721
|
const latestChangesByKey = new Map();
|
|
2628
2722
|
for (const event of events) {
|
|
2629
|
-
const eventChanges = this.materializeTrackedEventChanges(event);
|
|
2723
|
+
const eventChanges = this.materializeTrackedEventChanges(event).changes;
|
|
2630
2724
|
for (let index = 0; index < eventChanges.length; index += 1) {
|
|
2631
2725
|
const change = eventChanges[index];
|
|
2632
2726
|
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
@@ -2640,14 +2734,16 @@ export class WorkPaper {
|
|
|
2640
2734
|
});
|
|
2641
2735
|
}
|
|
2642
2736
|
}
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
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
|
+
}
|
|
2651
2747
|
}
|
|
2652
2748
|
}
|
|
2653
2749
|
const directChanges = [...latestChangesByKey.values()];
|
|
@@ -2691,7 +2787,7 @@ export class WorkPaper {
|
|
|
2691
2787
|
this.batchUsesTrackedFastPath = false;
|
|
2692
2788
|
}
|
|
2693
2789
|
computeTrackedChangesWithoutVisibilityCache(events) {
|
|
2694
|
-
const fastPath = this.computeCellChangesFromTrackedEvents(new Map(), events);
|
|
2790
|
+
const fastPath = this.computeCellChangesFromTrackedEvents(new Map(), events, false);
|
|
2695
2791
|
if (!fastPath) {
|
|
2696
2792
|
throw new WorkPaperOperationError('Mutation emitted an unsupported invalidation pattern for tracked changes');
|
|
2697
2793
|
}
|