@bilig/headless 0.1.102 → 0.3.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/fast-range-read.d.ts +4 -0
- package/dist/fast-range-read.js +81 -0
- package/dist/fast-range-read.js.map +1 -0
- package/dist/initial-sheet-load.d.ts +11 -5
- package/dist/initial-sheet-load.js +168 -65
- package/dist/initial-sheet-load.js.map +1 -1
- package/dist/tracked-cell-index-changes.d.ts +38 -0
- package/dist/tracked-cell-index-changes.js +842 -0
- package/dist/tracked-cell-index-changes.js.map +1 -0
- package/dist/tracked-engine-event-refs.d.ts +9 -1
- package/dist/tracked-engine-event-refs.js +152 -8
- package/dist/tracked-engine-event-refs.js.map +1 -1
- package/dist/work-paper-runtime.d.ts +30 -1
- package/dist/work-paper-runtime.js +1283 -147
- package/dist/work-paper-runtime.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,15 +1,68 @@
|
|
|
1
|
-
import { SpreadsheetEngine, attachRuntimeSnapshot, makeCellKey, readRuntimeSnapshot, } from '@bilig/core';
|
|
1
|
+
import { SpreadsheetEngine, attachRuntimeSnapshot, makeCellKey, readRuntimeImage, readRuntimeSnapshot, } from '@bilig/core';
|
|
2
2
|
import { ErrorCode, MAX_COLS, MAX_ROWS, ValueTag, } from '@bilig/protocol';
|
|
3
|
-
import { excelSerialToDateParts, formatAddress, formatRangeAddress, installExternalFunctionAdapter, isArrayValue, isCellReferenceText, parseCellAddress, parseFormula, parseRangeAddress, serializeFormula, translateFormulaReferences, } from '@bilig/formula';
|
|
4
|
-
import { loadInitialMixedSheet, tryLoadInitialLiteralSheet } from './initial-sheet-load.js';
|
|
3
|
+
import { excelSerialToDateParts, formatAddress, formatRangeAddress, indexToColumn, installExternalFunctionAdapter, isArrayValue, isCellReferenceText, parseCellAddress, parseFormula, parseRangeAddress, serializeFormula, translateFormulaReferences, } from '@bilig/formula';
|
|
4
|
+
import { loadInitialLiteralSheet, loadInitialMixedSheet, tryLoadInitialLiteralSheet } from './initial-sheet-load.js';
|
|
5
5
|
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
|
+
import { readFastPhysicalRangeValues } from './fast-range-read.js';
|
|
8
9
|
import { captureTrackedEngineEvent } from './tracked-engine-event-refs.js';
|
|
10
|
+
import { detachTrackedIndexChanges, hasDeferredTrackedIndexChanges, materializeTrackedIndexChangeSourcesWithMetadata, materializeTrackedIndexChangesWithMetadata, } from './tracked-cell-index-changes.js';
|
|
9
11
|
import { calculateWorkPaperFormulaInScratchWorkbook } from './work-paper-scratch-evaluator.js';
|
|
10
12
|
import { replaceWorkPaperSheetContent } from './work-paper-sheet-replacement.js';
|
|
13
|
+
const TRUSTED_TRACKED_PHYSICAL_SHEET_ID_PROPERTY = '__biligTrackedPhysicalSheetId';
|
|
14
|
+
const TRUSTED_TRACKED_PHYSICAL_SORTED_SPLIT_PROPERTY = '__biligTrackedPhysicalSortedSliceSplit';
|
|
15
|
+
function readTrustedPhysicalTrackedChangeMetadata(changedCellIndices) {
|
|
16
|
+
const trustedPhysicalSheetId = Reflect.get(changedCellIndices, TRUSTED_TRACKED_PHYSICAL_SHEET_ID_PROPERTY);
|
|
17
|
+
if (typeof trustedPhysicalSheetId !== 'number' || !Number.isInteger(trustedPhysicalSheetId) || trustedPhysicalSheetId < 0) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
const trustedSortedSliceSplit = Reflect.get(changedCellIndices, TRUSTED_TRACKED_PHYSICAL_SORTED_SPLIT_PROPERTY);
|
|
21
|
+
return typeof trustedSortedSliceSplit === 'number' && Number.isInteger(trustedSortedSliceSplit) && trustedSortedSliceSplit > 0
|
|
22
|
+
? { trustedPhysicalSheetId, trustedSortedSliceSplit }
|
|
23
|
+
: { trustedPhysicalSheetId };
|
|
24
|
+
}
|
|
11
25
|
const EMPTY_NAMED_EXPRESSION_VALUES = new Map();
|
|
26
|
+
const FAST_EXISTING_NUMERIC_LITERAL_FLAGS = 2 /* CellFlags.HasFormula */ | 4 /* CellFlags.JsOnly */ | 8 /* CellFlags.InCycle */ | 64 /* CellFlags.SpillChild */ | 128 /* CellFlags.PivotOutput */;
|
|
12
27
|
const VISIBILITY_SHEET_STRIDE = MAX_ROWS * MAX_COLS;
|
|
28
|
+
const TINY_TRACKED_CHANGE_LIMIT = 4;
|
|
29
|
+
const RUNTIME_COLUMN_LABEL_CACHE = [];
|
|
30
|
+
const RUNTIME_A1_CACHE_COLUMN_LIMIT = 64;
|
|
31
|
+
const RUNTIME_A1_CACHE_ROW_LIMIT = 4096;
|
|
32
|
+
const RUNTIME_A1_CACHE = [];
|
|
33
|
+
function countPotentialNewTrackedCellMutations(refs) {
|
|
34
|
+
let count = 0;
|
|
35
|
+
for (let index = 0; index < refs.length; index += 1) {
|
|
36
|
+
const ref = refs[index];
|
|
37
|
+
if (ref && ref.cellIndex === undefined && ref.mutation.kind !== 'clearCell') {
|
|
38
|
+
count += 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return count;
|
|
42
|
+
}
|
|
43
|
+
function canSkipDimensionUpdateAfterLiteralMutation(refs, potentialNewCells) {
|
|
44
|
+
if (potentialNewCells !== 0 || refs.length !== 1) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
const ref = refs[0];
|
|
48
|
+
return ref?.cellIndex !== undefined && ref.mutation.kind === 'setCellValue' && ref.mutation.value !== null;
|
|
49
|
+
}
|
|
50
|
+
function readTrackedRuntimeCellValue(cellStore, cellIndex, strings) {
|
|
51
|
+
const tag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
|
|
52
|
+
switch (tag) {
|
|
53
|
+
case ValueTag.Number:
|
|
54
|
+
return { tag: ValueTag.Number, value: cellStore.numbers[cellIndex] ?? 0 };
|
|
55
|
+
case ValueTag.Boolean:
|
|
56
|
+
return { tag: ValueTag.Boolean, value: (cellStore.numbers[cellIndex] ?? 0) !== 0 };
|
|
57
|
+
case ValueTag.String:
|
|
58
|
+
return cellStore.getValue(cellIndex, (stringId) => strings.get(stringId));
|
|
59
|
+
case ValueTag.Error:
|
|
60
|
+
return { tag: ValueTag.Error, code: cellStore.errors[cellIndex] };
|
|
61
|
+
case ValueTag.Empty:
|
|
62
|
+
default:
|
|
63
|
+
return { tag: ValueTag.Empty };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
13
66
|
const DEFAULT_CONFIG = Object.freeze({
|
|
14
67
|
accentSensitive: false,
|
|
15
68
|
caseSensitive: false,
|
|
@@ -281,9 +334,6 @@ function matrixContainsFormulaContent(content) {
|
|
|
281
334
|
function isDeferredBatchLiteralContent(content) {
|
|
282
335
|
return content === null || typeof content === 'boolean' || typeof content === 'number' || typeof content === 'string';
|
|
283
336
|
}
|
|
284
|
-
function canUseInitialMixedSheetFastPath(content) {
|
|
285
|
-
return content.some((row) => row.some((value) => typeof value === 'string' && value.trim().startsWith('=')));
|
|
286
|
-
}
|
|
287
337
|
function stripLeadingEquals(formula) {
|
|
288
338
|
return formula.trim().startsWith('=') ? formula.trim().slice(1) : formula.trim();
|
|
289
339
|
}
|
|
@@ -508,17 +558,123 @@ function validateWorkPaperConfig(config) {
|
|
|
508
558
|
}
|
|
509
559
|
}
|
|
510
560
|
}
|
|
511
|
-
function
|
|
561
|
+
function inspectSheetDimensionsWithinLimits(sheetName, sheet, config) {
|
|
512
562
|
const height = sheet.length;
|
|
513
|
-
|
|
563
|
+
let width = 0;
|
|
564
|
+
let materializedHeight = 0;
|
|
565
|
+
let materializedWidth = 0;
|
|
566
|
+
for (let rowIndex = 0; rowIndex < sheet.length; rowIndex += 1) {
|
|
567
|
+
const row = sheet[rowIndex];
|
|
568
|
+
if (!Array.isArray(row)) {
|
|
569
|
+
throw new WorkPaperUnableToParseError({ sheetName, reason: 'Rows must be arrays' });
|
|
570
|
+
}
|
|
571
|
+
width = Math.max(width, row.length);
|
|
572
|
+
for (let colIndex = 0; colIndex < row.length; colIndex += 1) {
|
|
573
|
+
if (row[colIndex] !== null) {
|
|
574
|
+
materializedHeight = Math.max(materializedHeight, rowIndex + 1);
|
|
575
|
+
materializedWidth = Math.max(materializedWidth, colIndex + 1);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
514
579
|
if (height > (config.maxRows ?? MAX_ROWS) || width > (config.maxColumns ?? MAX_COLS)) {
|
|
515
580
|
throw new WorkPaperSheetSizeLimitExceededError();
|
|
516
581
|
}
|
|
517
|
-
|
|
582
|
+
return { width: materializedWidth, height: materializedHeight };
|
|
583
|
+
}
|
|
584
|
+
function inspectRuntimeSnapshotSheetDimensionsWithinLimits(args) {
|
|
585
|
+
let materializedHeight = 0;
|
|
586
|
+
let materializedWidth = 0;
|
|
587
|
+
const dimensions = args.runtimeSheetCells?.dimensions;
|
|
588
|
+
if (dimensions &&
|
|
589
|
+
Number.isInteger(dimensions.width) &&
|
|
590
|
+
Number.isInteger(dimensions.height) &&
|
|
591
|
+
dimensions.width >= 0 &&
|
|
592
|
+
dimensions.height >= 0) {
|
|
593
|
+
materializedWidth = dimensions.width;
|
|
594
|
+
materializedHeight = dimensions.height;
|
|
595
|
+
}
|
|
596
|
+
else if (args.runtimeSheetCells?.coords) {
|
|
597
|
+
for (const coords of args.runtimeSheetCells.coords) {
|
|
598
|
+
materializedHeight = Math.max(materializedHeight, coords.row + 1);
|
|
599
|
+
materializedWidth = Math.max(materializedWidth, coords.col + 1);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
for (const cell of args.snapshotSheet.cells) {
|
|
604
|
+
const parsed = parseCellAddress(cell.address, args.sheetName);
|
|
605
|
+
materializedHeight = Math.max(materializedHeight, parsed.row + 1);
|
|
606
|
+
materializedWidth = Math.max(materializedWidth, parsed.col + 1);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (materializedHeight > (args.config.maxRows ?? MAX_ROWS) || materializedWidth > (args.config.maxColumns ?? MAX_COLS)) {
|
|
610
|
+
throw new WorkPaperSheetSizeLimitExceededError();
|
|
611
|
+
}
|
|
612
|
+
return { width: materializedWidth, height: materializedHeight };
|
|
613
|
+
}
|
|
614
|
+
function inspectSheetWithinLimits(sheetName, sheet, config) {
|
|
615
|
+
const height = sheet.length;
|
|
616
|
+
let width = 0;
|
|
617
|
+
let materializedHeight = 0;
|
|
618
|
+
let materializedWidth = 0;
|
|
619
|
+
let materializedCellCount = 0;
|
|
620
|
+
let formulaCellCount = 0;
|
|
621
|
+
let hasFormula = false;
|
|
622
|
+
for (let rowIndex = 0; rowIndex < sheet.length; rowIndex += 1) {
|
|
623
|
+
const row = sheet[rowIndex];
|
|
518
624
|
if (!Array.isArray(row)) {
|
|
519
625
|
throw new WorkPaperUnableToParseError({ sheetName, reason: 'Rows must be arrays' });
|
|
520
626
|
}
|
|
521
|
-
|
|
627
|
+
width = Math.max(width, row.length);
|
|
628
|
+
for (let colIndex = 0; colIndex < row.length; colIndex += 1) {
|
|
629
|
+
const cell = row[colIndex];
|
|
630
|
+
if (cell !== null) {
|
|
631
|
+
materializedCellCount += 1;
|
|
632
|
+
materializedHeight = Math.max(materializedHeight, rowIndex + 1);
|
|
633
|
+
materializedWidth = Math.max(materializedWidth, colIndex + 1);
|
|
634
|
+
}
|
|
635
|
+
if (typeof cell === 'string' && cellHasFormulaPrefix(cell)) {
|
|
636
|
+
formulaCellCount += 1;
|
|
637
|
+
hasFormula = true;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (height > (config.maxRows ?? MAX_ROWS) || width > (config.maxColumns ?? MAX_COLS)) {
|
|
642
|
+
throw new WorkPaperSheetSizeLimitExceededError();
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
hasFormula,
|
|
646
|
+
dimensions: { width: materializedWidth, height: materializedHeight },
|
|
647
|
+
materializedCellCount,
|
|
648
|
+
maxColumnCount: width,
|
|
649
|
+
formulaCellCount,
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
function cellHasFormulaPrefix(value) {
|
|
653
|
+
const first = value.charCodeAt(0);
|
|
654
|
+
if (first === 61) {
|
|
655
|
+
return true;
|
|
656
|
+
}
|
|
657
|
+
if (first !== 32 && first !== 9 && first !== 10 && first !== 13) {
|
|
658
|
+
return false;
|
|
659
|
+
}
|
|
660
|
+
return value.trimStart().charCodeAt(0) === 61;
|
|
661
|
+
}
|
|
662
|
+
function runtimeSnapshotMatchesSheetNames(sheets, runtimeSnapshot) {
|
|
663
|
+
const sheetNames = Object.keys(sheets);
|
|
664
|
+
if (runtimeSnapshot.sheets.length !== sheetNames.length) {
|
|
665
|
+
return false;
|
|
666
|
+
}
|
|
667
|
+
const matchedNames = new Set();
|
|
668
|
+
for (const snapshotSheet of runtimeSnapshot.sheets) {
|
|
669
|
+
if (!Object.prototype.hasOwnProperty.call(sheets, snapshotSheet.name) || matchedNames.has(snapshotSheet.name)) {
|
|
670
|
+
return false;
|
|
671
|
+
}
|
|
672
|
+
matchedNames.add(snapshotSheet.name);
|
|
673
|
+
}
|
|
674
|
+
return true;
|
|
675
|
+
}
|
|
676
|
+
function validateSheetWithinLimits(sheetName, sheet, config) {
|
|
677
|
+
inspectSheetWithinLimits(sheetName, sheet, config);
|
|
522
678
|
}
|
|
523
679
|
function functionPluginIds(config) {
|
|
524
680
|
return (config.functionPlugins ?? []).map((plugin) => plugin.id).toSorted();
|
|
@@ -612,6 +768,9 @@ class WorkPaperEmitter {
|
|
|
612
768
|
};
|
|
613
769
|
this.onDetailed(eventName, wrapper);
|
|
614
770
|
}
|
|
771
|
+
hasListeners(eventName) {
|
|
772
|
+
return this.listeners[eventName].size > 0 || this.detailedListeners[eventName].size > 0;
|
|
773
|
+
}
|
|
615
774
|
emitDetailed(event) {
|
|
616
775
|
this.dispatchDetailed(event);
|
|
617
776
|
}
|
|
@@ -708,6 +867,8 @@ export class WorkPaper {
|
|
|
708
867
|
visibilityCache = null;
|
|
709
868
|
namedExpressionValueCache = null;
|
|
710
869
|
sheetRecordsCache = null;
|
|
870
|
+
sheetDimensionsCache = new Map();
|
|
871
|
+
spillSheetIdsCache = null;
|
|
711
872
|
batchDepth = 0;
|
|
712
873
|
batchStartVisibility = null;
|
|
713
874
|
batchStartNamedValues = null;
|
|
@@ -723,9 +884,124 @@ export class WorkPaper {
|
|
|
723
884
|
suspendedCellMutationPotentialNewCells = 0;
|
|
724
885
|
queuedEvents = [];
|
|
725
886
|
trackedEngineEvents = [];
|
|
887
|
+
pendingLazyTrackedChanges = [];
|
|
726
888
|
engineEventCaptureEnabled = true;
|
|
889
|
+
retainedTrackedEngineEventIndicesDepth = 0;
|
|
727
890
|
unsubscribeEngineEvents = null;
|
|
728
891
|
disposed = false;
|
|
892
|
+
getVisibleCellIndex(sheetId, row, col) {
|
|
893
|
+
const sheet = this.engine.workbook.getSheetById(sheetId);
|
|
894
|
+
if (!sheet) {
|
|
895
|
+
return undefined;
|
|
896
|
+
}
|
|
897
|
+
return this.getVisibleCellIndexInSheet(sheet, row, col);
|
|
898
|
+
}
|
|
899
|
+
getVisibleCellIndexInSheet(sheet, row, col) {
|
|
900
|
+
if (sheet.structureVersion === 1) {
|
|
901
|
+
const cellIndex = sheet.grid.getPhysical(row, col);
|
|
902
|
+
return cellIndex === -1 ? undefined : cellIndex;
|
|
903
|
+
}
|
|
904
|
+
return sheet.logical.getVisibleCell(row, col);
|
|
905
|
+
}
|
|
906
|
+
cacheSheetDimensions(sheetId, dimensions) {
|
|
907
|
+
this.sheetDimensionsCache.set(sheetId, { width: dimensions.width, height: dimensions.height });
|
|
908
|
+
}
|
|
909
|
+
cacheInitializedSheetDimensions(sheetId, dimensions) {
|
|
910
|
+
if (this.sheetHasSpills(sheetId)) {
|
|
911
|
+
this.sheetDimensionsCache.delete(sheetId);
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
this.cacheSheetDimensions(sheetId, dimensions);
|
|
915
|
+
}
|
|
916
|
+
sheetHasSpills(sheetId) {
|
|
917
|
+
if (this.spillSheetIdsCache === null) {
|
|
918
|
+
const spillSheetIds = new Set();
|
|
919
|
+
this.engine.workbook.listSpills().forEach((spill) => {
|
|
920
|
+
const spillSheet = this.engine.workbook.getSheet(spill.sheetName);
|
|
921
|
+
if (spillSheet) {
|
|
922
|
+
spillSheetIds.add(spillSheet.id);
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
this.spillSheetIdsCache = spillSheetIds;
|
|
926
|
+
}
|
|
927
|
+
return this.spillSheetIdsCache.has(sheetId);
|
|
928
|
+
}
|
|
929
|
+
scanSheetDimensions(sheet) {
|
|
930
|
+
let width = 0;
|
|
931
|
+
let height = 0;
|
|
932
|
+
sheet.grid.forEachCellEntry((_cellIndex, row, col) => {
|
|
933
|
+
height = Math.max(height, row + 1);
|
|
934
|
+
width = Math.max(width, col + 1);
|
|
935
|
+
});
|
|
936
|
+
return { width, height };
|
|
937
|
+
}
|
|
938
|
+
invalidateSheetDimensions(sheetId) {
|
|
939
|
+
this.sheetDimensionsCache.delete(sheetId);
|
|
940
|
+
}
|
|
941
|
+
invalidateAllSheetDimensions() {
|
|
942
|
+
this.sheetDimensionsCache.clear();
|
|
943
|
+
this.spillSheetIdsCache = null;
|
|
944
|
+
}
|
|
945
|
+
expandCachedSheetDimensions(sheetId, row, col) {
|
|
946
|
+
const cached = this.sheetDimensionsCache.get(sheetId);
|
|
947
|
+
if (!cached) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
cached.height = Math.max(cached.height, row + 1);
|
|
951
|
+
cached.width = Math.max(cached.width, col + 1);
|
|
952
|
+
}
|
|
953
|
+
invalidateCachedSheetDimensionsIfEdge(sheetId, row, col) {
|
|
954
|
+
const cached = this.sheetDimensionsCache.get(sheetId);
|
|
955
|
+
if (!cached) {
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (row + 1 >= cached.height || col + 1 >= cached.width) {
|
|
959
|
+
this.invalidateSheetDimensions(sheetId);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
updateSheetDimensionsAfterCellMutationRefs(refs) {
|
|
963
|
+
if (this.sheetDimensionsCache.size === 0) {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
if (refs.length === 1) {
|
|
967
|
+
const ref = refs[0];
|
|
968
|
+
const mutation = ref?.mutation;
|
|
969
|
+
if (ref && mutation && mutation.kind !== 'setCellFormula') {
|
|
970
|
+
const cached = this.sheetDimensionsCache.get(ref.sheetId);
|
|
971
|
+
if (!cached) {
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
const noKnownSpills = this.spillSheetIdsCache !== null && !this.spillSheetIdsCache.has(ref.sheetId);
|
|
975
|
+
if (noKnownSpills &&
|
|
976
|
+
(mutation.kind === 'setCellValue'
|
|
977
|
+
? mutation.row + 1 <= cached.height && mutation.col + 1 <= cached.width
|
|
978
|
+
: mutation.row + 1 < cached.height && mutation.col + 1 < cached.width)) {
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
for (let index = 0; index < refs.length; index += 1) {
|
|
984
|
+
const ref = refs[index];
|
|
985
|
+
if (!ref) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
const mutation = ref.mutation;
|
|
989
|
+
if (mutation.kind === 'setCellFormula') {
|
|
990
|
+
this.spillSheetIdsCache = null;
|
|
991
|
+
this.invalidateSheetDimensions(ref.sheetId);
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
if (this.sheetHasSpills(ref.sheetId)) {
|
|
995
|
+
this.invalidateSheetDimensions(ref.sheetId);
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
if (mutation.kind === 'clearCell') {
|
|
999
|
+
this.invalidateCachedSheetDimensionsIfEdge(ref.sheetId, mutation.row, mutation.col);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
this.expandCachedSheetDimensions(ref.sheetId, mutation.row, mutation.col);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
729
1005
|
constructor(configInput = {}) {
|
|
730
1006
|
ensureCustomAdapterInstalled();
|
|
731
1007
|
validateWorkPaperConfig(configInput);
|
|
@@ -738,6 +1014,7 @@ export class WorkPaper {
|
|
|
738
1014
|
useColumnIndex: this.config.useColumnIndex,
|
|
739
1015
|
trackReplicaVersions: false,
|
|
740
1016
|
});
|
|
1017
|
+
this.invalidateAllSheetDimensions();
|
|
741
1018
|
this.attachEngineEventTracking();
|
|
742
1019
|
this.captureFunctionRegistry();
|
|
743
1020
|
this.internals = Object.freeze({
|
|
@@ -811,38 +1088,69 @@ export class WorkPaper {
|
|
|
811
1088
|
static buildFromSheets(sheets, configInput = {}, namedExpressions = []) {
|
|
812
1089
|
const workbook = new WorkPaper(configInput);
|
|
813
1090
|
const runtimeSnapshot = namedExpressions.length === 0 ? readRuntimeSnapshot(sheets) : undefined;
|
|
814
|
-
const runtimeSnapshotMatchesSheets = runtimeSnapshot !== undefined &&
|
|
815
|
-
|
|
816
|
-
runtimeSnapshot.sheets.
|
|
1091
|
+
const runtimeSnapshotMatchesSheets = runtimeSnapshot !== undefined && runtimeSnapshotMatchesSheetNames(sheets, runtimeSnapshot);
|
|
1092
|
+
const runtimeSnapshotSheetsByName = runtimeSnapshotMatchesSheets
|
|
1093
|
+
? new Map(runtimeSnapshot.sheets.map((sheet) => [sheet.name, sheet]))
|
|
1094
|
+
: undefined;
|
|
1095
|
+
const runtimeImageSheetCellsByName = runtimeSnapshotMatchesSheets && runtimeSnapshot
|
|
1096
|
+
? new Map((readRuntimeImage(runtimeSnapshot)?.sheetCells ?? []).map((sheet) => [sheet.sheetName, sheet]))
|
|
1097
|
+
: undefined;
|
|
1098
|
+
const inspectedSheets = new Map();
|
|
817
1099
|
Object.entries(sheets).forEach(([sheetName, sheet]) => {
|
|
818
|
-
|
|
1100
|
+
const snapshotSheet = runtimeSnapshotSheetsByName?.get(sheetName);
|
|
1101
|
+
inspectedSheets.set(sheetName, snapshotSheet
|
|
1102
|
+
? (() => {
|
|
1103
|
+
const dimensions = inspectRuntimeSnapshotSheetDimensionsWithinLimits({
|
|
1104
|
+
sheetName,
|
|
1105
|
+
snapshotSheet,
|
|
1106
|
+
runtimeSheetCells: runtimeImageSheetCellsByName?.get(sheetName),
|
|
1107
|
+
config: workbook.config,
|
|
1108
|
+
});
|
|
1109
|
+
return {
|
|
1110
|
+
hasFormula: false,
|
|
1111
|
+
dimensions,
|
|
1112
|
+
materializedCellCount: runtimeImageSheetCellsByName?.get(sheetName)?.cellCount ?? 0,
|
|
1113
|
+
maxColumnCount: dimensions.width,
|
|
1114
|
+
formulaCellCount: 0,
|
|
1115
|
+
};
|
|
1116
|
+
})()
|
|
1117
|
+
: inspectSheetWithinLimits(sheetName, sheet, workbook.config));
|
|
819
1118
|
});
|
|
820
1119
|
workbook.withEngineEventCaptureDisabled(() => {
|
|
821
1120
|
if (runtimeSnapshot && runtimeSnapshotMatchesSheets) {
|
|
822
1121
|
workbook.engine.importSnapshot(runtimeSnapshot);
|
|
823
|
-
return;
|
|
824
1122
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1123
|
+
else {
|
|
1124
|
+
Object.keys(sheets).forEach((sheetName) => {
|
|
1125
|
+
workbook.engine.createSheet(sheetName);
|
|
1126
|
+
});
|
|
1127
|
+
namedExpressions.forEach((expression) => {
|
|
1128
|
+
workbook.upsertNamedExpressionInternal(expression, { duringInitialization: true });
|
|
1129
|
+
});
|
|
1130
|
+
Object.entries(sheets).forEach(([sheetName, sheet]) => {
|
|
1131
|
+
const sheetId = workbook.requireSheetId(sheetName);
|
|
1132
|
+
const inspected = inspectedSheets.get(sheetName);
|
|
1133
|
+
if (!inspected?.hasFormula) {
|
|
1134
|
+
loadInitialLiteralSheet(workbook.engine, sheetId, sheet, inspected);
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
const rewriteInitialFormula = workbook.namedExpressions.size === 0 && workbook.functionAliasLookup.size === 0
|
|
1138
|
+
? (formula) => formula
|
|
1139
|
+
: (formula) => workbook.rewriteFormulaForStorage(formula, sheetId);
|
|
837
1140
|
loadInitialMixedSheet({
|
|
838
1141
|
engine: workbook.engine,
|
|
839
1142
|
sheetId,
|
|
840
1143
|
content: sheet,
|
|
841
|
-
rewriteFormula:
|
|
1144
|
+
rewriteFormula: rewriteInitialFormula,
|
|
1145
|
+
inspection: inspected,
|
|
842
1146
|
});
|
|
843
|
-
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1149
|
+
Object.keys(sheets).forEach((sheetName) => {
|
|
1150
|
+
const inspected = inspectedSheets.get(sheetName);
|
|
1151
|
+
if (inspected) {
|
|
1152
|
+
workbook.cacheInitializedSheetDimensions(workbook.requireSheetId(sheetName), inspected.dimensions);
|
|
844
1153
|
}
|
|
845
|
-
workbook.replaceSheetContentInternal(sheetId, sheet, { duringInitialization: true });
|
|
846
1154
|
});
|
|
847
1155
|
});
|
|
848
1156
|
workbook.clearHistoryStacks();
|
|
@@ -1008,6 +1316,7 @@ export class WorkPaper {
|
|
|
1008
1316
|
}
|
|
1009
1317
|
updateConfig(next) {
|
|
1010
1318
|
this.assertNotDisposed();
|
|
1319
|
+
this.materializePendingLazyTrackedChanges();
|
|
1011
1320
|
const merged = {
|
|
1012
1321
|
...this.config,
|
|
1013
1322
|
...cloneConfig(next),
|
|
@@ -1043,9 +1352,11 @@ export class WorkPaper {
|
|
|
1043
1352
|
}
|
|
1044
1353
|
rebuildAndRecalculate() {
|
|
1045
1354
|
this.assertNotDisposed();
|
|
1355
|
+
this.materializePendingLazyTrackedChanges();
|
|
1046
1356
|
if (this.shouldSuppressEvents()) {
|
|
1047
1357
|
try {
|
|
1048
1358
|
this.engine.recalculateNow();
|
|
1359
|
+
this.invalidateAllSheetDimensions();
|
|
1049
1360
|
}
|
|
1050
1361
|
catch (error) {
|
|
1051
1362
|
throw new WorkPaperOperationError(this.messageOf(error, 'Recalculation failed'));
|
|
@@ -1057,6 +1368,7 @@ export class WorkPaper {
|
|
|
1057
1368
|
this.drainTrackedEngineEvents();
|
|
1058
1369
|
try {
|
|
1059
1370
|
this.engine.recalculateNow();
|
|
1371
|
+
this.invalidateAllSheetDimensions();
|
|
1060
1372
|
}
|
|
1061
1373
|
catch (error) {
|
|
1062
1374
|
throw new WorkPaperOperationError(this.messageOf(error, 'Recalculation failed'));
|
|
@@ -1069,13 +1381,14 @@ export class WorkPaper {
|
|
|
1069
1381
|
...this.computeCellChanges(beforeVisibility, afterVisibility),
|
|
1070
1382
|
...this.computeNamedExpressionChanges(beforeNames, afterNames),
|
|
1071
1383
|
];
|
|
1072
|
-
if (changes.length > 0) {
|
|
1384
|
+
if (changes.length > 0 && this.emitter.hasListeners('valuesUpdated')) {
|
|
1073
1385
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1074
1386
|
}
|
|
1075
1387
|
return changes;
|
|
1076
1388
|
}
|
|
1077
1389
|
batch(batchOperations) {
|
|
1078
1390
|
this.assertNotDisposed();
|
|
1391
|
+
this.materializePendingLazyTrackedChanges();
|
|
1079
1392
|
const isOutermost = this.batchDepth === 0;
|
|
1080
1393
|
if (isOutermost) {
|
|
1081
1394
|
this.batchUsesTrackedFastPath = this.canUseTrackedMutationFastPath();
|
|
@@ -1097,22 +1410,32 @@ export class WorkPaper {
|
|
|
1097
1410
|
finally {
|
|
1098
1411
|
this.batchDepth -= 1;
|
|
1099
1412
|
if (isOutermost) {
|
|
1100
|
-
this.
|
|
1413
|
+
if (this.batchUsesTrackedFastPath) {
|
|
1414
|
+
this.withRetainedTrackedEngineEventIndices(() => {
|
|
1415
|
+
this.flushPendingBatchOps();
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
this.flushPendingBatchOps();
|
|
1420
|
+
}
|
|
1101
1421
|
this.mergeUndoHistory(this.batchUndoStackLength);
|
|
1102
1422
|
}
|
|
1103
1423
|
}
|
|
1104
1424
|
if (!isOutermost) {
|
|
1105
1425
|
return [];
|
|
1106
1426
|
}
|
|
1427
|
+
const shouldEmitValuesUpdated = this.emitter.hasListeners('valuesUpdated');
|
|
1107
1428
|
const changes = this.batchUsesTrackedFastPath
|
|
1108
|
-
? this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents()
|
|
1429
|
+
? this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents(), {
|
|
1430
|
+
preferLazyPublicChanges: !shouldEmitValuesUpdated,
|
|
1431
|
+
})
|
|
1109
1432
|
: this.computeChangesAfterMutation(this.batchStartVisibility ?? new Map(), this.batchStartNamedValues ?? new Map());
|
|
1110
1433
|
this.batchUsesTrackedFastPath = false;
|
|
1111
1434
|
this.batchStartVisibility = null;
|
|
1112
1435
|
this.batchStartNamedValues = null;
|
|
1113
1436
|
if (!this.evaluationSuspended) {
|
|
1114
1437
|
this.flushQueuedEvents();
|
|
1115
|
-
if (changes.length > 0) {
|
|
1438
|
+
if (changes.length > 0 && shouldEmitValuesUpdated) {
|
|
1116
1439
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1117
1440
|
}
|
|
1118
1441
|
}
|
|
@@ -1120,6 +1443,7 @@ export class WorkPaper {
|
|
|
1120
1443
|
}
|
|
1121
1444
|
suspendEvaluation() {
|
|
1122
1445
|
this.assertNotDisposed();
|
|
1446
|
+
this.materializePendingLazyTrackedChanges();
|
|
1123
1447
|
if (this.evaluationSuspended) {
|
|
1124
1448
|
return;
|
|
1125
1449
|
}
|
|
@@ -1140,12 +1464,23 @@ export class WorkPaper {
|
|
|
1140
1464
|
}
|
|
1141
1465
|
resumeEvaluation() {
|
|
1142
1466
|
this.assertNotDisposed();
|
|
1467
|
+
this.materializePendingLazyTrackedChanges();
|
|
1143
1468
|
if (!this.evaluationSuspended) {
|
|
1144
1469
|
return [];
|
|
1145
1470
|
}
|
|
1146
|
-
this.
|
|
1471
|
+
if (this.suspendedUsesTrackedFastPath) {
|
|
1472
|
+
this.withRetainedTrackedEngineEventIndices(() => {
|
|
1473
|
+
this.flushSuspendedCellMutations();
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1476
|
+
else {
|
|
1477
|
+
this.flushSuspendedCellMutations();
|
|
1478
|
+
}
|
|
1479
|
+
const shouldEmitValuesUpdated = this.emitter.hasListeners('valuesUpdated');
|
|
1147
1480
|
const changes = this.suspendedUsesTrackedFastPath
|
|
1148
|
-
? this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents()
|
|
1481
|
+
? this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents(), {
|
|
1482
|
+
preferLazyPublicChanges: !shouldEmitValuesUpdated,
|
|
1483
|
+
})
|
|
1149
1484
|
: this.computeChangesAfterMutation(this.suspendedVisibility ?? new Map(), this.suspendedNamedValues ?? new Map());
|
|
1150
1485
|
this.evaluationSuspended = false;
|
|
1151
1486
|
this.suspendedVisibility = null;
|
|
@@ -1153,7 +1488,7 @@ export class WorkPaper {
|
|
|
1153
1488
|
this.suspendedUsesTrackedFastPath = false;
|
|
1154
1489
|
this.flushQueuedEvents();
|
|
1155
1490
|
this.emitter.emitDetailed({ eventName: 'evaluationResumed', payload: { changes } });
|
|
1156
|
-
if (changes.length > 0) {
|
|
1491
|
+
if (changes.length > 0 && shouldEmitValuesUpdated) {
|
|
1157
1492
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1158
1493
|
}
|
|
1159
1494
|
return changes;
|
|
@@ -1163,19 +1498,39 @@ export class WorkPaper {
|
|
|
1163
1498
|
}
|
|
1164
1499
|
undo() {
|
|
1165
1500
|
this.assertNotDisposed();
|
|
1501
|
+
const preservesPositions = !this.historyTopIsCellMutations(this.getUndoStack());
|
|
1502
|
+
if (this.canUseTrackedMutationFastPath()) {
|
|
1503
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1504
|
+
if (!this.engine.undo()) {
|
|
1505
|
+
throw new WorkPaperNoOperationToUndoError();
|
|
1506
|
+
}
|
|
1507
|
+
this.invalidateAllSheetDimensions();
|
|
1508
|
+
}, { preservePendingTrackedPositions: preservesPositions });
|
|
1509
|
+
}
|
|
1166
1510
|
return this.captureChanges(undefined, () => {
|
|
1167
1511
|
if (!this.engine.undo()) {
|
|
1168
1512
|
throw new WorkPaperNoOperationToUndoError();
|
|
1169
1513
|
}
|
|
1170
|
-
|
|
1514
|
+
this.invalidateAllSheetDimensions();
|
|
1515
|
+
}, { preservePendingTrackedPositions: preservesPositions });
|
|
1171
1516
|
}
|
|
1172
1517
|
redo() {
|
|
1173
1518
|
this.assertNotDisposed();
|
|
1519
|
+
const preservesPositions = !this.historyTopIsCellMutations(this.getRedoStack());
|
|
1520
|
+
if (this.canUseTrackedMutationFastPath()) {
|
|
1521
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1522
|
+
if (!this.engine.redo()) {
|
|
1523
|
+
throw new WorkPaperNoOperationToRedoError();
|
|
1524
|
+
}
|
|
1525
|
+
this.invalidateAllSheetDimensions();
|
|
1526
|
+
}, { preservePendingTrackedPositions: preservesPositions });
|
|
1527
|
+
}
|
|
1174
1528
|
return this.captureChanges(undefined, () => {
|
|
1175
1529
|
if (!this.engine.redo()) {
|
|
1176
1530
|
throw new WorkPaperNoOperationToRedoError();
|
|
1177
1531
|
}
|
|
1178
|
-
|
|
1532
|
+
this.invalidateAllSheetDimensions();
|
|
1533
|
+
}, { preservePendingTrackedPositions: preservesPositions });
|
|
1179
1534
|
}
|
|
1180
1535
|
isThereSomethingToUndo() {
|
|
1181
1536
|
return this.getUndoStack().length > 0;
|
|
@@ -1254,7 +1609,11 @@ export class WorkPaper {
|
|
|
1254
1609
|
}
|
|
1255
1610
|
getCellValue(address) {
|
|
1256
1611
|
this.assertReadable();
|
|
1257
|
-
|
|
1612
|
+
const sheet = this.sheetRecord(address.sheet);
|
|
1613
|
+
const cellIndex = this.getVisibleCellIndexInSheet(sheet, address.row, address.col);
|
|
1614
|
+
return cellIndex === undefined
|
|
1615
|
+
? emptyValue()
|
|
1616
|
+
: readTrackedRuntimeCellValue(this.engine.workbook.cellStore, cellIndex, this.engine.strings);
|
|
1258
1617
|
}
|
|
1259
1618
|
getCellFormula(address) {
|
|
1260
1619
|
this.prepareReadableState();
|
|
@@ -1282,6 +1641,11 @@ export class WorkPaper {
|
|
|
1282
1641
|
}
|
|
1283
1642
|
getRangeValues(range) {
|
|
1284
1643
|
this.assertReadable();
|
|
1644
|
+
assertRange(range);
|
|
1645
|
+
const fastValues = readFastPhysicalRangeValues(this.engine, range);
|
|
1646
|
+
if (fastValues !== undefined) {
|
|
1647
|
+
return fastValues;
|
|
1648
|
+
}
|
|
1285
1649
|
const ref = this.rangeRef(range);
|
|
1286
1650
|
return this.engine.getRangeValues(ref);
|
|
1287
1651
|
}
|
|
@@ -1340,13 +1704,13 @@ export class WorkPaper {
|
|
|
1340
1704
|
getSheetDimensions(sheetId) {
|
|
1341
1705
|
this.prepareReadableState();
|
|
1342
1706
|
const sheet = this.sheetRecord(sheetId);
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
return
|
|
1707
|
+
const cached = this.sheetDimensionsCache.get(sheetId);
|
|
1708
|
+
if (cached) {
|
|
1709
|
+
return { width: cached.width, height: cached.height };
|
|
1710
|
+
}
|
|
1711
|
+
const dimensions = this.scanSheetDimensions(sheet);
|
|
1712
|
+
this.cacheSheetDimensions(sheetId, dimensions);
|
|
1713
|
+
return dimensions;
|
|
1350
1714
|
}
|
|
1351
1715
|
simpleCellAddressFromString(value, defaultSheetId) {
|
|
1352
1716
|
this.assertNotDisposed();
|
|
@@ -1744,6 +2108,81 @@ export class WorkPaper {
|
|
|
1744
2108
|
const { hours, minutes, seconds } = dateTime;
|
|
1745
2109
|
return { hours, minutes, seconds };
|
|
1746
2110
|
}
|
|
2111
|
+
trySetExistingNumericCellContentsWithTrackedFastPath(args) {
|
|
2112
|
+
if (!this.canUseTrackedMutationFastPath() || args.sheet.structureVersion !== 1) {
|
|
2113
|
+
return null;
|
|
2114
|
+
}
|
|
2115
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
2116
|
+
if (cellStore.sheetIds[args.cellIndex] !== args.address.sheet ||
|
|
2117
|
+
cellStore.rows[args.cellIndex] !== args.address.row ||
|
|
2118
|
+
cellStore.cols[args.cellIndex] !== args.address.col ||
|
|
2119
|
+
(cellStore.formulaIds[args.cellIndex] ?? 0) !== 0 ||
|
|
2120
|
+
((cellStore.flags[args.cellIndex] ?? 0) & FAST_EXISTING_NUMERIC_LITERAL_FLAGS) !== 0 ||
|
|
2121
|
+
cellStore.tags[args.cellIndex] !== ValueTag.Number) {
|
|
2122
|
+
return null;
|
|
2123
|
+
}
|
|
2124
|
+
const existingNumericMutationEngine = this.engine;
|
|
2125
|
+
if (typeof existingNumericMutationEngine.tryApplyExistingNumericCellMutationAt !== 'function') {
|
|
2126
|
+
return null;
|
|
2127
|
+
}
|
|
2128
|
+
if (this.pendingLazyTrackedChanges.length > 0) {
|
|
2129
|
+
this.materializePendingLazyTrackedChanges();
|
|
2130
|
+
}
|
|
2131
|
+
if (this.trackedEngineEvents.length > 0) {
|
|
2132
|
+
this.drainTrackedEngineEvents();
|
|
2133
|
+
}
|
|
2134
|
+
let result = null;
|
|
2135
|
+
const oldNumericValue = cellStore.numbers[args.cellIndex] ?? 0;
|
|
2136
|
+
const previousCaptureEnabled = this.engineEventCaptureEnabled;
|
|
2137
|
+
this.engineEventCaptureEnabled = false;
|
|
2138
|
+
try {
|
|
2139
|
+
if (this.pendingBatchOps.length > 0) {
|
|
2140
|
+
this.flushPendingBatchOps();
|
|
2141
|
+
}
|
|
2142
|
+
const request = {
|
|
2143
|
+
sheetId: args.address.sheet,
|
|
2144
|
+
row: args.address.row,
|
|
2145
|
+
col: args.address.col,
|
|
2146
|
+
cellIndex: args.cellIndex,
|
|
2147
|
+
value: args.value,
|
|
2148
|
+
emitTracked: false,
|
|
2149
|
+
trustedExistingNumericLiteral: true,
|
|
2150
|
+
oldNumericValue,
|
|
2151
|
+
};
|
|
2152
|
+
result = existingNumericMutationEngine.tryApplyExistingNumericCellMutationAt(request);
|
|
2153
|
+
}
|
|
2154
|
+
catch (error) {
|
|
2155
|
+
if (error instanceof Error && WORKPAPER_PUBLIC_ERROR_NAMES.has(error.name)) {
|
|
2156
|
+
throw error;
|
|
2157
|
+
}
|
|
2158
|
+
throw new WorkPaperOperationError(this.messageOf(error, 'Mutation failed'));
|
|
2159
|
+
}
|
|
2160
|
+
finally {
|
|
2161
|
+
this.engineEventCaptureEnabled = previousCaptureEnabled;
|
|
2162
|
+
}
|
|
2163
|
+
if (!result) {
|
|
2164
|
+
return null;
|
|
2165
|
+
}
|
|
2166
|
+
if (this.trackedEngineEvents.length > 0) {
|
|
2167
|
+
this.trackedEngineEvents = [];
|
|
2168
|
+
}
|
|
2169
|
+
let changes = this.tryBuildDirectExistingNumericTrackedChanges(result, args.address, args.cellIndex, true, args.sheet.name, args.value);
|
|
2170
|
+
if (changes === null) {
|
|
2171
|
+
const shouldEmitValuesUpdated = this.emitter.hasListeners('valuesUpdated');
|
|
2172
|
+
const events = [this.trackedEventFromExistingNumericMutationResult(result)];
|
|
2173
|
+
changes = this.computeTrackedChangesWithoutVisibilityCache(events, {
|
|
2174
|
+
preferLazyPublicChanges: !shouldEmitValuesUpdated,
|
|
2175
|
+
});
|
|
2176
|
+
if (changes.length > 0 && shouldEmitValuesUpdated) {
|
|
2177
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2178
|
+
}
|
|
2179
|
+
return changes;
|
|
2180
|
+
}
|
|
2181
|
+
if (changes.length > 0 && this.emitter.hasListeners('valuesUpdated')) {
|
|
2182
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2183
|
+
}
|
|
2184
|
+
return changes;
|
|
2185
|
+
}
|
|
1747
2186
|
setCellContents(address, content) {
|
|
1748
2187
|
this.assertNotDisposed();
|
|
1749
2188
|
const sheet = this.sheetRecord(address.sheet);
|
|
@@ -1753,15 +2192,40 @@ export class WorkPaper {
|
|
|
1753
2192
|
if (address.row >= (this.config.maxRows ?? MAX_ROWS) || address.col >= (this.config.maxColumns ?? MAX_COLS)) {
|
|
1754
2193
|
throw new WorkPaperOperationError('Cell contents cannot be set');
|
|
1755
2194
|
}
|
|
1756
|
-
const
|
|
1757
|
-
if (this.
|
|
2195
|
+
const visibleCellIndex = this.getVisibleCellIndexInSheet(sheet, address.row, address.col);
|
|
2196
|
+
if (this.evaluationSuspended &&
|
|
2197
|
+
this.enqueueSuspendedLiteralMutation(address.sheet, address.row, address.col, content, visibleCellIndex)) {
|
|
1758
2198
|
return [];
|
|
1759
2199
|
}
|
|
1760
|
-
if (this.enqueueDeferredBatchLiteral(address.sheet, address.row, address.col, content,
|
|
2200
|
+
if (this.batchDepth !== 0 && this.enqueueDeferredBatchLiteral(address.sheet, address.row, address.col, content, visibleCellIndex)) {
|
|
1761
2201
|
return [];
|
|
1762
2202
|
}
|
|
2203
|
+
if (typeof content === 'number' && visibleCellIndex !== undefined) {
|
|
2204
|
+
const fastPathChanges = this.trySetExistingNumericCellContentsWithTrackedFastPath({
|
|
2205
|
+
sheet,
|
|
2206
|
+
address,
|
|
2207
|
+
cellIndex: visibleCellIndex,
|
|
2208
|
+
value: content,
|
|
2209
|
+
});
|
|
2210
|
+
if (fastPathChanges !== null) {
|
|
2211
|
+
return fastPathChanges;
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
1763
2214
|
const mutate = () => {
|
|
1764
2215
|
this.flushPendingBatchOps();
|
|
2216
|
+
const existingNumericMutationEngine = this.engine;
|
|
2217
|
+
if (typeof content === 'number' &&
|
|
2218
|
+
visibleCellIndex !== undefined &&
|
|
2219
|
+
sheet.structureVersion === 1 &&
|
|
2220
|
+
existingNumericMutationEngine.tryApplyExistingNumericCellMutationAt?.({
|
|
2221
|
+
sheetId: address.sheet,
|
|
2222
|
+
row: address.row,
|
|
2223
|
+
col: address.col,
|
|
2224
|
+
cellIndex: visibleCellIndex,
|
|
2225
|
+
value: content,
|
|
2226
|
+
})) {
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
1765
2229
|
const mutation = content === null
|
|
1766
2230
|
? { kind: 'clearCell', row: address.row, col: address.col }
|
|
1767
2231
|
: typeof content === 'string' && content.trim().startsWith('=')
|
|
@@ -1777,16 +2241,26 @@ export class WorkPaper {
|
|
|
1777
2241
|
col: address.col,
|
|
1778
2242
|
value: content,
|
|
1779
2243
|
};
|
|
1780
|
-
this.applyCellMutationRefs([{ sheetId: address.sheet, mutation }], {
|
|
2244
|
+
this.applyCellMutationRefs([{ sheetId: address.sheet, mutation, ...(visibleCellIndex !== undefined ? { cellIndex: visibleCellIndex } : {}) }], {
|
|
1781
2245
|
captureUndo: true,
|
|
1782
|
-
potentialNewCells: content === null ||
|
|
2246
|
+
potentialNewCells: content === null || visibleCellIndex !== undefined ? 0 : 1,
|
|
1783
2247
|
source: 'local',
|
|
1784
2248
|
returnUndoOps: false,
|
|
1785
2249
|
reuseRefs: true,
|
|
1786
2250
|
});
|
|
1787
2251
|
};
|
|
1788
2252
|
if (this.canUseTrackedMutationFastPath()) {
|
|
1789
|
-
return this.captureTrackedChangesWithoutVisibilityCache(mutate
|
|
2253
|
+
return this.captureTrackedChangesWithoutVisibilityCache(mutate, {
|
|
2254
|
+
singleLiteralChange: isFormulaContent(content)
|
|
2255
|
+
? undefined
|
|
2256
|
+
: {
|
|
2257
|
+
address: { sheet: address.sheet, row: address.row, col: address.col },
|
|
2258
|
+
...(visibleCellIndex === undefined ? {} : { cellIndex: visibleCellIndex }),
|
|
2259
|
+
isPhysicalSheet: sheet.structureVersion === 1,
|
|
2260
|
+
sheetName: sheet.name,
|
|
2261
|
+
value: content,
|
|
2262
|
+
},
|
|
2263
|
+
});
|
|
1790
2264
|
}
|
|
1791
2265
|
return this.captureChanges(undefined, () => {
|
|
1792
2266
|
mutate();
|
|
@@ -1884,9 +2358,17 @@ export class WorkPaper {
|
|
|
1884
2358
|
if (!this.isItPossibleToAddRows(sheetId, ...indexes)) {
|
|
1885
2359
|
throw new WorkPaperOperationError('Rows cannot be added');
|
|
1886
2360
|
}
|
|
2361
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
2362
|
+
const [start, amount] = indexes[0];
|
|
2363
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
2364
|
+
this.engine.insertRows(this.sheetName(sheetId), start, amount);
|
|
2365
|
+
this.invalidateSheetDimensions(sheetId);
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
1887
2368
|
return this.batchStructuralChanges(() => {
|
|
1888
2369
|
indexes.forEach(([start, amount]) => {
|
|
1889
2370
|
this.engine.insertRows(this.sheetName(sheetId), start, amount);
|
|
2371
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1890
2372
|
});
|
|
1891
2373
|
});
|
|
1892
2374
|
}
|
|
@@ -1895,11 +2377,19 @@ export class WorkPaper {
|
|
|
1895
2377
|
if (!this.isItPossibleToRemoveRows(sheetId, ...indexes)) {
|
|
1896
2378
|
throw new WorkPaperOperationError('Rows cannot be removed');
|
|
1897
2379
|
}
|
|
2380
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
2381
|
+
const [start, amount] = indexes[0];
|
|
2382
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
2383
|
+
this.engine.deleteRows(this.sheetName(sheetId), start, amount);
|
|
2384
|
+
this.invalidateSheetDimensions(sheetId);
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
1898
2387
|
return this.batchStructuralChanges(() => {
|
|
1899
2388
|
indexes
|
|
1900
2389
|
.toSorted((left, right) => right[0] - left[0])
|
|
1901
2390
|
.forEach(([start, amount]) => {
|
|
1902
2391
|
this.engine.deleteRows(this.sheetName(sheetId), start, amount);
|
|
2392
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1903
2393
|
});
|
|
1904
2394
|
});
|
|
1905
2395
|
}
|
|
@@ -1908,9 +2398,17 @@ export class WorkPaper {
|
|
|
1908
2398
|
if (!this.isItPossibleToAddColumns(sheetId, ...indexes)) {
|
|
1909
2399
|
throw new WorkPaperOperationError('Columns cannot be added');
|
|
1910
2400
|
}
|
|
2401
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
2402
|
+
const [start, amount] = indexes[0];
|
|
2403
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
2404
|
+
this.engine.insertColumns(this.sheetName(sheetId), start, amount);
|
|
2405
|
+
this.invalidateSheetDimensions(sheetId);
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
1911
2408
|
return this.batchStructuralChanges(() => {
|
|
1912
2409
|
indexes.forEach(([start, amount]) => {
|
|
1913
2410
|
this.engine.insertColumns(this.sheetName(sheetId), start, amount);
|
|
2411
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1914
2412
|
});
|
|
1915
2413
|
});
|
|
1916
2414
|
}
|
|
@@ -1919,11 +2417,19 @@ export class WorkPaper {
|
|
|
1919
2417
|
if (!this.isItPossibleToRemoveColumns(sheetId, ...indexes)) {
|
|
1920
2418
|
throw new WorkPaperOperationError('Columns cannot be removed');
|
|
1921
2419
|
}
|
|
2420
|
+
if (indexes.length === 1 && this.canUseTrackedStructuralFastPath()) {
|
|
2421
|
+
const [start, amount] = indexes[0];
|
|
2422
|
+
return this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
2423
|
+
this.engine.deleteColumns(this.sheetName(sheetId), start, amount);
|
|
2424
|
+
this.invalidateSheetDimensions(sheetId);
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
1922
2427
|
return this.batchStructuralChanges(() => {
|
|
1923
2428
|
indexes
|
|
1924
2429
|
.toSorted((left, right) => right[0] - left[0])
|
|
1925
2430
|
.forEach(([start, amount]) => {
|
|
1926
2431
|
this.engine.deleteColumns(this.sheetName(sheetId), start, amount);
|
|
2432
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1927
2433
|
});
|
|
1928
2434
|
});
|
|
1929
2435
|
}
|
|
@@ -1939,6 +2445,8 @@ export class WorkPaper {
|
|
|
1939
2445
|
startAddress: formatAddress(target.row, target.col),
|
|
1940
2446
|
endAddress: formatAddress(target.row + sourceHeight, target.col + sourceWidth),
|
|
1941
2447
|
});
|
|
2448
|
+
this.invalidateSheetDimensions(source.start.sheet);
|
|
2449
|
+
this.invalidateSheetDimensions(target.sheet);
|
|
1942
2450
|
});
|
|
1943
2451
|
}
|
|
1944
2452
|
moveRows(sheetId, start, count, target) {
|
|
@@ -1946,11 +2454,13 @@ export class WorkPaper {
|
|
|
1946
2454
|
throw new WorkPaperOperationError('Rows cannot be moved');
|
|
1947
2455
|
}
|
|
1948
2456
|
return this.canUseTrackedStructuralFastPath()
|
|
1949
|
-
? this.
|
|
2457
|
+
? this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1950
2458
|
this.engine.moveRows(this.sheetName(sheetId), start, count, target);
|
|
2459
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1951
2460
|
})
|
|
1952
2461
|
: this.captureChanges(undefined, () => {
|
|
1953
2462
|
this.engine.moveRows(this.sheetName(sheetId), start, count, target);
|
|
2463
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1954
2464
|
});
|
|
1955
2465
|
}
|
|
1956
2466
|
moveColumns(sheetId, start, count, target) {
|
|
@@ -1958,15 +2468,18 @@ export class WorkPaper {
|
|
|
1958
2468
|
throw new WorkPaperOperationError('Columns cannot be moved');
|
|
1959
2469
|
}
|
|
1960
2470
|
return this.canUseTrackedStructuralFastPath()
|
|
1961
|
-
? this.
|
|
2471
|
+
? this.captureTrackedChangesWithoutVisibilityCache(() => {
|
|
1962
2472
|
this.engine.moveColumns(this.sheetName(sheetId), start, count, target);
|
|
2473
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1963
2474
|
})
|
|
1964
2475
|
: this.captureChanges(undefined, () => {
|
|
1965
2476
|
this.engine.moveColumns(this.sheetName(sheetId), start, count, target);
|
|
2477
|
+
this.invalidateSheetDimensions(sheetId);
|
|
1966
2478
|
});
|
|
1967
2479
|
}
|
|
1968
2480
|
addSheet(sheetName) {
|
|
1969
2481
|
this.assertNotDisposed();
|
|
2482
|
+
this.materializePendingLazyTrackedChanges();
|
|
1970
2483
|
const name = sheetName?.trim() || this.nextSheetName();
|
|
1971
2484
|
if (!this.isItPossibleToAddSheet(name)) {
|
|
1972
2485
|
throw new WorkPaperSheetNameAlreadyTakenError(name);
|
|
@@ -1977,6 +2490,7 @@ export class WorkPaper {
|
|
|
1977
2490
|
this.engine.createSheet(name);
|
|
1978
2491
|
this.sheetRecordsCache = null;
|
|
1979
2492
|
const sheetId = this.requireSheetId(name);
|
|
2493
|
+
this.cacheSheetDimensions(sheetId, { width: 0, height: 0 });
|
|
1980
2494
|
const payload = { sheetId, sheetName: name };
|
|
1981
2495
|
if (this.shouldSuppressEvents()) {
|
|
1982
2496
|
this.queuedEvents.push({ eventName: 'sheetAdded', payload });
|
|
@@ -1985,7 +2499,7 @@ export class WorkPaper {
|
|
|
1985
2499
|
this.emitter.emitDetailed({ eventName: 'sheetAdded', payload });
|
|
1986
2500
|
}
|
|
1987
2501
|
const changes = this.computeChangesAfterMutation(beforeVisibility, beforeNames);
|
|
1988
|
-
if (!this.shouldSuppressEvents() && changes.length > 0) {
|
|
2502
|
+
if (!this.shouldSuppressEvents() && changes.length > 0 && this.emitter.hasListeners('valuesUpdated')) {
|
|
1989
2503
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1990
2504
|
}
|
|
1991
2505
|
return name;
|
|
@@ -2005,6 +2519,7 @@ export class WorkPaper {
|
|
|
2005
2519
|
}, () => {
|
|
2006
2520
|
this.engine.deleteSheet(sheetName);
|
|
2007
2521
|
this.sheetRecordsCache = null;
|
|
2522
|
+
this.invalidateSheetDimensions(sheetId);
|
|
2008
2523
|
});
|
|
2009
2524
|
}
|
|
2010
2525
|
clearSheet(sheetId) {
|
|
@@ -2021,6 +2536,7 @@ export class WorkPaper {
|
|
|
2021
2536
|
startAddress: 'A1',
|
|
2022
2537
|
endAddress: formatAddress(dimensions.height - 1, dimensions.width - 1),
|
|
2023
2538
|
});
|
|
2539
|
+
this.cacheSheetDimensions(sheetId, { width: 0, height: 0 });
|
|
2024
2540
|
});
|
|
2025
2541
|
}
|
|
2026
2542
|
setSheetContent(sheetId, content) {
|
|
@@ -2207,6 +2723,7 @@ export class WorkPaper {
|
|
|
2207
2723
|
if (this.disposed) {
|
|
2208
2724
|
return;
|
|
2209
2725
|
}
|
|
2726
|
+
this.materializePendingLazyTrackedChanges();
|
|
2210
2727
|
this.disposed = true;
|
|
2211
2728
|
this.unsubscribeEngineEvents?.();
|
|
2212
2729
|
this.unsubscribeEngineEvents = null;
|
|
@@ -2215,8 +2732,11 @@ export class WorkPaper {
|
|
|
2215
2732
|
this.clipboard = null;
|
|
2216
2733
|
this.visibilityCache = null;
|
|
2217
2734
|
this.namedExpressionValueCache = null;
|
|
2735
|
+
this.sheetDimensionsCache.clear();
|
|
2736
|
+
this.spillSheetIdsCache = null;
|
|
2218
2737
|
this.queuedEvents = [];
|
|
2219
2738
|
this.trackedEngineEvents = [];
|
|
2739
|
+
this.pendingLazyTrackedChanges = [];
|
|
2220
2740
|
this.namedExpressions.clear();
|
|
2221
2741
|
}
|
|
2222
2742
|
attachEngineEventTracking() {
|
|
@@ -2229,9 +2749,21 @@ export class WorkPaper {
|
|
|
2229
2749
|
if (!this.engineEventCaptureEnabled) {
|
|
2230
2750
|
return;
|
|
2231
2751
|
}
|
|
2232
|
-
this.trackedEngineEvents.push(captureTrackedEngineEvent(event
|
|
2752
|
+
this.trackedEngineEvents.push(captureTrackedEngineEvent(event, {
|
|
2753
|
+
borrowChangedCellIndexViews: this.retainedTrackedEngineEventIndicesDepth > 0,
|
|
2754
|
+
cloneChangedCellIndices: this.retainedTrackedEngineEventIndicesDepth === 0,
|
|
2755
|
+
}));
|
|
2233
2756
|
});
|
|
2234
2757
|
}
|
|
2758
|
+
withRetainedTrackedEngineEventIndices(callback) {
|
|
2759
|
+
this.retainedTrackedEngineEventIndicesDepth += 1;
|
|
2760
|
+
try {
|
|
2761
|
+
return callback();
|
|
2762
|
+
}
|
|
2763
|
+
finally {
|
|
2764
|
+
this.retainedTrackedEngineEventIndicesDepth -= 1;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2235
2767
|
withEngineEventCaptureDisabled(callback) {
|
|
2236
2768
|
const previous = this.engineEventCaptureEnabled;
|
|
2237
2769
|
this.engineEventCaptureEnabled = false;
|
|
@@ -2249,6 +2781,77 @@ export class WorkPaper {
|
|
|
2249
2781
|
this.trackedEngineEvents = [];
|
|
2250
2782
|
return events;
|
|
2251
2783
|
}
|
|
2784
|
+
existingNumericMutationChangedCellCount(result) {
|
|
2785
|
+
return result.changedCellIndices?.length ?? result.changedCellCount ?? 0;
|
|
2786
|
+
}
|
|
2787
|
+
existingNumericMutationChangedCellAt(result, index) {
|
|
2788
|
+
if (result.changedCellIndices) {
|
|
2789
|
+
return result.changedCellIndices[index];
|
|
2790
|
+
}
|
|
2791
|
+
if (index === 0) {
|
|
2792
|
+
return result.firstChangedCellIndex;
|
|
2793
|
+
}
|
|
2794
|
+
if (index === 1) {
|
|
2795
|
+
return result.secondChangedCellIndex;
|
|
2796
|
+
}
|
|
2797
|
+
return undefined;
|
|
2798
|
+
}
|
|
2799
|
+
materializeExistingNumericMutationChangedCellIndices(result) {
|
|
2800
|
+
if (result.changedCellIndices) {
|
|
2801
|
+
return result.changedCellIndices;
|
|
2802
|
+
}
|
|
2803
|
+
const count = this.existingNumericMutationChangedCellCount(result);
|
|
2804
|
+
const changed = new Uint32Array(count);
|
|
2805
|
+
for (let index = 0; index < count; index += 1) {
|
|
2806
|
+
changed[index] = this.existingNumericMutationChangedCellAt(result, index) ?? 0;
|
|
2807
|
+
}
|
|
2808
|
+
return changed;
|
|
2809
|
+
}
|
|
2810
|
+
trackedEventFromExistingNumericMutationResult(result) {
|
|
2811
|
+
let sortedDisjoint = true;
|
|
2812
|
+
let previous = -1;
|
|
2813
|
+
let firstChangedCellIndex;
|
|
2814
|
+
let lastChangedCellIndex;
|
|
2815
|
+
const changedCellCount = this.existingNumericMutationChangedCellCount(result);
|
|
2816
|
+
for (let index = 0; index < changedCellCount; index += 1) {
|
|
2817
|
+
const cellIndex = this.existingNumericMutationChangedCellAt(result, index) ?? -1;
|
|
2818
|
+
if (index === 0) {
|
|
2819
|
+
firstChangedCellIndex = cellIndex;
|
|
2820
|
+
}
|
|
2821
|
+
if (!Number.isInteger(cellIndex) || cellIndex < 0 || cellIndex <= previous) {
|
|
2822
|
+
sortedDisjoint = false;
|
|
2823
|
+
}
|
|
2824
|
+
previous = cellIndex;
|
|
2825
|
+
lastChangedCellIndex = cellIndex;
|
|
2826
|
+
}
|
|
2827
|
+
return {
|
|
2828
|
+
invalidation: 'cells',
|
|
2829
|
+
changedCellIndices: this.materializeExistingNumericMutationChangedCellIndices(result),
|
|
2830
|
+
changedInputCount: 1,
|
|
2831
|
+
explicitChangedCount: result.explicitChangedCount,
|
|
2832
|
+
changedCellIndicesSortedDisjoint: sortedDisjoint,
|
|
2833
|
+
...(firstChangedCellIndex === undefined ? {} : { firstChangedCellIndex }),
|
|
2834
|
+
...(lastChangedCellIndex === undefined ? {} : { lastChangedCellIndex }),
|
|
2835
|
+
hasInvalidatedRanges: false,
|
|
2836
|
+
hasInvalidatedRows: false,
|
|
2837
|
+
hasInvalidatedColumns: false,
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
trackLazyTrackedChanges(changes) {
|
|
2841
|
+
if (hasDeferredTrackedIndexChanges(changes)) {
|
|
2842
|
+
this.pendingLazyTrackedChanges.push(changes);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
materializePendingLazyTrackedChanges(options = {}) {
|
|
2846
|
+
if (this.pendingLazyTrackedChanges.length === 0) {
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
const pending = this.pendingLazyTrackedChanges;
|
|
2850
|
+
this.pendingLazyTrackedChanges = [];
|
|
2851
|
+
for (let index = 0; index < pending.length; index += 1) {
|
|
2852
|
+
detachTrackedIndexChanges(pending[index], { preservePositions: options.preservePositions });
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2252
2855
|
resetChangeTrackingCaches() {
|
|
2253
2856
|
this.sheetRecordsCache = null;
|
|
2254
2857
|
this.visibilityCache = null;
|
|
@@ -2278,11 +2881,12 @@ export class WorkPaper {
|
|
|
2278
2881
|
this.pendingBatchPotentialNewCells = 0;
|
|
2279
2882
|
this.engine.applyCellMutationsAtWithOptions(ops, {
|
|
2280
2883
|
captureUndo: true,
|
|
2281
|
-
potentialNewCells
|
|
2884
|
+
potentialNewCells,
|
|
2282
2885
|
source: 'local',
|
|
2283
2886
|
returnUndoOps: false,
|
|
2284
2887
|
reuseRefs: true,
|
|
2285
2888
|
});
|
|
2889
|
+
this.updateSheetDimensionsAfterCellMutationRefs(ops);
|
|
2286
2890
|
}
|
|
2287
2891
|
applyCellMutationRefs(refs, options) {
|
|
2288
2892
|
if (this.evaluationSuspended && (options.source ?? 'local') === 'local') {
|
|
@@ -2294,6 +2898,7 @@ export class WorkPaper {
|
|
|
2294
2898
|
const mutation = ref.mutation;
|
|
2295
2899
|
this.suspendedCellMutationRefs.push({
|
|
2296
2900
|
sheetId: ref.sheetId,
|
|
2901
|
+
...(ref.cellIndex !== undefined ? { cellIndex: ref.cellIndex } : {}),
|
|
2297
2902
|
mutation: mutation.kind === 'setCellValue'
|
|
2298
2903
|
? {
|
|
2299
2904
|
kind: 'setCellValue',
|
|
@@ -2315,11 +2920,13 @@ export class WorkPaper {
|
|
|
2315
2920
|
},
|
|
2316
2921
|
});
|
|
2317
2922
|
}
|
|
2318
|
-
this.suspendedCellMutationPotentialNewCells +=
|
|
2319
|
-
options.potentialNewCells ?? refs.reduce((count, ref) => (ref?.mutation.kind === 'clearCell' ? count : count + 1), 0);
|
|
2923
|
+
this.suspendedCellMutationPotentialNewCells += options.potentialNewCells ?? countPotentialNewTrackedCellMutations(refs);
|
|
2320
2924
|
return;
|
|
2321
2925
|
}
|
|
2322
2926
|
this.engine.applyCellMutationsAtWithOptions(refs, options);
|
|
2927
|
+
if (!canSkipDimensionUpdateAfterLiteralMutation(refs, options.potentialNewCells)) {
|
|
2928
|
+
this.updateSheetDimensionsAfterCellMutationRefs(refs);
|
|
2929
|
+
}
|
|
2323
2930
|
}
|
|
2324
2931
|
flushSuspendedCellMutations() {
|
|
2325
2932
|
if (this.suspendedCellMutationRefs.length === 0) {
|
|
@@ -2331,42 +2938,53 @@ export class WorkPaper {
|
|
|
2331
2938
|
this.suspendedCellMutationPotentialNewCells = 0;
|
|
2332
2939
|
this.engine.applyCellMutationsAtWithOptions(refs, {
|
|
2333
2940
|
captureUndo: true,
|
|
2334
|
-
potentialNewCells
|
|
2941
|
+
potentialNewCells,
|
|
2335
2942
|
source: 'local',
|
|
2336
2943
|
returnUndoOps: false,
|
|
2337
2944
|
reuseRefs: true,
|
|
2338
2945
|
});
|
|
2946
|
+
this.updateSheetDimensionsAfterCellMutationRefs(refs);
|
|
2339
2947
|
}
|
|
2340
|
-
enqueueSuspendedLiteralMutation(sheetId, row, col, content,
|
|
2948
|
+
enqueueSuspendedLiteralMutation(sheetId, row, col, content, cellIndex) {
|
|
2341
2949
|
if (!this.evaluationSuspended || !isDeferredBatchLiteralContent(content) || isFormulaContent(content)) {
|
|
2342
2950
|
return false;
|
|
2343
2951
|
}
|
|
2344
2952
|
if (content === null) {
|
|
2345
|
-
this.suspendedCellMutationRefs.push({
|
|
2953
|
+
this.suspendedCellMutationRefs.push({
|
|
2954
|
+
sheetId,
|
|
2955
|
+
mutation: { kind: 'clearCell', row, col },
|
|
2956
|
+
...(cellIndex !== undefined ? { cellIndex } : {}),
|
|
2957
|
+
});
|
|
2346
2958
|
return true;
|
|
2347
2959
|
}
|
|
2348
2960
|
this.suspendedCellMutationRefs.push({
|
|
2349
2961
|
sheetId,
|
|
2350
2962
|
mutation: { kind: 'setCellValue', row, col, value: content },
|
|
2963
|
+
...(cellIndex !== undefined ? { cellIndex } : {}),
|
|
2351
2964
|
});
|
|
2352
|
-
if (
|
|
2965
|
+
if (cellIndex === undefined) {
|
|
2353
2966
|
this.suspendedCellMutationPotentialNewCells += 1;
|
|
2354
2967
|
}
|
|
2355
2968
|
return true;
|
|
2356
2969
|
}
|
|
2357
|
-
enqueueDeferredBatchLiteral(sheetId, row, col, content,
|
|
2970
|
+
enqueueDeferredBatchLiteral(sheetId, row, col, content, cellIndex) {
|
|
2358
2971
|
if (this.batchDepth === 0 || this.evaluationSuspended || !isDeferredBatchLiteralContent(content) || isFormulaContent(content)) {
|
|
2359
2972
|
return false;
|
|
2360
2973
|
}
|
|
2361
2974
|
if (content === null) {
|
|
2362
|
-
this.pendingBatchOps.push({
|
|
2975
|
+
this.pendingBatchOps.push({
|
|
2976
|
+
sheetId,
|
|
2977
|
+
mutation: { kind: 'clearCell', row, col },
|
|
2978
|
+
...(cellIndex !== undefined ? { cellIndex } : {}),
|
|
2979
|
+
});
|
|
2363
2980
|
return true;
|
|
2364
2981
|
}
|
|
2365
2982
|
this.pendingBatchOps.push({
|
|
2366
2983
|
sheetId,
|
|
2367
2984
|
mutation: { kind: 'setCellValue', row, col, value: content },
|
|
2985
|
+
...(cellIndex !== undefined ? { cellIndex } : {}),
|
|
2368
2986
|
});
|
|
2369
|
-
if (
|
|
2987
|
+
if (cellIndex === undefined) {
|
|
2370
2988
|
this.pendingBatchPotentialNewCells += 1;
|
|
2371
2989
|
}
|
|
2372
2990
|
return true;
|
|
@@ -2406,6 +3024,28 @@ export class WorkPaper {
|
|
|
2406
3024
|
a1(address) {
|
|
2407
3025
|
return formatAddress(address.row, address.col);
|
|
2408
3026
|
}
|
|
3027
|
+
trackedA1(row, col) {
|
|
3028
|
+
if (row >= 0 && row < RUNTIME_A1_CACHE_ROW_LIMIT && col >= 0 && col < RUNTIME_A1_CACHE_COLUMN_LIMIT) {
|
|
3029
|
+
const cacheKey = row * RUNTIME_A1_CACHE_COLUMN_LIMIT + col;
|
|
3030
|
+
let cached = RUNTIME_A1_CACHE[cacheKey];
|
|
3031
|
+
if (cached === undefined) {
|
|
3032
|
+
let column = RUNTIME_COLUMN_LABEL_CACHE[col];
|
|
3033
|
+
if (column === undefined) {
|
|
3034
|
+
column = indexToColumn(col);
|
|
3035
|
+
RUNTIME_COLUMN_LABEL_CACHE[col] = column;
|
|
3036
|
+
}
|
|
3037
|
+
cached = `${column}${row + 1}`;
|
|
3038
|
+
RUNTIME_A1_CACHE[cacheKey] = cached;
|
|
3039
|
+
}
|
|
3040
|
+
return cached;
|
|
3041
|
+
}
|
|
3042
|
+
let column = RUNTIME_COLUMN_LABEL_CACHE[col];
|
|
3043
|
+
if (column === undefined) {
|
|
3044
|
+
column = indexToColumn(col);
|
|
3045
|
+
RUNTIME_COLUMN_LABEL_CACHE[col] = column;
|
|
3046
|
+
}
|
|
3047
|
+
return `${column}${row + 1}`;
|
|
3048
|
+
}
|
|
2409
3049
|
rangeRef(range) {
|
|
2410
3050
|
assertRange(range);
|
|
2411
3051
|
return sourceRangeRef(this.sheetName(range.start.sheet), range);
|
|
@@ -2500,44 +3140,264 @@ export class WorkPaper {
|
|
|
2500
3140
|
});
|
|
2501
3141
|
return orderWorkPaperCellChanges(cellChanges, this.listSheetRecords());
|
|
2502
3142
|
}
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
3143
|
+
materializeTrackedEventChanges(event, lazy = false) {
|
|
3144
|
+
if (event.patches && event.patches.length > 0) {
|
|
3145
|
+
const cellPatches = event.patches.filter((patch) => patch.kind === 'cell');
|
|
3146
|
+
return { changes: cellPatches, canReusePublicChanges: false, ordered: false };
|
|
3147
|
+
}
|
|
3148
|
+
const trustedPhysicalMetadata = lazy && event.changedCellIndices instanceof Uint32Array
|
|
3149
|
+
? readTrustedPhysicalTrackedChangeMetadata(event.changedCellIndices)
|
|
3150
|
+
: undefined;
|
|
3151
|
+
const materialized = materializeTrackedIndexChangesWithMetadata(this.engine, event.changedCellIndices, {
|
|
3152
|
+
explicitChangedCount: event.explicitChangedCount,
|
|
3153
|
+
lazy,
|
|
3154
|
+
...trustedPhysicalMetadata,
|
|
3155
|
+
});
|
|
3156
|
+
if (lazy) {
|
|
3157
|
+
this.trackLazyTrackedChanges(materialized.changes);
|
|
2509
3158
|
}
|
|
2510
|
-
|
|
2511
|
-
|
|
3159
|
+
return {
|
|
3160
|
+
changes: materialized.changes,
|
|
3161
|
+
canReusePublicChanges: true,
|
|
3162
|
+
ordered: materialized.ordered,
|
|
3163
|
+
};
|
|
3164
|
+
}
|
|
3165
|
+
readSingleTrackedCellChange(cellIndex) {
|
|
3166
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
3167
|
+
const sheetId = cellStore.sheetIds[cellIndex];
|
|
3168
|
+
if (sheetId === undefined) {
|
|
2512
3169
|
return undefined;
|
|
2513
3170
|
}
|
|
3171
|
+
const sheet = this.engine.workbook.getSheetById(sheetId);
|
|
3172
|
+
const sheetName = sheet?.name ?? this.engine.workbook.getSheetNameById(sheetId);
|
|
3173
|
+
let row;
|
|
3174
|
+
let col;
|
|
3175
|
+
if (!sheet || sheet.structureVersion === 1) {
|
|
3176
|
+
row = cellStore.rows[cellIndex];
|
|
3177
|
+
col = cellStore.cols[cellIndex];
|
|
3178
|
+
}
|
|
3179
|
+
else {
|
|
3180
|
+
const position = this.engine.workbook.getCellPosition(cellIndex);
|
|
3181
|
+
if (!position) {
|
|
3182
|
+
return undefined;
|
|
3183
|
+
}
|
|
3184
|
+
row = position.row;
|
|
3185
|
+
col = position.col;
|
|
3186
|
+
}
|
|
3187
|
+
const tag = cellStore.tags[cellIndex] ?? ValueTag.Empty;
|
|
3188
|
+
let newValue;
|
|
3189
|
+
switch (tag) {
|
|
3190
|
+
case ValueTag.Number:
|
|
3191
|
+
newValue = { tag: ValueTag.Number, value: cellStore.numbers[cellIndex] ?? 0 };
|
|
3192
|
+
break;
|
|
3193
|
+
case ValueTag.Boolean:
|
|
3194
|
+
newValue = { tag: ValueTag.Boolean, value: (cellStore.numbers[cellIndex] ?? 0) !== 0 };
|
|
3195
|
+
break;
|
|
3196
|
+
case ValueTag.String:
|
|
3197
|
+
newValue = cellStore.getValue(cellIndex, (stringId) => this.engine.strings.get(stringId));
|
|
3198
|
+
break;
|
|
3199
|
+
case ValueTag.Error:
|
|
3200
|
+
newValue = { tag: ValueTag.Error, code: cellStore.errors[cellIndex] };
|
|
3201
|
+
break;
|
|
3202
|
+
case ValueTag.Empty:
|
|
3203
|
+
default:
|
|
3204
|
+
newValue = { tag: ValueTag.Empty };
|
|
3205
|
+
break;
|
|
3206
|
+
}
|
|
2514
3207
|
return {
|
|
2515
3208
|
kind: 'cell',
|
|
2516
3209
|
address: { sheet: sheetId, row, col },
|
|
2517
3210
|
sheetName,
|
|
2518
|
-
a1:
|
|
2519
|
-
newValue
|
|
3211
|
+
a1: this.trackedA1(row, col),
|
|
3212
|
+
newValue,
|
|
2520
3213
|
};
|
|
2521
3214
|
}
|
|
2522
|
-
|
|
2523
|
-
if (event.
|
|
2524
|
-
|
|
2525
|
-
|
|
3215
|
+
readTinySortedPhysicalTrackedEventChanges(event) {
|
|
3216
|
+
if (!event.changedCellIndicesSortedDisjoint) {
|
|
3217
|
+
return null;
|
|
3218
|
+
}
|
|
3219
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
3220
|
+
const firstCellIndex = event.changedCellIndices[0];
|
|
3221
|
+
if (firstCellIndex === undefined) {
|
|
3222
|
+
return [];
|
|
3223
|
+
}
|
|
3224
|
+
const sheetId = cellStore.sheetIds[firstCellIndex];
|
|
3225
|
+
if (sheetId === undefined) {
|
|
3226
|
+
return [];
|
|
3227
|
+
}
|
|
3228
|
+
const sheet = this.engine.workbook.getSheetById(sheetId);
|
|
3229
|
+
if (sheet && sheet.structureVersion !== 1) {
|
|
3230
|
+
return null;
|
|
3231
|
+
}
|
|
3232
|
+
const sheetName = sheet?.name ?? this.engine.workbook.getSheetNameById(sheetId);
|
|
3233
|
+
if (event.changedCellIndices.length === 1) {
|
|
3234
|
+
const row = cellStore.rows[firstCellIndex];
|
|
3235
|
+
const col = cellStore.cols[firstCellIndex];
|
|
3236
|
+
return [
|
|
3237
|
+
{
|
|
3238
|
+
kind: 'cell',
|
|
3239
|
+
address: { sheet: sheetId, row, col },
|
|
3240
|
+
sheetName,
|
|
3241
|
+
a1: this.trackedA1(row, col),
|
|
3242
|
+
newValue: readTrackedRuntimeCellValue(cellStore, firstCellIndex, this.engine.strings),
|
|
3243
|
+
},
|
|
3244
|
+
];
|
|
3245
|
+
}
|
|
3246
|
+
if (event.changedCellIndices.length === 2) {
|
|
3247
|
+
const secondCellIndex = event.changedCellIndices[1];
|
|
3248
|
+
if (cellStore.sheetIds[secondCellIndex] !== sheetId) {
|
|
3249
|
+
return null;
|
|
3250
|
+
}
|
|
3251
|
+
const firstRow = cellStore.rows[firstCellIndex];
|
|
3252
|
+
const firstCol = cellStore.cols[firstCellIndex];
|
|
3253
|
+
const secondRow = cellStore.rows[secondCellIndex];
|
|
3254
|
+
const secondCol = cellStore.cols[secondCellIndex];
|
|
3255
|
+
if (secondRow < firstRow || (secondRow === firstRow && secondCol < firstCol)) {
|
|
3256
|
+
return null;
|
|
3257
|
+
}
|
|
3258
|
+
return [
|
|
3259
|
+
{
|
|
3260
|
+
kind: 'cell',
|
|
3261
|
+
address: { sheet: sheetId, row: firstRow, col: firstCol },
|
|
3262
|
+
sheetName,
|
|
3263
|
+
a1: this.trackedA1(firstRow, firstCol),
|
|
3264
|
+
newValue: readTrackedRuntimeCellValue(cellStore, firstCellIndex, this.engine.strings),
|
|
3265
|
+
},
|
|
3266
|
+
{
|
|
3267
|
+
kind: 'cell',
|
|
3268
|
+
address: { sheet: sheetId, row: secondRow, col: secondCol },
|
|
3269
|
+
sheetName,
|
|
3270
|
+
a1: this.trackedA1(secondRow, secondCol),
|
|
3271
|
+
newValue: readTrackedRuntimeCellValue(cellStore, secondCellIndex, this.engine.strings),
|
|
3272
|
+
},
|
|
3273
|
+
];
|
|
2526
3274
|
}
|
|
2527
3275
|
const changes = [];
|
|
3276
|
+
let previousRow = -1;
|
|
3277
|
+
let previousCol = -1;
|
|
2528
3278
|
for (let index = 0; index < event.changedCellIndices.length; index += 1) {
|
|
2529
|
-
const
|
|
2530
|
-
if (
|
|
2531
|
-
|
|
3279
|
+
const cellIndex = event.changedCellIndices[index];
|
|
3280
|
+
if (cellStore.sheetIds[cellIndex] !== sheetId) {
|
|
3281
|
+
return null;
|
|
3282
|
+
}
|
|
3283
|
+
const row = cellStore.rows[cellIndex];
|
|
3284
|
+
const col = cellStore.cols[cellIndex];
|
|
3285
|
+
if (row < previousRow || (row === previousRow && col < previousCol)) {
|
|
3286
|
+
return null;
|
|
2532
3287
|
}
|
|
3288
|
+
changes.push({
|
|
3289
|
+
kind: 'cell',
|
|
3290
|
+
address: { sheet: sheetId, row, col },
|
|
3291
|
+
sheetName,
|
|
3292
|
+
a1: this.trackedA1(row, col),
|
|
3293
|
+
newValue: readTrackedRuntimeCellValue(cellStore, cellIndex, this.engine.strings),
|
|
3294
|
+
});
|
|
3295
|
+
previousRow = row;
|
|
3296
|
+
previousCol = col;
|
|
2533
3297
|
}
|
|
2534
3298
|
return changes;
|
|
2535
3299
|
}
|
|
2536
|
-
|
|
3300
|
+
tryReadTinyTrackedEventChangesWithoutVisibility(event) {
|
|
3301
|
+
if (event.patches !== undefined &&
|
|
3302
|
+
event.invalidation !== 'full' &&
|
|
3303
|
+
event.patches.length <= TINY_TRACKED_CHANGE_LIMIT &&
|
|
3304
|
+
!event.hasInvalidatedRanges &&
|
|
3305
|
+
!event.hasInvalidatedRows &&
|
|
3306
|
+
!event.hasInvalidatedColumns) {
|
|
3307
|
+
const changes = [];
|
|
3308
|
+
let alreadySorted = true;
|
|
3309
|
+
let previousSheetId = -1;
|
|
3310
|
+
let previousSheetOrder = -1;
|
|
3311
|
+
let previousRow = -1;
|
|
3312
|
+
let previousCol = -1;
|
|
3313
|
+
for (let index = 0; index < event.patches.length; index += 1) {
|
|
3314
|
+
const patch = event.patches[index];
|
|
3315
|
+
if (!patch || patch.kind !== 'cell') {
|
|
3316
|
+
return null;
|
|
3317
|
+
}
|
|
3318
|
+
const sheetOrder = patch.address.sheet === previousSheetId ? previousSheetOrder : this.sheetRecord(patch.address.sheet).order;
|
|
3319
|
+
if (sheetOrder < previousSheetOrder ||
|
|
3320
|
+
(sheetOrder === previousSheetOrder &&
|
|
3321
|
+
(patch.address.row < previousRow || (patch.address.row === previousRow && patch.address.col < previousCol)))) {
|
|
3322
|
+
alreadySorted = false;
|
|
3323
|
+
}
|
|
3324
|
+
changes.push({
|
|
3325
|
+
kind: 'cell',
|
|
3326
|
+
address: patch.address,
|
|
3327
|
+
sheetName: patch.sheetName,
|
|
3328
|
+
a1: patch.a1,
|
|
3329
|
+
newValue: patch.newValue,
|
|
3330
|
+
});
|
|
3331
|
+
previousSheetId = patch.address.sheet;
|
|
3332
|
+
previousSheetOrder = sheetOrder;
|
|
3333
|
+
previousRow = patch.address.row;
|
|
3334
|
+
previousCol = patch.address.col;
|
|
3335
|
+
}
|
|
3336
|
+
return alreadySorted ? changes : orderWorkPaperCellChanges(changes, this.listSheetRecords(), event.explicitChangedCount);
|
|
3337
|
+
}
|
|
3338
|
+
if (event.invalidation === 'full' ||
|
|
3339
|
+
event.patches !== undefined ||
|
|
3340
|
+
event.changedCellIndices.length > TINY_TRACKED_CHANGE_LIMIT ||
|
|
3341
|
+
event.hasInvalidatedRanges ||
|
|
3342
|
+
event.hasInvalidatedRows ||
|
|
3343
|
+
event.hasInvalidatedColumns) {
|
|
3344
|
+
return null;
|
|
3345
|
+
}
|
|
3346
|
+
if (event.changedCellIndices.length === 0) {
|
|
3347
|
+
return [];
|
|
3348
|
+
}
|
|
3349
|
+
const sortedPhysicalChanges = this.readTinySortedPhysicalTrackedEventChanges(event);
|
|
3350
|
+
if (sortedPhysicalChanges) {
|
|
3351
|
+
return sortedPhysicalChanges;
|
|
3352
|
+
}
|
|
3353
|
+
const changes = [];
|
|
3354
|
+
const cellKeys = [];
|
|
3355
|
+
let alreadySorted = true;
|
|
3356
|
+
let previousSheetId = -1;
|
|
3357
|
+
let previousSheetOrder = -1;
|
|
3358
|
+
let previousRow = -1;
|
|
3359
|
+
let previousCol = -1;
|
|
3360
|
+
for (let index = 0; index < event.changedCellIndices.length; index += 1) {
|
|
3361
|
+
const change = this.readSingleTrackedCellChange(event.changedCellIndices[index]);
|
|
3362
|
+
if (!change) {
|
|
3363
|
+
continue;
|
|
3364
|
+
}
|
|
3365
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
3366
|
+
for (let priorIndex = 0; priorIndex < cellKeys.length; priorIndex += 1) {
|
|
3367
|
+
if (cellKeys[priorIndex] === cellKey) {
|
|
3368
|
+
return null;
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
cellKeys.push(cellKey);
|
|
3372
|
+
const sheetOrder = change.address.sheet === previousSheetId ? previousSheetOrder : this.sheetRecord(change.address.sheet).order;
|
|
3373
|
+
if (sheetOrder < previousSheetOrder ||
|
|
3374
|
+
(sheetOrder === previousSheetOrder &&
|
|
3375
|
+
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
3376
|
+
alreadySorted = false;
|
|
3377
|
+
}
|
|
3378
|
+
changes.push(change);
|
|
3379
|
+
previousSheetId = change.address.sheet;
|
|
3380
|
+
previousSheetOrder = sheetOrder;
|
|
3381
|
+
previousRow = change.address.row;
|
|
3382
|
+
previousCol = change.address.col;
|
|
3383
|
+
}
|
|
3384
|
+
return alreadySorted ? changes : orderWorkPaperCellChanges(changes, this.listSheetRecords(), event.explicitChangedCount);
|
|
3385
|
+
}
|
|
3386
|
+
computeCellChangesFromTrackedEvents(beforeVisibility, events, updateVisibility = true, options = {}) {
|
|
2537
3387
|
if (events.some((event) => event.invalidation === 'full')) {
|
|
2538
3388
|
return null;
|
|
2539
3389
|
}
|
|
2540
3390
|
const nextVisibility = beforeVisibility;
|
|
3391
|
+
const sheetOrders = new Map();
|
|
3392
|
+
const sheetOrderFor = (sheetId) => {
|
|
3393
|
+
const existing = sheetOrders.get(sheetId);
|
|
3394
|
+
if (existing !== undefined) {
|
|
3395
|
+
return existing;
|
|
3396
|
+
}
|
|
3397
|
+
const order = this.sheetRecord(sheetId).order;
|
|
3398
|
+
sheetOrders.set(sheetId, order);
|
|
3399
|
+
return order;
|
|
3400
|
+
};
|
|
2541
3401
|
const ensureMutableSheet = (sheetId, sheetName) => {
|
|
2542
3402
|
const existing = nextVisibility.get(sheetId);
|
|
2543
3403
|
if (existing) {
|
|
@@ -2552,17 +3412,95 @@ export class WorkPaper {
|
|
|
2552
3412
|
nextVisibility.set(sheetId, created);
|
|
2553
3413
|
return created;
|
|
2554
3414
|
};
|
|
3415
|
+
const tryReadSmallTrackedEventChanges = (event) => {
|
|
3416
|
+
if (event.invalidation === 'full' ||
|
|
3417
|
+
event.patches !== undefined ||
|
|
3418
|
+
event.changedCellIndices.length > 4 ||
|
|
3419
|
+
event.hasInvalidatedRanges ||
|
|
3420
|
+
event.hasInvalidatedRows ||
|
|
3421
|
+
event.hasInvalidatedColumns) {
|
|
3422
|
+
return null;
|
|
3423
|
+
}
|
|
3424
|
+
if (event.changedCellIndices.length === 0) {
|
|
3425
|
+
return [];
|
|
3426
|
+
}
|
|
3427
|
+
const changes = [];
|
|
3428
|
+
const cellKeys = [];
|
|
3429
|
+
let alreadySorted = true;
|
|
3430
|
+
let previousSheetOrder = -1;
|
|
3431
|
+
let previousRow = -1;
|
|
3432
|
+
let previousCol = -1;
|
|
3433
|
+
for (let index = 0; index < event.changedCellIndices.length; index += 1) {
|
|
3434
|
+
const change = this.readSingleTrackedCellChange(event.changedCellIndices[index]);
|
|
3435
|
+
if (!change) {
|
|
3436
|
+
continue;
|
|
3437
|
+
}
|
|
3438
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
3439
|
+
for (let priorIndex = 0; priorIndex < cellKeys.length; priorIndex += 1) {
|
|
3440
|
+
if (cellKeys[priorIndex] === cellKey) {
|
|
3441
|
+
return null;
|
|
3442
|
+
}
|
|
3443
|
+
}
|
|
3444
|
+
cellKeys.push(cellKey);
|
|
3445
|
+
const sheet = updateVisibility ? ensureMutableSheet(change.address.sheet, change.sheetName) : undefined;
|
|
3446
|
+
const sheetOrder = sheet?.order ?? sheetOrderFor(change.address.sheet);
|
|
3447
|
+
if (sheetOrder < previousSheetOrder ||
|
|
3448
|
+
(sheetOrder === previousSheetOrder &&
|
|
3449
|
+
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
3450
|
+
alreadySorted = false;
|
|
3451
|
+
}
|
|
3452
|
+
if (sheet) {
|
|
3453
|
+
if (change.newValue.tag === ValueTag.Empty) {
|
|
3454
|
+
sheet.cells.delete(cellKey);
|
|
3455
|
+
}
|
|
3456
|
+
else {
|
|
3457
|
+
sheet.cells.set(cellKey, change.newValue);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
changes.push(change);
|
|
3461
|
+
previousSheetOrder = sheetOrder;
|
|
3462
|
+
previousRow = change.address.row;
|
|
3463
|
+
previousCol = change.address.col;
|
|
3464
|
+
}
|
|
3465
|
+
return alreadySorted ? changes : orderWorkPaperCellChanges(changes, this.listSheetRecords(), event.explicitChangedCount);
|
|
3466
|
+
};
|
|
2555
3467
|
if (events.length === 1) {
|
|
2556
3468
|
const event = events[0];
|
|
2557
|
-
|
|
3469
|
+
if (!options.preferLazyPublicChanges) {
|
|
3470
|
+
const smallChanges = tryReadSmallTrackedEventChanges(event);
|
|
3471
|
+
if (smallChanges) {
|
|
3472
|
+
return { changes: smallChanges, nextVisibility };
|
|
3473
|
+
}
|
|
3474
|
+
}
|
|
3475
|
+
const materializedEventChanges = this.materializeTrackedEventChanges(event, !updateVisibility);
|
|
3476
|
+
const eventChanges = materializedEventChanges.changes;
|
|
3477
|
+
if (!updateVisibility && materializedEventChanges.canReusePublicChanges && materializedEventChanges.ordered) {
|
|
3478
|
+
return {
|
|
3479
|
+
changes: eventChanges,
|
|
3480
|
+
nextVisibility,
|
|
3481
|
+
};
|
|
3482
|
+
}
|
|
3483
|
+
const directChanges = [];
|
|
3484
|
+
const seenCellKeys = eventChanges.length > 4 && eventChanges.length <= 64 ? new Set() : undefined;
|
|
3485
|
+
const smallCellKeys = eventChanges.length > 1 && eventChanges.length <= 4 ? [] : undefined;
|
|
2558
3486
|
let hasDuplicateCellKey = false;
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
3487
|
+
let alreadySorted = true;
|
|
3488
|
+
let previousSheetOrder = -1;
|
|
3489
|
+
let previousRow = -1;
|
|
3490
|
+
let previousCol = -1;
|
|
3491
|
+
for (let index = 0; index < eventChanges.length; index += 1) {
|
|
3492
|
+
const change = eventChanges[index];
|
|
3493
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
3494
|
+
if (seenCellKeys) {
|
|
3495
|
+
if (seenCellKeys.has(cellKey)) {
|
|
3496
|
+
hasDuplicateCellKey = true;
|
|
3497
|
+
break;
|
|
3498
|
+
}
|
|
3499
|
+
seenCellKeys.add(cellKey);
|
|
3500
|
+
}
|
|
3501
|
+
else if (smallCellKeys) {
|
|
2563
3502
|
for (let priorIndex = 0; priorIndex < index; priorIndex += 1) {
|
|
2564
|
-
|
|
2565
|
-
if (makeCellKey(prior.address.sheet, prior.address.row, prior.address.col) === cellKey) {
|
|
3503
|
+
if (smallCellKeys[priorIndex] === cellKey) {
|
|
2566
3504
|
hasDuplicateCellKey = true;
|
|
2567
3505
|
break;
|
|
2568
3506
|
}
|
|
@@ -2570,52 +3508,37 @@ export class WorkPaper {
|
|
|
2570
3508
|
if (hasDuplicateCellKey) {
|
|
2571
3509
|
break;
|
|
2572
3510
|
}
|
|
3511
|
+
smallCellKeys[index] = cellKey;
|
|
2573
3512
|
}
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
if (seenCellKeys.has(cellKey)) {
|
|
2581
|
-
hasDuplicateCellKey = true;
|
|
2582
|
-
break;
|
|
2583
|
-
}
|
|
2584
|
-
seenCellKeys.add(cellKey);
|
|
3513
|
+
const sheet = updateVisibility ? ensureMutableSheet(change.address.sheet, change.sheetName) : undefined;
|
|
3514
|
+
const sheetOrder = sheet?.order ?? sheetOrderFor(change.address.sheet);
|
|
3515
|
+
if (sheetOrder < previousSheetOrder ||
|
|
3516
|
+
(sheetOrder === previousSheetOrder &&
|
|
3517
|
+
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
3518
|
+
alreadySorted = false;
|
|
2585
3519
|
}
|
|
2586
|
-
|
|
2587
|
-
if (!hasDuplicateCellKey) {
|
|
2588
|
-
const directChanges = [];
|
|
2589
|
-
let alreadySorted = true;
|
|
2590
|
-
let previousSheetOrder = -1;
|
|
2591
|
-
let previousRow = -1;
|
|
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;
|
|
2601
|
-
}
|
|
3520
|
+
if (sheet) {
|
|
2602
3521
|
if (change.newValue.tag === ValueTag.Empty) {
|
|
2603
3522
|
sheet.cells.delete(cellKey);
|
|
2604
3523
|
}
|
|
2605
3524
|
else {
|
|
2606
3525
|
sheet.cells.set(cellKey, change.newValue);
|
|
2607
3526
|
}
|
|
2608
|
-
|
|
3527
|
+
}
|
|
3528
|
+
directChanges[index] = materializedEventChanges.canReusePublicChanges
|
|
3529
|
+
? change
|
|
3530
|
+
: {
|
|
2609
3531
|
kind: 'cell',
|
|
2610
3532
|
address: change.address,
|
|
2611
3533
|
sheetName: change.sheetName,
|
|
2612
3534
|
a1: change.a1,
|
|
2613
3535
|
newValue: change.newValue,
|
|
2614
3536
|
};
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
3537
|
+
previousSheetOrder = sheetOrder;
|
|
3538
|
+
previousRow = change.address.row;
|
|
3539
|
+
previousCol = change.address.col;
|
|
3540
|
+
}
|
|
3541
|
+
if (!hasDuplicateCellKey) {
|
|
2619
3542
|
return {
|
|
2620
3543
|
changes: alreadySorted
|
|
2621
3544
|
? directChanges
|
|
@@ -2624,9 +3547,36 @@ export class WorkPaper {
|
|
|
2624
3547
|
};
|
|
2625
3548
|
}
|
|
2626
3549
|
}
|
|
3550
|
+
const materializedSources = updateVisibility
|
|
3551
|
+
? null
|
|
3552
|
+
: materializeTrackedIndexChangeSourcesWithMetadata(this.engine, events, {
|
|
3553
|
+
deferLazyDetach: true,
|
|
3554
|
+
lazy: options.preferLazyPublicChanges,
|
|
3555
|
+
});
|
|
3556
|
+
if (materializedSources) {
|
|
3557
|
+
this.trackLazyTrackedChanges(materializedSources.changes);
|
|
3558
|
+
if (updateVisibility) {
|
|
3559
|
+
for (const change of materializedSources.changes) {
|
|
3560
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
3561
|
+
const sheet = ensureMutableSheet(change.address.sheet, change.sheetName);
|
|
3562
|
+
if (change.newValue.tag === ValueTag.Empty) {
|
|
3563
|
+
sheet.cells.delete(cellKey);
|
|
3564
|
+
}
|
|
3565
|
+
else {
|
|
3566
|
+
sheet.cells.set(cellKey, change.newValue);
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
return {
|
|
3571
|
+
changes: materializedSources.ordered
|
|
3572
|
+
? materializedSources.changes
|
|
3573
|
+
: orderWorkPaperCellChanges(materializedSources.changes, this.listSheetRecords()),
|
|
3574
|
+
nextVisibility,
|
|
3575
|
+
};
|
|
3576
|
+
}
|
|
2627
3577
|
const latestChangesByKey = new Map();
|
|
2628
3578
|
for (const event of events) {
|
|
2629
|
-
const eventChanges = this.materializeTrackedEventChanges(event);
|
|
3579
|
+
const eventChanges = this.materializeTrackedEventChanges(event).changes;
|
|
2630
3580
|
for (let index = 0; index < eventChanges.length; index += 1) {
|
|
2631
3581
|
const change = eventChanges[index];
|
|
2632
3582
|
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
@@ -2640,14 +3590,16 @@ export class WorkPaper {
|
|
|
2640
3590
|
});
|
|
2641
3591
|
}
|
|
2642
3592
|
}
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
3593
|
+
if (updateVisibility) {
|
|
3594
|
+
for (const change of latestChangesByKey.values()) {
|
|
3595
|
+
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
3596
|
+
const sheet = ensureMutableSheet(change.address.sheet, change.sheetName);
|
|
3597
|
+
if (change.newValue.tag === ValueTag.Empty) {
|
|
3598
|
+
sheet.cells.delete(cellKey);
|
|
3599
|
+
}
|
|
3600
|
+
else {
|
|
3601
|
+
sheet.cells.set(cellKey, change.newValue);
|
|
3602
|
+
}
|
|
2651
3603
|
}
|
|
2652
3604
|
}
|
|
2653
3605
|
const directChanges = [...latestChangesByKey.values()];
|
|
@@ -2690,16 +3642,31 @@ export class WorkPaper {
|
|
|
2690
3642
|
this.batchStartNamedValues = this.namedExpressions.size > 0 ? this.ensureNamedExpressionValueCache() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2691
3643
|
this.batchUsesTrackedFastPath = false;
|
|
2692
3644
|
}
|
|
2693
|
-
computeTrackedChangesWithoutVisibilityCache(events) {
|
|
2694
|
-
|
|
3645
|
+
computeTrackedChangesWithoutVisibilityCache(events, options = {}) {
|
|
3646
|
+
if (events.length === 1) {
|
|
3647
|
+
const event = events[0];
|
|
3648
|
+
if (!options.preferLazyPublicChanges || event.changedCellIndices.length <= TINY_TRACKED_CHANGE_LIMIT) {
|
|
3649
|
+
const tinyChanges = this.tryReadTinyTrackedEventChangesWithoutVisibility(event);
|
|
3650
|
+
if (tinyChanges) {
|
|
3651
|
+
return tinyChanges;
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
}
|
|
3655
|
+
const fastPath = this.computeCellChangesFromTrackedEvents(new Map(), events, false, options);
|
|
2695
3656
|
if (!fastPath) {
|
|
2696
3657
|
throw new WorkPaperOperationError('Mutation emitted an unsupported invalidation pattern for tracked changes');
|
|
2697
3658
|
}
|
|
2698
3659
|
return fastPath.changes;
|
|
2699
3660
|
}
|
|
2700
|
-
captureTrackedChangesWithoutVisibilityCache(mutate) {
|
|
3661
|
+
captureTrackedChangesWithoutVisibilityCache(mutate, options = {}) {
|
|
2701
3662
|
this.assertNotDisposed();
|
|
2702
|
-
this.
|
|
3663
|
+
if (this.pendingLazyTrackedChanges.length > 0) {
|
|
3664
|
+
this.materializePendingLazyTrackedChanges({ preservePositions: options.preservePendingTrackedPositions });
|
|
3665
|
+
}
|
|
3666
|
+
if (this.trackedEngineEvents.length > 0) {
|
|
3667
|
+
this.drainTrackedEngineEvents();
|
|
3668
|
+
}
|
|
3669
|
+
this.retainedTrackedEngineEventIndicesDepth += 1;
|
|
2703
3670
|
try {
|
|
2704
3671
|
mutate();
|
|
2705
3672
|
}
|
|
@@ -2709,13 +3676,159 @@ export class WorkPaper {
|
|
|
2709
3676
|
}
|
|
2710
3677
|
throw new WorkPaperOperationError(this.messageOf(error, 'Mutation failed'));
|
|
2711
3678
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
3679
|
+
finally {
|
|
3680
|
+
this.retainedTrackedEngineEventIndicesDepth -= 1;
|
|
3681
|
+
}
|
|
3682
|
+
const events = this.drainTrackedEngineEvents();
|
|
3683
|
+
const directSingleLiteralChanges = this.tryBuildDirectSingleLiteralTrackedChange(events, options.singleLiteralChange);
|
|
3684
|
+
if (directSingleLiteralChanges) {
|
|
3685
|
+
if (directSingleLiteralChanges.length > 0 && this.emitter.hasListeners('valuesUpdated')) {
|
|
3686
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes: directSingleLiteralChanges } });
|
|
3687
|
+
}
|
|
3688
|
+
return directSingleLiteralChanges;
|
|
3689
|
+
}
|
|
3690
|
+
const shouldEmitValuesUpdated = this.emitter.hasListeners('valuesUpdated');
|
|
3691
|
+
const changes = this.computeTrackedChangesWithoutVisibilityCache(events, {
|
|
3692
|
+
preferLazyPublicChanges: !shouldEmitValuesUpdated,
|
|
3693
|
+
});
|
|
3694
|
+
if (changes.length > 0 && shouldEmitValuesUpdated) {
|
|
2714
3695
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2715
3696
|
}
|
|
2716
3697
|
return changes;
|
|
2717
3698
|
}
|
|
3699
|
+
tryBuildDirectSingleLiteralTrackedChange(events, expected) {
|
|
3700
|
+
if (expected === undefined || expected.cellIndex === undefined || events.length !== 1) {
|
|
3701
|
+
return null;
|
|
3702
|
+
}
|
|
3703
|
+
const event = events[0];
|
|
3704
|
+
if (event.invalidation === 'full' ||
|
|
3705
|
+
event.patches !== undefined ||
|
|
3706
|
+
event.changedCellIndices.length < 1 ||
|
|
3707
|
+
event.changedCellIndices.length > 2 ||
|
|
3708
|
+
event.changedCellIndices[0] !== expected.cellIndex ||
|
|
3709
|
+
event.hasInvalidatedRanges ||
|
|
3710
|
+
event.hasInvalidatedRows ||
|
|
3711
|
+
event.hasInvalidatedColumns) {
|
|
3712
|
+
return null;
|
|
3713
|
+
}
|
|
3714
|
+
const literalChange = {
|
|
3715
|
+
kind: 'cell',
|
|
3716
|
+
address: { sheet: expected.address.sheet, row: expected.address.row, col: expected.address.col },
|
|
3717
|
+
sheetName: expected.sheetName,
|
|
3718
|
+
a1: this.trackedA1(expected.address.row, expected.address.col),
|
|
3719
|
+
newValue: scalarValueFromLiteral(expected.value),
|
|
3720
|
+
};
|
|
3721
|
+
if (event.changedCellIndices.length === 1) {
|
|
3722
|
+
return [literalChange];
|
|
3723
|
+
}
|
|
3724
|
+
const formulaCellIndex = event.changedCellIndices[1];
|
|
3725
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
3726
|
+
if (cellStore.sheetIds[formulaCellIndex] !== expected.address.sheet) {
|
|
3727
|
+
return null;
|
|
3728
|
+
}
|
|
3729
|
+
if (!expected.isPhysicalSheet) {
|
|
3730
|
+
return null;
|
|
3731
|
+
}
|
|
3732
|
+
const formulaRow = cellStore.rows[formulaCellIndex];
|
|
3733
|
+
const formulaCol = cellStore.cols[formulaCellIndex];
|
|
3734
|
+
if (formulaRow < expected.address.row || (formulaRow === expected.address.row && formulaCol < expected.address.col)) {
|
|
3735
|
+
return null;
|
|
3736
|
+
}
|
|
3737
|
+
return [
|
|
3738
|
+
literalChange,
|
|
3739
|
+
{
|
|
3740
|
+
kind: 'cell',
|
|
3741
|
+
address: { sheet: expected.address.sheet, row: formulaRow, col: formulaCol },
|
|
3742
|
+
sheetName: expected.sheetName,
|
|
3743
|
+
a1: this.trackedA1(formulaRow, formulaCol),
|
|
3744
|
+
newValue: readTrackedRuntimeCellValue(cellStore, formulaCellIndex, this.engine.strings),
|
|
3745
|
+
},
|
|
3746
|
+
];
|
|
3747
|
+
}
|
|
3748
|
+
tryBuildDirectExistingNumericTrackedChanges(result, address, cellIndex, isPhysicalSheet, sheetName, value) {
|
|
3749
|
+
const changedCellCount = result.changedCellIndices?.length ?? result.changedCellCount ?? 0;
|
|
3750
|
+
const firstChangedCellIndex = result.changedCellIndices?.[0] ?? result.firstChangedCellIndex;
|
|
3751
|
+
if (changedCellCount < 1 || firstChangedCellIndex !== cellIndex) {
|
|
3752
|
+
return null;
|
|
3753
|
+
}
|
|
3754
|
+
const literalChange = {
|
|
3755
|
+
kind: 'cell',
|
|
3756
|
+
address: { sheet: address.sheet, row: address.row, col: address.col },
|
|
3757
|
+
sheetName,
|
|
3758
|
+
a1: this.trackedA1(address.row, address.col),
|
|
3759
|
+
newValue: { tag: ValueTag.Number, value },
|
|
3760
|
+
};
|
|
3761
|
+
if (changedCellCount === 1) {
|
|
3762
|
+
return [literalChange];
|
|
3763
|
+
}
|
|
3764
|
+
if (!isPhysicalSheet) {
|
|
3765
|
+
return null;
|
|
3766
|
+
}
|
|
3767
|
+
if (changedCellCount > 2) {
|
|
3768
|
+
const changedCellIndices = result.changedCellIndices;
|
|
3769
|
+
if (changedCellIndices === undefined) {
|
|
3770
|
+
return null;
|
|
3771
|
+
}
|
|
3772
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
3773
|
+
const changes = [];
|
|
3774
|
+
changes.length = changedCellCount;
|
|
3775
|
+
changes[0] = literalChange;
|
|
3776
|
+
let alreadySorted = true;
|
|
3777
|
+
let previousRow = address.row;
|
|
3778
|
+
let previousCol = address.col;
|
|
3779
|
+
for (let index = 1; index < changedCellCount; index += 1) {
|
|
3780
|
+
const changedCellIndex = changedCellIndices[index];
|
|
3781
|
+
if (cellStore.sheetIds[changedCellIndex] !== address.sheet) {
|
|
3782
|
+
return null;
|
|
3783
|
+
}
|
|
3784
|
+
const row = cellStore.rows[changedCellIndex];
|
|
3785
|
+
const col = cellStore.cols[changedCellIndex];
|
|
3786
|
+
if (row === undefined || col === undefined) {
|
|
3787
|
+
return null;
|
|
3788
|
+
}
|
|
3789
|
+
if (row < previousRow || (row === previousRow && col < previousCol)) {
|
|
3790
|
+
alreadySorted = false;
|
|
3791
|
+
}
|
|
3792
|
+
changes[index] = {
|
|
3793
|
+
kind: 'cell',
|
|
3794
|
+
address: { sheet: address.sheet, row, col },
|
|
3795
|
+
sheetName,
|
|
3796
|
+
a1: this.trackedA1(row, col),
|
|
3797
|
+
newValue: readTrackedRuntimeCellValue(cellStore, changedCellIndex, this.engine.strings),
|
|
3798
|
+
};
|
|
3799
|
+
previousRow = row;
|
|
3800
|
+
previousCol = col;
|
|
3801
|
+
}
|
|
3802
|
+
return alreadySorted ? changes : orderWorkPaperCellChanges(changes, this.listSheetRecords(), result.explicitChangedCount);
|
|
3803
|
+
}
|
|
3804
|
+
const formulaCellIndex = result.changedCellIndices?.[1] ?? result.secondChangedCellIndex;
|
|
3805
|
+
if (formulaCellIndex === undefined) {
|
|
3806
|
+
return null;
|
|
3807
|
+
}
|
|
3808
|
+
const cellStore = this.engine.workbook.cellStore;
|
|
3809
|
+
if (cellStore.sheetIds[formulaCellIndex] !== address.sheet) {
|
|
3810
|
+
return null;
|
|
3811
|
+
}
|
|
3812
|
+
const formulaRow = result.secondChangedRow ?? cellStore.rows[formulaCellIndex];
|
|
3813
|
+
const formulaCol = result.secondChangedCol ?? cellStore.cols[formulaCellIndex];
|
|
3814
|
+
if (formulaRow < address.row || (formulaRow === address.row && formulaCol < address.col)) {
|
|
3815
|
+
return null;
|
|
3816
|
+
}
|
|
3817
|
+
return [
|
|
3818
|
+
literalChange,
|
|
3819
|
+
{
|
|
3820
|
+
kind: 'cell',
|
|
3821
|
+
address: { sheet: address.sheet, row: formulaRow, col: formulaCol },
|
|
3822
|
+
sheetName,
|
|
3823
|
+
a1: this.trackedA1(formulaRow, formulaCol),
|
|
3824
|
+
newValue: result.secondChangedNumericValue === undefined
|
|
3825
|
+
? readTrackedRuntimeCellValue(cellStore, formulaCellIndex, this.engine.strings)
|
|
3826
|
+
: { tag: ValueTag.Number, value: result.secondChangedNumericValue },
|
|
3827
|
+
},
|
|
3828
|
+
];
|
|
3829
|
+
}
|
|
2718
3830
|
batchStructuralChanges(batchOperations) {
|
|
3831
|
+
this.materializePendingLazyTrackedChanges();
|
|
2719
3832
|
if (!this.canUseTrackedStructuralFastPath()) {
|
|
2720
3833
|
this.downgradeTrackedBatchFastPath();
|
|
2721
3834
|
return this.batch(batchOperations);
|
|
@@ -2725,16 +3838,19 @@ export class WorkPaper {
|
|
|
2725
3838
|
this.drainTrackedEngineEvents();
|
|
2726
3839
|
this.batchDepth += 1;
|
|
2727
3840
|
try {
|
|
2728
|
-
batchOperations
|
|
3841
|
+
this.withRetainedTrackedEngineEventIndices(batchOperations);
|
|
2729
3842
|
}
|
|
2730
3843
|
finally {
|
|
2731
3844
|
this.batchDepth -= 1;
|
|
2732
3845
|
this.flushPendingBatchOps();
|
|
2733
3846
|
this.mergeUndoHistory(undoStackStart);
|
|
2734
3847
|
}
|
|
2735
|
-
const
|
|
3848
|
+
const shouldEmitValuesUpdated = this.emitter.hasListeners('valuesUpdated');
|
|
3849
|
+
const changes = this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents(), {
|
|
3850
|
+
preferLazyPublicChanges: !shouldEmitValuesUpdated,
|
|
3851
|
+
});
|
|
2736
3852
|
this.flushQueuedEvents();
|
|
2737
|
-
if (changes.length > 0) {
|
|
3853
|
+
if (changes.length > 0 && shouldEmitValuesUpdated) {
|
|
2738
3854
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2739
3855
|
}
|
|
2740
3856
|
return changes;
|
|
@@ -2756,8 +3872,9 @@ export class WorkPaper {
|
|
|
2756
3872
|
this.namedExpressionValueCache = afterNames;
|
|
2757
3873
|
return hasNamedExpressions ? [...cellChanges, ...this.computeNamedExpressionChanges(beforeNames, afterNames)] : cellChanges;
|
|
2758
3874
|
}
|
|
2759
|
-
captureChanges(semanticEvent, mutate) {
|
|
3875
|
+
captureChanges(semanticEvent, mutate, options = {}) {
|
|
2760
3876
|
this.assertNotDisposed();
|
|
3877
|
+
this.materializePendingLazyTrackedChanges({ preservePositions: options.preservePendingTrackedPositions });
|
|
2761
3878
|
this.downgradeTrackedBatchFastPath();
|
|
2762
3879
|
if (semanticEvent !== undefined) {
|
|
2763
3880
|
this.flushPendingBatchOps();
|
|
@@ -2810,7 +3927,7 @@ export class WorkPaper {
|
|
|
2810
3927
|
this.emitter.emitDetailed(event);
|
|
2811
3928
|
}
|
|
2812
3929
|
}
|
|
2813
|
-
if (!this.shouldSuppressEvents() && changes.length > 0) {
|
|
3930
|
+
if (!this.shouldSuppressEvents() && changes.length > 0 && this.emitter.hasListeners('valuesUpdated')) {
|
|
2814
3931
|
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2815
3932
|
}
|
|
2816
3933
|
return changes;
|
|
@@ -2839,6 +3956,10 @@ export class WorkPaper {
|
|
|
2839
3956
|
}
|
|
2840
3957
|
return stack;
|
|
2841
3958
|
}
|
|
3959
|
+
historyTopIsCellMutations(stack) {
|
|
3960
|
+
const kind = stack.at(-1)?.forward.kind;
|
|
3961
|
+
return kind === 'cell-mutations' || kind === 'single-existing-numeric-cell-mutation';
|
|
3962
|
+
}
|
|
2842
3963
|
clearHistoryStacks() {
|
|
2843
3964
|
this.getUndoStack().length = 0;
|
|
2844
3965
|
this.getRedoStack().length = 0;
|
|
@@ -2849,6 +3970,19 @@ export class WorkPaper {
|
|
|
2849
3970
|
return record.ops;
|
|
2850
3971
|
case 'single-op':
|
|
2851
3972
|
return [record.op];
|
|
3973
|
+
case 'single-existing-numeric-cell-mutation': {
|
|
3974
|
+
const sheetName = this.getSheetName(record.sheetId);
|
|
3975
|
+
return sheetName
|
|
3976
|
+
? [
|
|
3977
|
+
{
|
|
3978
|
+
kind: 'setCellValue',
|
|
3979
|
+
sheetName,
|
|
3980
|
+
address: formatAddress(record.row, record.col),
|
|
3981
|
+
value: record.value,
|
|
3982
|
+
},
|
|
3983
|
+
]
|
|
3984
|
+
: [];
|
|
3985
|
+
}
|
|
2852
3986
|
case 'cell-mutations':
|
|
2853
3987
|
return record.refs.flatMap((ref) => {
|
|
2854
3988
|
const sheetName = this.getSheetName(ref.sheetId);
|
|
@@ -3024,6 +4158,7 @@ export class WorkPaper {
|
|
|
3024
4158
|
});
|
|
3025
4159
|
}
|
|
3026
4160
|
replaceSheetContentInternal(sheetId, content, options) {
|
|
4161
|
+
const dimensions = inspectSheetDimensionsWithinLimits(this.sheetName(sheetId), content, this.config);
|
|
3027
4162
|
replaceWorkPaperSheetContent({
|
|
3028
4163
|
sheetId,
|
|
3029
4164
|
sheetName: this.sheetName(sheetId),
|
|
@@ -3037,9 +4172,10 @@ export class WorkPaper {
|
|
|
3037
4172
|
getUndoStackLength: () => this.getUndoStack().length,
|
|
3038
4173
|
mergeUndoHistory: (undoStackStart) => this.mergeUndoHistory(undoStackStart),
|
|
3039
4174
|
});
|
|
4175
|
+
this.cacheInitializedSheetDimensions(sheetId, dimensions);
|
|
3040
4176
|
}
|
|
3041
4177
|
applyRawContent(address, content) {
|
|
3042
|
-
const
|
|
4178
|
+
const cellIndex = this.getVisibleCellIndex(address.sheet, address.row, address.col);
|
|
3043
4179
|
const mutation = content === null
|
|
3044
4180
|
? { kind: 'clearCell', row: address.row, col: address.col }
|
|
3045
4181
|
: typeof content === 'string' && content.trim().startsWith('=')
|
|
@@ -3055,9 +4191,9 @@ export class WorkPaper {
|
|
|
3055
4191
|
col: address.col,
|
|
3056
4192
|
value: content,
|
|
3057
4193
|
};
|
|
3058
|
-
this.applyCellMutationRefs([{ sheetId: address.sheet, mutation }], {
|
|
4194
|
+
this.applyCellMutationRefs([{ sheetId: address.sheet, mutation, ...(cellIndex !== undefined ? { cellIndex } : {}) }], {
|
|
3059
4195
|
captureUndo: true,
|
|
3060
|
-
potentialNewCells: content === null ||
|
|
4196
|
+
potentialNewCells: content === null || cellIndex !== undefined ? 0 : 1,
|
|
3061
4197
|
source: 'local',
|
|
3062
4198
|
returnUndoOps: false,
|
|
3063
4199
|
reuseRefs: true,
|