@bilig/headless 0.1.83 → 0.1.85
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/README.md +5 -5
- package/dist/change-order.d.ts +1 -1
- package/dist/change-order.js +2 -2
- package/dist/change-order.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/initial-sheet-load.d.ts +2 -2
- package/dist/initial-sheet-load.js +9 -9
- package/dist/initial-sheet-load.js.map +1 -1
- package/dist/matrix-mutation-plan.d.ts +2 -2
- package/dist/matrix-mutation-plan.js +6 -7
- package/dist/matrix-mutation-plan.js.map +1 -1
- package/dist/persistence.d.ts +2 -2
- package/dist/persistence.js +66 -77
- package/dist/persistence.js.map +1 -1
- package/dist/tracked-engine-event-refs.d.ts +2 -2
- package/dist/tracked-engine-event-refs.js +2 -4
- package/dist/tracked-engine-event-refs.js.map +1 -1
- package/dist/work-paper-errors.d.ts +1 -1
- package/dist/work-paper-errors.js +36 -38
- package/dist/work-paper-errors.js.map +1 -1
- package/dist/work-paper-runtime.d.ts +4 -4
- package/dist/work-paper-runtime.js +413 -495
- package/dist/work-paper-runtime.js.map +1 -1
- package/dist/work-paper-scratch-evaluator.d.ts +2 -2
- package/dist/work-paper-scratch-evaluator.js +3 -3
- package/dist/work-paper-scratch-evaluator.js.map +1 -1
- package/dist/work-paper-sheet-replacement.d.ts +1 -1
- package/dist/work-paper-sheet-replacement.js +2 -2
- package/dist/work-paper-sheet-replacement.js.map +1 -1
- package/dist/work-paper-types.d.ts +19 -19
- package/dist/work-paper.d.ts +3 -3
- package/dist/work-paper.js +3 -3
- package/dist/work-paper.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { SpreadsheetEngine, makeCellKey
|
|
2
|
-
import { ErrorCode, MAX_COLS, MAX_ROWS, ValueTag, } from
|
|
3
|
-
import { excelSerialToDateParts, formatAddress, formatRangeAddress, installExternalFunctionAdapter, isArrayValue, isCellReferenceText, parseCellAddress, parseFormula, parseRangeAddress, serializeFormula, translateFormulaReferences, } from
|
|
4
|
-
import { loadInitialMixedSheet, tryLoadInitialLiteralSheet } from
|
|
5
|
-
import { orderWorkPaperCellChanges } from
|
|
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
|
|
7
|
-
import { buildMatrixMutationPlan } from
|
|
8
|
-
import { captureTrackedEngineEvent } from
|
|
9
|
-
import { calculateWorkPaperFormulaInScratchWorkbook } from
|
|
10
|
-
import { replaceWorkPaperSheetContent } from
|
|
1
|
+
import { SpreadsheetEngine, makeCellKey } from '@bilig/core';
|
|
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';
|
|
5
|
+
import { orderWorkPaperCellChanges } from './change-order.js';
|
|
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
|
+
import { buildMatrixMutationPlan } from './matrix-mutation-plan.js';
|
|
8
|
+
import { captureTrackedEngineEvent } from './tracked-engine-event-refs.js';
|
|
9
|
+
import { calculateWorkPaperFormulaInScratchWorkbook } from './work-paper-scratch-evaluator.js';
|
|
10
|
+
import { replaceWorkPaperSheetContent } from './work-paper-sheet-replacement.js';
|
|
11
11
|
const EMPTY_NAMED_EXPRESSION_VALUES = new Map();
|
|
12
12
|
const VISIBILITY_SHEET_STRIDE = MAX_ROWS * MAX_COLS;
|
|
13
13
|
const DEFAULT_CONFIG = Object.freeze({
|
|
14
14
|
accentSensitive: false,
|
|
15
15
|
caseSensitive: false,
|
|
16
|
-
caseFirst:
|
|
16
|
+
caseFirst: 'false',
|
|
17
17
|
chooseAddressMappingPolicy: undefined,
|
|
18
18
|
context: undefined,
|
|
19
|
-
currencySymbol: [
|
|
19
|
+
currencySymbol: ['$'],
|
|
20
20
|
dateFormats: [],
|
|
21
|
-
functionArgSeparator:
|
|
22
|
-
decimalSeparator:
|
|
21
|
+
functionArgSeparator: ',',
|
|
22
|
+
decimalSeparator: '.',
|
|
23
23
|
evaluateNullToZero: true,
|
|
24
24
|
functionPlugins: [],
|
|
25
25
|
ignorePunctuation: false,
|
|
26
|
-
language:
|
|
27
|
-
ignoreWhiteSpace:
|
|
26
|
+
language: 'enGB',
|
|
27
|
+
ignoreWhiteSpace: 'standard',
|
|
28
28
|
leapYear1900: true,
|
|
29
|
-
licenseKey:
|
|
30
|
-
localeLang:
|
|
29
|
+
licenseKey: 'internal',
|
|
30
|
+
localeLang: 'en-US',
|
|
31
31
|
matchWholeCell: true,
|
|
32
|
-
arrayColumnSeparator:
|
|
33
|
-
arrayRowSeparator:
|
|
32
|
+
arrayColumnSeparator: ',',
|
|
33
|
+
arrayRowSeparator: ';',
|
|
34
34
|
maxRows: MAX_ROWS,
|
|
35
35
|
maxColumns: MAX_COLS,
|
|
36
36
|
nullDate: { year: 1899, month: 12, day: 30 },
|
|
@@ -41,7 +41,7 @@ const DEFAULT_CONFIG = Object.freeze({
|
|
|
41
41
|
stringifyDateTime: undefined,
|
|
42
42
|
stringifyDuration: undefined,
|
|
43
43
|
smartRounding: true,
|
|
44
|
-
thousandSeparator:
|
|
44
|
+
thousandSeparator: ',',
|
|
45
45
|
timeFormats: [],
|
|
46
46
|
useArrayArithmetic: true,
|
|
47
47
|
useColumnIndex: false,
|
|
@@ -51,83 +51,83 @@ const DEFAULT_CONFIG = Object.freeze({
|
|
|
51
51
|
useWildcards: true,
|
|
52
52
|
});
|
|
53
53
|
const WORKPAPER_CONFIG_KEYS = [
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
54
|
+
'accentSensitive',
|
|
55
|
+
'caseSensitive',
|
|
56
|
+
'caseFirst',
|
|
57
|
+
'chooseAddressMappingPolicy',
|
|
58
|
+
'context',
|
|
59
|
+
'currencySymbol',
|
|
60
|
+
'dateFormats',
|
|
61
|
+
'functionArgSeparator',
|
|
62
|
+
'decimalSeparator',
|
|
63
|
+
'evaluateNullToZero',
|
|
64
|
+
'functionPlugins',
|
|
65
|
+
'ignorePunctuation',
|
|
66
|
+
'language',
|
|
67
|
+
'ignoreWhiteSpace',
|
|
68
|
+
'leapYear1900',
|
|
69
|
+
'licenseKey',
|
|
70
|
+
'localeLang',
|
|
71
|
+
'matchWholeCell',
|
|
72
|
+
'arrayColumnSeparator',
|
|
73
|
+
'arrayRowSeparator',
|
|
74
|
+
'maxRows',
|
|
75
|
+
'maxColumns',
|
|
76
|
+
'nullDate',
|
|
77
|
+
'nullYear',
|
|
78
|
+
'parseDateTime',
|
|
79
|
+
'precisionEpsilon',
|
|
80
|
+
'precisionRounding',
|
|
81
|
+
'stringifyDateTime',
|
|
82
|
+
'stringifyDuration',
|
|
83
|
+
'smartRounding',
|
|
84
|
+
'thousandSeparator',
|
|
85
|
+
'timeFormats',
|
|
86
|
+
'useArrayArithmetic',
|
|
87
|
+
'useColumnIndex',
|
|
88
|
+
'useStats',
|
|
89
|
+
'undoLimit',
|
|
90
|
+
'useRegularExpressions',
|
|
91
|
+
'useWildcards',
|
|
92
92
|
];
|
|
93
93
|
const WORKPAPER_PUBLIC_ERROR_NAMES = new Set([
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
94
|
+
'WorkPaperConfigValueTooBigError',
|
|
95
|
+
'WorkPaperConfigValueTooSmallError',
|
|
96
|
+
'WorkPaperEvaluationSuspendedError',
|
|
97
|
+
'WorkPaperExpectedOneOfValuesError',
|
|
98
|
+
'WorkPaperExpectedValueOfTypeError',
|
|
99
|
+
'WorkPaperFunctionPluginValidationError',
|
|
100
|
+
'WorkPaperInvalidAddressError',
|
|
101
|
+
'WorkPaperInvalidArgumentsError',
|
|
102
|
+
'WorkPaperLanguageAlreadyRegisteredError',
|
|
103
|
+
'WorkPaperLanguageNotRegisteredError',
|
|
104
|
+
'WorkPaperMissingTranslationError',
|
|
105
|
+
'WorkPaperNamedExpressionDoesNotExistError',
|
|
106
|
+
'WorkPaperNamedExpressionNameIsAlreadyTakenError',
|
|
107
|
+
'WorkPaperNamedExpressionNameIsInvalidError',
|
|
108
|
+
'WorkPaperNoOperationToRedoError',
|
|
109
|
+
'WorkPaperNoOperationToUndoError',
|
|
110
|
+
'WorkPaperNoRelativeAddressesAllowedError',
|
|
111
|
+
'WorkPaperNoSheetWithIdError',
|
|
112
|
+
'WorkPaperNoSheetWithNameError',
|
|
113
|
+
'WorkPaperNotAFormulaError',
|
|
114
|
+
'WorkPaperNothingToPasteError',
|
|
115
|
+
'WorkPaperProtectedFunctionTranslationError',
|
|
116
|
+
'WorkPaperSheetNameAlreadyTakenError',
|
|
117
|
+
'WorkPaperSheetSizeLimitExceededError',
|
|
118
|
+
'WorkPaperSourceLocationHasArrayError',
|
|
119
|
+
'WorkPaperTargetLocationHasArrayError',
|
|
120
|
+
'WorkPaperUnableToParseError',
|
|
121
|
+
'WorkPaperConfigError',
|
|
122
|
+
'WorkPaperSheetError',
|
|
123
|
+
'WorkPaperNamedExpressionError',
|
|
124
|
+
'WorkPaperClipboardError',
|
|
125
|
+
'WorkPaperParseError',
|
|
126
|
+
'WorkPaperOperationError',
|
|
127
127
|
]);
|
|
128
|
-
const WORKPAPER_VERSION =
|
|
129
|
-
const WORKPAPER_BUILD_DATE =
|
|
130
|
-
const WORKPAPER_RELEASE_DATE =
|
|
128
|
+
const WORKPAPER_VERSION = '0.1.2';
|
|
129
|
+
const WORKPAPER_BUILD_DATE = '2026-04-10';
|
|
130
|
+
const WORKPAPER_RELEASE_DATE = '2026-04-10';
|
|
131
131
|
const globalCustomFunctions = new Map();
|
|
132
132
|
let customAdapterInstalled = false;
|
|
133
133
|
let nextWorkbookId = 1;
|
|
@@ -136,14 +136,14 @@ function ensureCustomAdapterInstalled() {
|
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
installExternalFunctionAdapter({
|
|
139
|
-
surface:
|
|
139
|
+
surface: 'host',
|
|
140
140
|
resolveFunction(name) {
|
|
141
141
|
const implementation = globalCustomFunctions.get(name.trim().toUpperCase());
|
|
142
142
|
if (!implementation) {
|
|
143
143
|
return undefined;
|
|
144
144
|
}
|
|
145
145
|
return {
|
|
146
|
-
kind:
|
|
146
|
+
kind: 'scalar',
|
|
147
147
|
implementation: (...args) => {
|
|
148
148
|
const result = implementation(...args);
|
|
149
149
|
if (!result) {
|
|
@@ -159,10 +159,7 @@ function ensureCustomAdapterInstalled() {
|
|
|
159
159
|
function clonePluginDefinition(plugin) {
|
|
160
160
|
return {
|
|
161
161
|
...plugin,
|
|
162
|
-
implementedFunctions: Object.fromEntries(Object.entries(plugin.implementedFunctions).map(([name, metadata]) => [
|
|
163
|
-
name,
|
|
164
|
-
{ ...metadata },
|
|
165
|
-
])),
|
|
162
|
+
implementedFunctions: Object.fromEntries(Object.entries(plugin.implementedFunctions).map(([name, metadata]) => [name, { ...metadata }])),
|
|
166
163
|
aliases: plugin.aliases ? { ...plugin.aliases } : undefined,
|
|
167
164
|
functions: plugin.functions ? { ...plugin.functions } : undefined,
|
|
168
165
|
};
|
|
@@ -170,15 +167,11 @@ function clonePluginDefinition(plugin) {
|
|
|
170
167
|
function cloneConfig(config) {
|
|
171
168
|
return {
|
|
172
169
|
...config,
|
|
173
|
-
chooseAddressMappingPolicy: config.chooseAddressMappingPolicy
|
|
174
|
-
? { ...config.chooseAddressMappingPolicy }
|
|
175
|
-
: undefined,
|
|
170
|
+
chooseAddressMappingPolicy: config.chooseAddressMappingPolicy ? { ...config.chooseAddressMappingPolicy } : undefined,
|
|
176
171
|
context: config.context !== undefined ? structuredClone(config.context) : undefined,
|
|
177
172
|
currencySymbol: config.currencySymbol ? [...config.currencySymbol] : undefined,
|
|
178
173
|
dateFormats: config.dateFormats ? [...config.dateFormats] : undefined,
|
|
179
|
-
functionPlugins: config.functionPlugins
|
|
180
|
-
? config.functionPlugins.map((plugin) => clonePluginDefinition(plugin))
|
|
181
|
-
: undefined,
|
|
174
|
+
functionPlugins: config.functionPlugins ? config.functionPlugins.map((plugin) => clonePluginDefinition(plugin)) : undefined,
|
|
182
175
|
nullDate: config.nullDate ? { ...config.nullDate } : undefined,
|
|
183
176
|
timeFormats: config.timeFormats ? [...config.timeFormats] : undefined,
|
|
184
177
|
};
|
|
@@ -193,10 +186,10 @@ function scalarValueFromLiteral(value) {
|
|
|
193
186
|
if (value === null) {
|
|
194
187
|
return emptyValue();
|
|
195
188
|
}
|
|
196
|
-
if (typeof value ===
|
|
189
|
+
if (typeof value === 'number') {
|
|
197
190
|
return { tag: ValueTag.Number, value };
|
|
198
191
|
}
|
|
199
|
-
if (typeof value ===
|
|
192
|
+
if (typeof value === 'boolean') {
|
|
200
193
|
return { tag: ValueTag.Boolean, value };
|
|
201
194
|
}
|
|
202
195
|
return { tag: ValueTag.String, value, stringId: 0 };
|
|
@@ -265,13 +258,13 @@ function normalizeName(name) {
|
|
|
265
258
|
return name.trim().toUpperCase();
|
|
266
259
|
}
|
|
267
260
|
function makeNamedExpressionKey(name, scope) {
|
|
268
|
-
return `${scope ??
|
|
261
|
+
return `${scope ?? 'workbook'}:${normalizeName(name)}`;
|
|
269
262
|
}
|
|
270
263
|
function makeInternalScopedName(scope, name) {
|
|
271
264
|
return `__BILIG_WORKPAPER_SCOPE_${scope}_${normalizeName(name)}`;
|
|
272
265
|
}
|
|
273
266
|
function isFormulaContent(content) {
|
|
274
|
-
return typeof content ===
|
|
267
|
+
return typeof content === 'string' && content.trim().startsWith('=');
|
|
275
268
|
}
|
|
276
269
|
function isCellValueMatrix(value) {
|
|
277
270
|
return Array.isArray(value);
|
|
@@ -283,16 +276,13 @@ function matrixContainsFormulaContent(content) {
|
|
|
283
276
|
return content.some((row) => row.some((cell) => isFormulaContent(cell)));
|
|
284
277
|
}
|
|
285
278
|
function isDeferredBatchLiteralContent(content) {
|
|
286
|
-
return
|
|
287
|
-
typeof content === "boolean" ||
|
|
288
|
-
typeof content === "number" ||
|
|
289
|
-
typeof content === "string");
|
|
279
|
+
return content === null || typeof content === 'boolean' || typeof content === 'number' || typeof content === 'string';
|
|
290
280
|
}
|
|
291
281
|
function canUseInitialMixedSheetFastPath(content) {
|
|
292
|
-
return content.some((row) => row.some((value) => typeof value ===
|
|
282
|
+
return content.some((row) => row.some((value) => typeof value === 'string' && value.trim().startsWith('=')));
|
|
293
283
|
}
|
|
294
284
|
function stripLeadingEquals(formula) {
|
|
295
|
-
return formula.trim().startsWith(
|
|
285
|
+
return formula.trim().startsWith('=') ? formula.trim().slice(1) : formula.trim();
|
|
296
286
|
}
|
|
297
287
|
function assertRowAndColumn(value, label) {
|
|
298
288
|
if (!Number.isInteger(value) || value < 0) {
|
|
@@ -300,18 +290,18 @@ function assertRowAndColumn(value, label) {
|
|
|
300
290
|
}
|
|
301
291
|
}
|
|
302
292
|
function assertRange(range) {
|
|
303
|
-
assertRowAndColumn(range.start.sheet,
|
|
304
|
-
assertRowAndColumn(range.start.row,
|
|
305
|
-
assertRowAndColumn(range.start.col,
|
|
306
|
-
assertRowAndColumn(range.end.sheet,
|
|
307
|
-
assertRowAndColumn(range.end.row,
|
|
308
|
-
assertRowAndColumn(range.end.col,
|
|
293
|
+
assertRowAndColumn(range.start.sheet, 'start.sheet');
|
|
294
|
+
assertRowAndColumn(range.start.row, 'start.row');
|
|
295
|
+
assertRowAndColumn(range.start.col, 'start.col');
|
|
296
|
+
assertRowAndColumn(range.end.sheet, 'end.sheet');
|
|
297
|
+
assertRowAndColumn(range.end.row, 'end.row');
|
|
298
|
+
assertRowAndColumn(range.end.col, 'end.col');
|
|
309
299
|
if (range.start.sheet !== range.end.sheet) {
|
|
310
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
300
|
+
throw new WorkPaperInvalidArgumentsError('Ranges must stay on a single sheet');
|
|
311
301
|
}
|
|
312
302
|
}
|
|
313
303
|
function isCellRange(value) {
|
|
314
|
-
return
|
|
304
|
+
return 'start' in value && 'end' in value;
|
|
315
305
|
}
|
|
316
306
|
function cloneCellValue(value) {
|
|
317
307
|
switch (value.tag) {
|
|
@@ -332,35 +322,35 @@ function cloneCellValue(value) {
|
|
|
332
322
|
function transformFormulaNode(node, transform) {
|
|
333
323
|
const current = transform(node);
|
|
334
324
|
switch (current.kind) {
|
|
335
|
-
case
|
|
336
|
-
case
|
|
337
|
-
case
|
|
338
|
-
case
|
|
339
|
-
case
|
|
340
|
-
case
|
|
341
|
-
case
|
|
342
|
-
case
|
|
343
|
-
case
|
|
344
|
-
case
|
|
345
|
-
case
|
|
325
|
+
case 'BooleanLiteral':
|
|
326
|
+
case 'CellRef':
|
|
327
|
+
case 'ColumnRef':
|
|
328
|
+
case 'ErrorLiteral':
|
|
329
|
+
case 'NameRef':
|
|
330
|
+
case 'NumberLiteral':
|
|
331
|
+
case 'RangeRef':
|
|
332
|
+
case 'RowRef':
|
|
333
|
+
case 'SpillRef':
|
|
334
|
+
case 'StringLiteral':
|
|
335
|
+
case 'StructuredRef':
|
|
346
336
|
return current;
|
|
347
|
-
case
|
|
337
|
+
case 'UnaryExpr':
|
|
348
338
|
return {
|
|
349
339
|
...current,
|
|
350
340
|
argument: transformFormulaNode(current.argument, transform),
|
|
351
341
|
};
|
|
352
|
-
case
|
|
342
|
+
case 'BinaryExpr':
|
|
353
343
|
return {
|
|
354
344
|
...current,
|
|
355
345
|
left: transformFormulaNode(current.left, transform),
|
|
356
346
|
right: transformFormulaNode(current.right, transform),
|
|
357
347
|
};
|
|
358
|
-
case
|
|
348
|
+
case 'CallExpr':
|
|
359
349
|
return {
|
|
360
350
|
...current,
|
|
361
351
|
args: current.args.map((argument) => transformFormulaNode(argument, transform)),
|
|
362
352
|
};
|
|
363
|
-
case
|
|
353
|
+
case 'InvokeExpr':
|
|
364
354
|
return {
|
|
365
355
|
...current,
|
|
366
356
|
callee: transformFormulaNode(current.callee, transform),
|
|
@@ -372,33 +362,33 @@ function transformFormulaNode(node, transform) {
|
|
|
372
362
|
}
|
|
373
363
|
function collectFormulaNameRefs(node, output) {
|
|
374
364
|
switch (node.kind) {
|
|
375
|
-
case
|
|
376
|
-
case
|
|
377
|
-
case
|
|
378
|
-
case
|
|
379
|
-
case
|
|
380
|
-
if (node.kind ===
|
|
365
|
+
case 'BooleanLiteral':
|
|
366
|
+
case 'CellRef':
|
|
367
|
+
case 'ColumnRef':
|
|
368
|
+
case 'ErrorLiteral':
|
|
369
|
+
case 'NameRef':
|
|
370
|
+
if (node.kind === 'NameRef') {
|
|
381
371
|
output.add(node.name);
|
|
382
372
|
}
|
|
383
373
|
return;
|
|
384
|
-
case
|
|
385
|
-
case
|
|
386
|
-
case
|
|
387
|
-
case
|
|
388
|
-
case
|
|
389
|
-
case
|
|
374
|
+
case 'NumberLiteral':
|
|
375
|
+
case 'RangeRef':
|
|
376
|
+
case 'RowRef':
|
|
377
|
+
case 'SpillRef':
|
|
378
|
+
case 'StringLiteral':
|
|
379
|
+
case 'StructuredRef':
|
|
390
380
|
return;
|
|
391
|
-
case
|
|
381
|
+
case 'UnaryExpr':
|
|
392
382
|
collectFormulaNameRefs(node.argument, output);
|
|
393
383
|
return;
|
|
394
|
-
case
|
|
384
|
+
case 'BinaryExpr':
|
|
395
385
|
collectFormulaNameRefs(node.left, output);
|
|
396
386
|
collectFormulaNameRefs(node.right, output);
|
|
397
387
|
return;
|
|
398
|
-
case
|
|
388
|
+
case 'CallExpr':
|
|
399
389
|
node.args.forEach((argument) => collectFormulaNameRefs(argument, output));
|
|
400
390
|
return;
|
|
401
|
-
case
|
|
391
|
+
case 'InvokeExpr':
|
|
402
392
|
collectFormulaNameRefs(node.callee, output);
|
|
403
393
|
node.args.forEach((argument) => collectFormulaNameRefs(argument, output));
|
|
404
394
|
return;
|
|
@@ -417,37 +407,36 @@ function isAbsoluteColumnReference(value) {
|
|
|
417
407
|
}
|
|
418
408
|
function formulaHasRelativeReferences(node) {
|
|
419
409
|
switch (node.kind) {
|
|
420
|
-
case
|
|
421
|
-
case
|
|
422
|
-
case
|
|
423
|
-
case
|
|
424
|
-
case
|
|
425
|
-
case
|
|
410
|
+
case 'BooleanLiteral':
|
|
411
|
+
case 'ErrorLiteral':
|
|
412
|
+
case 'NameRef':
|
|
413
|
+
case 'NumberLiteral':
|
|
414
|
+
case 'StringLiteral':
|
|
415
|
+
case 'StructuredRef':
|
|
426
416
|
return false;
|
|
427
|
-
case
|
|
428
|
-
case
|
|
417
|
+
case 'CellRef':
|
|
418
|
+
case 'SpillRef':
|
|
429
419
|
return !isAbsoluteCellReference(node.ref);
|
|
430
|
-
case
|
|
420
|
+
case 'RowRef':
|
|
431
421
|
return !isAbsoluteRowReference(node.ref);
|
|
432
|
-
case
|
|
422
|
+
case 'ColumnRef':
|
|
433
423
|
return !isAbsoluteColumnReference(node.ref);
|
|
434
|
-
case
|
|
435
|
-
if (node.refKind ===
|
|
424
|
+
case 'RangeRef':
|
|
425
|
+
if (node.refKind === 'cells') {
|
|
436
426
|
return !isAbsoluteCellReference(node.start) || !isAbsoluteCellReference(node.end);
|
|
437
427
|
}
|
|
438
|
-
if (node.refKind ===
|
|
428
|
+
if (node.refKind === 'rows') {
|
|
439
429
|
return !isAbsoluteRowReference(node.start) || !isAbsoluteRowReference(node.end);
|
|
440
430
|
}
|
|
441
431
|
return !isAbsoluteColumnReference(node.start) || !isAbsoluteColumnReference(node.end);
|
|
442
|
-
case
|
|
432
|
+
case 'UnaryExpr':
|
|
443
433
|
return formulaHasRelativeReferences(node.argument);
|
|
444
|
-
case
|
|
434
|
+
case 'BinaryExpr':
|
|
445
435
|
return formulaHasRelativeReferences(node.left) || formulaHasRelativeReferences(node.right);
|
|
446
|
-
case
|
|
436
|
+
case 'CallExpr':
|
|
447
437
|
return node.args.some((argument) => formulaHasRelativeReferences(argument));
|
|
448
|
-
case
|
|
449
|
-
return
|
|
450
|
-
node.args.some((argument) => formulaHasRelativeReferences(argument)));
|
|
438
|
+
case 'InvokeExpr':
|
|
439
|
+
return formulaHasRelativeReferences(node.callee) || node.args.some((argument) => formulaHasRelativeReferences(argument));
|
|
451
440
|
default:
|
|
452
441
|
return false;
|
|
453
442
|
}
|
|
@@ -457,77 +446,62 @@ function compareSheetNames(left, right) {
|
|
|
457
446
|
}
|
|
458
447
|
function checkWorkPaperLicenseKeyValidity(licenseKey) {
|
|
459
448
|
if (!licenseKey || licenseKey.trim().length === 0) {
|
|
460
|
-
return
|
|
449
|
+
return 'missing';
|
|
461
450
|
}
|
|
462
|
-
if (licenseKey ===
|
|
463
|
-
|
|
464
|
-
licenseKey === "internal-use-in-handsontable") {
|
|
465
|
-
return "valid";
|
|
451
|
+
if (licenseKey === 'internal' || licenseKey === 'gpl-v3' || licenseKey === 'internal-use-in-handsontable') {
|
|
452
|
+
return 'valid';
|
|
466
453
|
}
|
|
467
|
-
return
|
|
454
|
+
return 'invalid';
|
|
468
455
|
}
|
|
469
456
|
function validateWorkPaperConfig(config) {
|
|
470
457
|
if (config.maxRows !== undefined && (!Number.isInteger(config.maxRows) || config.maxRows < 1)) {
|
|
471
|
-
throw new WorkPaperConfigValueTooSmallError(
|
|
458
|
+
throw new WorkPaperConfigValueTooSmallError('maxRows', 1);
|
|
472
459
|
}
|
|
473
|
-
if (config.maxColumns !== undefined &&
|
|
474
|
-
|
|
475
|
-
throw new WorkPaperConfigValueTooSmallError("maxColumns", 1);
|
|
460
|
+
if (config.maxColumns !== undefined && (!Number.isInteger(config.maxColumns) || config.maxColumns < 1)) {
|
|
461
|
+
throw new WorkPaperConfigValueTooSmallError('maxColumns', 1);
|
|
476
462
|
}
|
|
477
463
|
if ((config.maxRows ?? MAX_ROWS) > MAX_ROWS) {
|
|
478
|
-
throw new WorkPaperConfigValueTooBigError(
|
|
464
|
+
throw new WorkPaperConfigValueTooBigError('maxRows', MAX_ROWS);
|
|
479
465
|
}
|
|
480
466
|
if ((config.maxColumns ?? MAX_COLS) > MAX_COLS) {
|
|
481
|
-
throw new WorkPaperConfigValueTooBigError(
|
|
482
|
-
}
|
|
483
|
-
if (config.decimalSeparator !== undefined &&
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
throw new WorkPaperExpectedOneOfValuesError('"
|
|
497
|
-
}
|
|
498
|
-
if (config.ignoreWhiteSpace !== undefined &&
|
|
499
|
-
config.ignoreWhiteSpace !== "standard" &&
|
|
500
|
-
config.ignoreWhiteSpace !== "any") {
|
|
501
|
-
throw new WorkPaperExpectedOneOfValuesError('"standard", "any"', "ignoreWhiteSpace");
|
|
502
|
-
}
|
|
503
|
-
if (config.caseFirst !== undefined &&
|
|
504
|
-
config.caseFirst !== "upper" &&
|
|
505
|
-
config.caseFirst !== "lower" &&
|
|
506
|
-
config.caseFirst !== "false") {
|
|
507
|
-
throw new WorkPaperExpectedOneOfValuesError('"upper", "lower", "false"', "caseFirst");
|
|
467
|
+
throw new WorkPaperConfigValueTooBigError('maxColumns', MAX_COLS);
|
|
468
|
+
}
|
|
469
|
+
if (config.decimalSeparator !== undefined && config.decimalSeparator !== '.' && config.decimalSeparator !== ',') {
|
|
470
|
+
throw new WorkPaperExpectedOneOfValuesError('".", ","', 'decimalSeparator');
|
|
471
|
+
}
|
|
472
|
+
if (config.arrayColumnSeparator !== undefined && config.arrayColumnSeparator !== ',' && config.arrayColumnSeparator !== ';') {
|
|
473
|
+
throw new WorkPaperExpectedOneOfValuesError('",", ";"', 'arrayColumnSeparator');
|
|
474
|
+
}
|
|
475
|
+
if (config.arrayRowSeparator !== undefined && config.arrayRowSeparator !== ';' && config.arrayRowSeparator !== '|') {
|
|
476
|
+
throw new WorkPaperExpectedOneOfValuesError('";", "|"', 'arrayRowSeparator');
|
|
477
|
+
}
|
|
478
|
+
if (config.ignoreWhiteSpace !== undefined && config.ignoreWhiteSpace !== 'standard' && config.ignoreWhiteSpace !== 'any') {
|
|
479
|
+
throw new WorkPaperExpectedOneOfValuesError('"standard", "any"', 'ignoreWhiteSpace');
|
|
480
|
+
}
|
|
481
|
+
if (config.caseFirst !== undefined && config.caseFirst !== 'upper' && config.caseFirst !== 'lower' && config.caseFirst !== 'false') {
|
|
482
|
+
throw new WorkPaperExpectedOneOfValuesError('"upper", "lower", "false"', 'caseFirst');
|
|
508
483
|
}
|
|
509
484
|
if (config.chooseAddressMappingPolicy !== undefined &&
|
|
510
|
-
(typeof config.chooseAddressMappingPolicy !==
|
|
485
|
+
(typeof config.chooseAddressMappingPolicy !== 'object' ||
|
|
511
486
|
config.chooseAddressMappingPolicy === null ||
|
|
512
|
-
(config.chooseAddressMappingPolicy.mode !==
|
|
513
|
-
|
|
514
|
-
throw new WorkPaperExpectedOneOfValuesError('"dense", "sparse"', "chooseAddressMappingPolicy.mode");
|
|
487
|
+
(config.chooseAddressMappingPolicy.mode !== 'dense' && config.chooseAddressMappingPolicy.mode !== 'sparse'))) {
|
|
488
|
+
throw new WorkPaperExpectedOneOfValuesError('"dense", "sparse"', 'chooseAddressMappingPolicy.mode');
|
|
515
489
|
}
|
|
516
|
-
if (config.parseDateTime !== undefined && typeof config.parseDateTime !==
|
|
517
|
-
throw new WorkPaperExpectedValueOfTypeError(
|
|
490
|
+
if (config.parseDateTime !== undefined && typeof config.parseDateTime !== 'function') {
|
|
491
|
+
throw new WorkPaperExpectedValueOfTypeError('function', 'parseDateTime');
|
|
518
492
|
}
|
|
519
|
-
if (config.stringifyDateTime !== undefined && typeof config.stringifyDateTime !==
|
|
520
|
-
throw new WorkPaperExpectedValueOfTypeError(
|
|
493
|
+
if (config.stringifyDateTime !== undefined && typeof config.stringifyDateTime !== 'function') {
|
|
494
|
+
throw new WorkPaperExpectedValueOfTypeError('function', 'stringifyDateTime');
|
|
521
495
|
}
|
|
522
|
-
if (config.stringifyDuration !== undefined && typeof config.stringifyDuration !==
|
|
523
|
-
throw new WorkPaperExpectedValueOfTypeError(
|
|
496
|
+
if (config.stringifyDuration !== undefined && typeof config.stringifyDuration !== 'function') {
|
|
497
|
+
throw new WorkPaperExpectedValueOfTypeError('function', 'stringifyDuration');
|
|
524
498
|
}
|
|
525
499
|
if (config.context !== undefined) {
|
|
526
500
|
try {
|
|
527
501
|
structuredClone(config.context);
|
|
528
502
|
}
|
|
529
503
|
catch {
|
|
530
|
-
throw new WorkPaperExpectedValueOfTypeError(
|
|
504
|
+
throw new WorkPaperExpectedValueOfTypeError('structured-cloneable value', 'context');
|
|
531
505
|
}
|
|
532
506
|
}
|
|
533
507
|
}
|
|
@@ -539,7 +513,7 @@ function validateSheetWithinLimits(sheetName, sheet, config) {
|
|
|
539
513
|
}
|
|
540
514
|
sheet.forEach((row) => {
|
|
541
515
|
if (!Array.isArray(row)) {
|
|
542
|
-
throw new WorkPaperUnableToParseError({ sheetName, reason:
|
|
516
|
+
throw new WorkPaperUnableToParseError({ sheetName, reason: 'Rows must be arrays' });
|
|
543
517
|
}
|
|
544
518
|
});
|
|
545
519
|
}
|
|
@@ -551,29 +525,29 @@ function isHistoryRecordArray(value) {
|
|
|
551
525
|
}
|
|
552
526
|
function withEventChanges(event, changes) {
|
|
553
527
|
switch (event.eventName) {
|
|
554
|
-
case
|
|
528
|
+
case 'sheetAdded':
|
|
555
529
|
return event;
|
|
556
|
-
case
|
|
530
|
+
case 'sheetRemoved':
|
|
557
531
|
return {
|
|
558
|
-
eventName:
|
|
532
|
+
eventName: 'sheetRemoved',
|
|
559
533
|
payload: {
|
|
560
534
|
...event.payload,
|
|
561
535
|
changes,
|
|
562
536
|
},
|
|
563
537
|
};
|
|
564
|
-
case
|
|
538
|
+
case 'sheetRenamed':
|
|
565
539
|
return event;
|
|
566
|
-
case
|
|
540
|
+
case 'namedExpressionAdded':
|
|
567
541
|
return {
|
|
568
|
-
eventName:
|
|
542
|
+
eventName: 'namedExpressionAdded',
|
|
569
543
|
payload: {
|
|
570
544
|
...event.payload,
|
|
571
545
|
changes,
|
|
572
546
|
},
|
|
573
547
|
};
|
|
574
|
-
case
|
|
548
|
+
case 'namedExpressionRemoved':
|
|
575
549
|
return {
|
|
576
|
-
eventName:
|
|
550
|
+
eventName: 'namedExpressionRemoved',
|
|
577
551
|
payload: {
|
|
578
552
|
...event.payload,
|
|
579
553
|
changes,
|
|
@@ -640,7 +614,7 @@ class WorkPaperEmitter {
|
|
|
640
614
|
}
|
|
641
615
|
dispatchDetailed(event) {
|
|
642
616
|
switch (event.eventName) {
|
|
643
|
-
case
|
|
617
|
+
case 'sheetAdded':
|
|
644
618
|
this.listeners.sheetAdded.forEach((listener) => {
|
|
645
619
|
listener(event.payload.sheetName);
|
|
646
620
|
});
|
|
@@ -648,7 +622,7 @@ class WorkPaperEmitter {
|
|
|
648
622
|
listener(event.payload);
|
|
649
623
|
});
|
|
650
624
|
return;
|
|
651
|
-
case
|
|
625
|
+
case 'sheetRemoved':
|
|
652
626
|
this.listeners.sheetRemoved.forEach((listener) => {
|
|
653
627
|
listener(event.payload.sheetName, event.payload.changes);
|
|
654
628
|
});
|
|
@@ -656,7 +630,7 @@ class WorkPaperEmitter {
|
|
|
656
630
|
listener(event.payload);
|
|
657
631
|
});
|
|
658
632
|
return;
|
|
659
|
-
case
|
|
633
|
+
case 'sheetRenamed':
|
|
660
634
|
this.listeners.sheetRenamed.forEach((listener) => {
|
|
661
635
|
listener(event.payload.oldName, event.payload.newName);
|
|
662
636
|
});
|
|
@@ -664,7 +638,7 @@ class WorkPaperEmitter {
|
|
|
664
638
|
listener(event.payload);
|
|
665
639
|
});
|
|
666
640
|
return;
|
|
667
|
-
case
|
|
641
|
+
case 'namedExpressionAdded':
|
|
668
642
|
this.listeners.namedExpressionAdded.forEach((listener) => {
|
|
669
643
|
listener(event.payload.name, event.payload.changes);
|
|
670
644
|
});
|
|
@@ -672,7 +646,7 @@ class WorkPaperEmitter {
|
|
|
672
646
|
listener(event.payload);
|
|
673
647
|
});
|
|
674
648
|
return;
|
|
675
|
-
case
|
|
649
|
+
case 'namedExpressionRemoved':
|
|
676
650
|
this.listeners.namedExpressionRemoved.forEach((listener) => {
|
|
677
651
|
listener(event.payload.name, event.payload.changes);
|
|
678
652
|
});
|
|
@@ -680,7 +654,7 @@ class WorkPaperEmitter {
|
|
|
680
654
|
listener(event.payload);
|
|
681
655
|
});
|
|
682
656
|
return;
|
|
683
|
-
case
|
|
657
|
+
case 'valuesUpdated':
|
|
684
658
|
this.listeners.valuesUpdated.forEach((listener) => {
|
|
685
659
|
listener(event.payload.changes);
|
|
686
660
|
});
|
|
@@ -688,7 +662,7 @@ class WorkPaperEmitter {
|
|
|
688
662
|
listener(event.payload);
|
|
689
663
|
});
|
|
690
664
|
return;
|
|
691
|
-
case
|
|
665
|
+
case 'evaluationSuspended':
|
|
692
666
|
this.listeners.evaluationSuspended.forEach((listener) => {
|
|
693
667
|
listener();
|
|
694
668
|
});
|
|
@@ -696,7 +670,7 @@ class WorkPaperEmitter {
|
|
|
696
670
|
listener(event.payload);
|
|
697
671
|
});
|
|
698
672
|
return;
|
|
699
|
-
case
|
|
673
|
+
case 'evaluationResumed':
|
|
700
674
|
this.listeners.evaluationResumed.forEach((listener) => {
|
|
701
675
|
listener(event.payload.changes);
|
|
702
676
|
});
|
|
@@ -757,7 +731,7 @@ export class WorkPaper {
|
|
|
757
731
|
...cloneConfig(configInput),
|
|
758
732
|
};
|
|
759
733
|
this.engine = new SpreadsheetEngine({
|
|
760
|
-
workbookName:
|
|
734
|
+
workbookName: 'Workbook',
|
|
761
735
|
useColumnIndex: this.config.useColumnIndex,
|
|
762
736
|
trackReplicaVersions: false,
|
|
763
737
|
});
|
|
@@ -802,9 +776,7 @@ export class WorkPaper {
|
|
|
802
776
|
for (let row = 0; row < dimensions.height; row += 1) {
|
|
803
777
|
const address = { sheet: sheetId, row, col: column };
|
|
804
778
|
const value = this.getCellValue(address);
|
|
805
|
-
const isMatch = typeof matcher ===
|
|
806
|
-
? value.tag === ValueTag.String && value.value === matcher
|
|
807
|
-
: matcher(value);
|
|
779
|
+
const isMatch = typeof matcher === 'string' ? value.tag === ValueTag.String && value.value === matcher : matcher(value);
|
|
808
780
|
if (isMatch) {
|
|
809
781
|
matches.push(address);
|
|
810
782
|
}
|
|
@@ -896,7 +868,7 @@ export class WorkPaper {
|
|
|
896
868
|
}
|
|
897
869
|
}
|
|
898
870
|
static unregisterFunctionPlugin(plugin) {
|
|
899
|
-
const pluginId = typeof plugin ===
|
|
871
|
+
const pluginId = typeof plugin === 'string' ? plugin : plugin.id;
|
|
900
872
|
this.functionPluginRegistry.delete(pluginId);
|
|
901
873
|
}
|
|
902
874
|
static registerFunction(functionId, plugin, translations) {
|
|
@@ -923,8 +895,7 @@ export class WorkPaper {
|
|
|
923
895
|
}
|
|
924
896
|
if (nextPlugin.aliases) {
|
|
925
897
|
Object.entries(nextPlugin.aliases).forEach(([alias, target]) => {
|
|
926
|
-
if (target.trim().toUpperCase() === normalized ||
|
|
927
|
-
alias.trim().toUpperCase() === normalized) {
|
|
898
|
+
if (target.trim().toUpperCase() === normalized || alias.trim().toUpperCase() === normalized) {
|
|
928
899
|
delete nextPlugin.aliases[alias];
|
|
929
900
|
}
|
|
930
901
|
});
|
|
@@ -936,7 +907,7 @@ export class WorkPaper {
|
|
|
936
907
|
this.functionPluginRegistry.clear();
|
|
937
908
|
}
|
|
938
909
|
static getRegisteredFunctionNames(languageCode) {
|
|
939
|
-
const normalized = languageCode ??
|
|
910
|
+
const normalized = languageCode ?? 'enGB';
|
|
940
911
|
const language = this.languageRegistry.get(normalized);
|
|
941
912
|
const functions = [...this.functionPluginRegistry.values()].flatMap((plugin) => Object.keys(plugin.implementedFunctions));
|
|
942
913
|
if (!language?.functions) {
|
|
@@ -946,8 +917,7 @@ export class WorkPaper {
|
|
|
946
917
|
}
|
|
947
918
|
static getFunctionPlugin(functionId) {
|
|
948
919
|
const normalized = functionId.trim().toUpperCase();
|
|
949
|
-
const plugin = [...this.functionPluginRegistry.values()].find((candidate) => candidate.implementedFunctions[normalized] !== undefined ||
|
|
950
|
-
candidate.aliases?.[normalized] !== undefined);
|
|
920
|
+
const plugin = [...this.functionPluginRegistry.values()].find((candidate) => candidate.implementedFunctions[normalized] !== undefined || candidate.aliases?.[normalized] !== undefined);
|
|
951
921
|
return plugin ? clonePluginDefinition(plugin) : undefined;
|
|
952
922
|
}
|
|
953
923
|
static getAllFunctionPlugins() {
|
|
@@ -1057,25 +1027,21 @@ export class WorkPaper {
|
|
|
1057
1027
|
this.engine.recalculateNow();
|
|
1058
1028
|
}
|
|
1059
1029
|
catch (error) {
|
|
1060
|
-
throw new WorkPaperOperationError(this.messageOf(error,
|
|
1030
|
+
throw new WorkPaperOperationError(this.messageOf(error, 'Recalculation failed'));
|
|
1061
1031
|
}
|
|
1062
1032
|
return [];
|
|
1063
1033
|
}
|
|
1064
1034
|
const beforeVisibility = this.ensureVisibilityCache();
|
|
1065
|
-
const beforeNames = this.namedExpressions.size > 0
|
|
1066
|
-
? this.ensureNamedExpressionValueCache()
|
|
1067
|
-
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
1035
|
+
const beforeNames = this.namedExpressions.size > 0 ? this.ensureNamedExpressionValueCache() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
1068
1036
|
this.drainTrackedEngineEvents();
|
|
1069
1037
|
try {
|
|
1070
1038
|
this.engine.recalculateNow();
|
|
1071
1039
|
}
|
|
1072
1040
|
catch (error) {
|
|
1073
|
-
throw new WorkPaperOperationError(this.messageOf(error,
|
|
1041
|
+
throw new WorkPaperOperationError(this.messageOf(error, 'Recalculation failed'));
|
|
1074
1042
|
}
|
|
1075
1043
|
const afterVisibility = this.captureVisibilitySnapshot();
|
|
1076
|
-
const afterNames = this.namedExpressions.size > 0
|
|
1077
|
-
? this.captureNamedExpressionValueSnapshot()
|
|
1078
|
-
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
1044
|
+
const afterNames = this.namedExpressions.size > 0 ? this.captureNamedExpressionValueSnapshot() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
1079
1045
|
this.visibilityCache = afterVisibility;
|
|
1080
1046
|
this.namedExpressionValueCache = afterNames;
|
|
1081
1047
|
const changes = [
|
|
@@ -1083,7 +1049,7 @@ export class WorkPaper {
|
|
|
1083
1049
|
...this.computeNamedExpressionChanges(beforeNames, afterNames),
|
|
1084
1050
|
];
|
|
1085
1051
|
if (changes.length > 0) {
|
|
1086
|
-
this.emitter.emitDetailed({ eventName:
|
|
1052
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1087
1053
|
}
|
|
1088
1054
|
return changes;
|
|
1089
1055
|
}
|
|
@@ -1126,7 +1092,7 @@ export class WorkPaper {
|
|
|
1126
1092
|
if (!this.evaluationSuspended) {
|
|
1127
1093
|
this.flushQueuedEvents();
|
|
1128
1094
|
if (changes.length > 0) {
|
|
1129
|
-
this.emitter.emitDetailed({ eventName:
|
|
1095
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1130
1096
|
}
|
|
1131
1097
|
}
|
|
1132
1098
|
return changes;
|
|
@@ -1149,7 +1115,7 @@ export class WorkPaper {
|
|
|
1149
1115
|
this.suspendedUsesTrackedFastPath = false;
|
|
1150
1116
|
}
|
|
1151
1117
|
this.drainTrackedEngineEvents();
|
|
1152
|
-
this.emitter.emitDetailed({ eventName:
|
|
1118
|
+
this.emitter.emitDetailed({ eventName: 'evaluationSuspended', payload: {} });
|
|
1153
1119
|
}
|
|
1154
1120
|
resumeEvaluation() {
|
|
1155
1121
|
this.assertNotDisposed();
|
|
@@ -1165,9 +1131,9 @@ export class WorkPaper {
|
|
|
1165
1131
|
this.suspendedNamedValues = null;
|
|
1166
1132
|
this.suspendedUsesTrackedFastPath = false;
|
|
1167
1133
|
this.flushQueuedEvents();
|
|
1168
|
-
this.emitter.emitDetailed({ eventName:
|
|
1134
|
+
this.emitter.emitDetailed({ eventName: 'evaluationResumed', payload: { changes } });
|
|
1169
1135
|
if (changes.length > 0) {
|
|
1170
|
-
this.emitter.emitDetailed({ eventName:
|
|
1136
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
1171
1137
|
}
|
|
1172
1138
|
return changes;
|
|
1173
1139
|
}
|
|
@@ -1251,15 +1217,10 @@ export class WorkPaper {
|
|
|
1251
1217
|
for (let colOffset = 0; colOffset < targetWidth; colOffset += 1) {
|
|
1252
1218
|
const targetRow = target.start.row + rowOffset;
|
|
1253
1219
|
const targetCol = target.start.col + colOffset;
|
|
1254
|
-
const sourceRow = (((targetRow - (offsetsFromTarget ? target.start.row : source.start.row)) %
|
|
1255
|
-
|
|
1256
|
-
sourceHeight) %
|
|
1257
|
-
sourceHeight;
|
|
1258
|
-
const sourceCol = (((targetCol - (offsetsFromTarget ? target.start.col : source.start.col)) % sourceWidth) +
|
|
1259
|
-
sourceWidth) %
|
|
1260
|
-
sourceWidth;
|
|
1220
|
+
const sourceRow = (((targetRow - (offsetsFromTarget ? target.start.row : source.start.row)) % sourceHeight) + sourceHeight) % sourceHeight;
|
|
1221
|
+
const sourceCol = (((targetCol - (offsetsFromTarget ? target.start.col : source.start.col)) % sourceWidth) + sourceWidth) % sourceWidth;
|
|
1261
1222
|
const raw = sourceSerialized[sourceRow]?.[sourceCol] ?? null;
|
|
1262
|
-
if (typeof raw ===
|
|
1223
|
+
if (typeof raw === 'string' && raw.startsWith('=')) {
|
|
1263
1224
|
row.push(`=${translateFormulaReferences(raw.slice(1), targetRow - (source.start.row + sourceRow), targetCol - (source.start.col + sourceCol))}`);
|
|
1264
1225
|
}
|
|
1265
1226
|
else {
|
|
@@ -1288,11 +1249,11 @@ export class WorkPaper {
|
|
|
1288
1249
|
return undefined;
|
|
1289
1250
|
}
|
|
1290
1251
|
const parsed = parseFormula(stripLeadingEquals(formula));
|
|
1291
|
-
if (parsed.kind !==
|
|
1252
|
+
if (parsed.kind !== 'CallExpr' || parsed.callee.trim().toUpperCase() !== 'HYPERLINK') {
|
|
1292
1253
|
return undefined;
|
|
1293
1254
|
}
|
|
1294
1255
|
const firstArgument = parsed.args[0];
|
|
1295
|
-
return firstArgument?.kind ===
|
|
1256
|
+
return firstArgument?.kind === 'StringLiteral' ? firstArgument.value : undefined;
|
|
1296
1257
|
}
|
|
1297
1258
|
getCellSerialized(address) {
|
|
1298
1259
|
this.prepareReadableState();
|
|
@@ -1396,7 +1357,7 @@ export class WorkPaper {
|
|
|
1396
1357
|
: undefined;
|
|
1397
1358
|
try {
|
|
1398
1359
|
const parsed = parseRangeAddress(value, defaultSheetName);
|
|
1399
|
-
if (parsed.kind !==
|
|
1360
|
+
if (parsed.kind !== 'cells') {
|
|
1400
1361
|
return undefined;
|
|
1401
1362
|
}
|
|
1402
1363
|
const sheetName = parsed.sheetName ?? defaultSheetName;
|
|
@@ -1415,18 +1376,18 @@ export class WorkPaper {
|
|
|
1415
1376
|
}
|
|
1416
1377
|
simpleCellAddressToString(address, optionsOrContextSheetId = {}) {
|
|
1417
1378
|
this.assertNotDisposed();
|
|
1418
|
-
const includeSheetName = typeof optionsOrContextSheetId ===
|
|
1379
|
+
const includeSheetName = typeof optionsOrContextSheetId === 'number'
|
|
1419
1380
|
? optionsOrContextSheetId !== address.sheet
|
|
1420
1381
|
: optionsOrContextSheetId.includeSheetName === true;
|
|
1421
1382
|
return formatQualifiedCellAddress(includeSheetName ? this.sheetName(address.sheet) : undefined, address.row, address.col);
|
|
1422
1383
|
}
|
|
1423
1384
|
simpleCellRangeToString(range, optionsOrContextSheetId = {}) {
|
|
1424
|
-
const includeSheetName = typeof optionsOrContextSheetId ===
|
|
1385
|
+
const includeSheetName = typeof optionsOrContextSheetId === 'number'
|
|
1425
1386
|
? optionsOrContextSheetId !== range.start.sheet
|
|
1426
1387
|
: optionsOrContextSheetId.includeSheetName === true;
|
|
1427
1388
|
const sheetName = includeSheetName ? this.sheetName(range.start.sheet) : undefined;
|
|
1428
1389
|
return formatRangeAddress({
|
|
1429
|
-
kind:
|
|
1390
|
+
kind: 'cells',
|
|
1430
1391
|
sheetName,
|
|
1431
1392
|
start: {
|
|
1432
1393
|
row: range.start.row,
|
|
@@ -1445,8 +1406,7 @@ export class WorkPaper {
|
|
|
1445
1406
|
if (!isCellRange(address)) {
|
|
1446
1407
|
return this.toDependencyRefs(this.engine.getDependents(this.sheetName(address.sheet), this.a1(address)).directDependents);
|
|
1447
1408
|
}
|
|
1448
|
-
return this.collectRangeDependencies(address, (cellAddress) => this.engine.getDependents(this.sheetName(cellAddress.sheet), this.a1(cellAddress))
|
|
1449
|
-
.directDependents);
|
|
1409
|
+
return this.collectRangeDependencies(address, (cellAddress) => this.engine.getDependents(this.sheetName(cellAddress.sheet), this.a1(cellAddress)).directDependents);
|
|
1450
1410
|
}
|
|
1451
1411
|
getCellPrecedents(address) {
|
|
1452
1412
|
this.flushPendingBatchOps();
|
|
@@ -1474,12 +1434,12 @@ export class WorkPaper {
|
|
|
1474
1434
|
this.flushPendingBatchOps();
|
|
1475
1435
|
const cell = this.engine.getCell(this.sheetName(address.sheet), this.a1(address));
|
|
1476
1436
|
if (this.isCellEmpty(address)) {
|
|
1477
|
-
return
|
|
1437
|
+
return 'EMPTY';
|
|
1478
1438
|
}
|
|
1479
1439
|
if (this.isCellPartOfArray(address)) {
|
|
1480
|
-
return
|
|
1440
|
+
return 'ARRAY';
|
|
1481
1441
|
}
|
|
1482
|
-
return cell.formula ?
|
|
1442
|
+
return cell.formula ? 'FORMULA' : 'VALUE';
|
|
1483
1443
|
}
|
|
1484
1444
|
doesCellHaveSimpleValue(address) {
|
|
1485
1445
|
this.flushPendingBatchOps();
|
|
@@ -1488,58 +1448,52 @@ export class WorkPaper {
|
|
|
1488
1448
|
}
|
|
1489
1449
|
doesCellHaveFormula(address) {
|
|
1490
1450
|
this.flushPendingBatchOps();
|
|
1491
|
-
return
|
|
1451
|
+
return this.engine.getCell(this.sheetName(address.sheet), this.a1(address)).formula !== undefined;
|
|
1492
1452
|
}
|
|
1493
1453
|
isCellEmpty(address) {
|
|
1494
1454
|
this.flushPendingBatchOps();
|
|
1495
|
-
return
|
|
1496
|
-
ValueTag.Empty);
|
|
1455
|
+
return this.engine.getCellValue(this.sheetName(address.sheet), this.a1(address)).tag === ValueTag.Empty;
|
|
1497
1456
|
}
|
|
1498
1457
|
isCellPartOfArray(address) {
|
|
1499
1458
|
this.flushPendingBatchOps();
|
|
1500
|
-
return this.engine
|
|
1501
|
-
.getSpillRanges()
|
|
1502
|
-
.some((spill) => {
|
|
1459
|
+
return this.engine.getSpillRanges().some((spill) => {
|
|
1503
1460
|
if (this.requireSheetId(spill.sheetName) !== address.sheet) {
|
|
1504
1461
|
return false;
|
|
1505
1462
|
}
|
|
1506
1463
|
const owner = parseCellAddress(spill.address, spill.sheetName);
|
|
1507
|
-
return (address.row >= owner.row &&
|
|
1508
|
-
address.row < owner.row + spill.rows &&
|
|
1509
|
-
address.col >= owner.col &&
|
|
1510
|
-
address.col < owner.col + spill.cols);
|
|
1464
|
+
return (address.row >= owner.row && address.row < owner.row + spill.rows && address.col >= owner.col && address.col < owner.col + spill.cols);
|
|
1511
1465
|
});
|
|
1512
1466
|
}
|
|
1513
1467
|
getCellValueType(address) {
|
|
1514
1468
|
const value = this.getCellValue(address);
|
|
1515
1469
|
switch (value.tag) {
|
|
1516
1470
|
case ValueTag.Number:
|
|
1517
|
-
return
|
|
1471
|
+
return 'NUMBER';
|
|
1518
1472
|
case ValueTag.String:
|
|
1519
|
-
return
|
|
1473
|
+
return 'STRING';
|
|
1520
1474
|
case ValueTag.Boolean:
|
|
1521
|
-
return
|
|
1475
|
+
return 'BOOLEAN';
|
|
1522
1476
|
case ValueTag.Error:
|
|
1523
|
-
return
|
|
1477
|
+
return 'ERROR';
|
|
1524
1478
|
case ValueTag.Empty:
|
|
1525
1479
|
default:
|
|
1526
|
-
return
|
|
1480
|
+
return 'EMPTY';
|
|
1527
1481
|
}
|
|
1528
1482
|
}
|
|
1529
1483
|
getCellValueDetailedType(address) {
|
|
1530
1484
|
const type = this.getCellValueType(address);
|
|
1531
|
-
if (type !==
|
|
1485
|
+
if (type !== 'NUMBER') {
|
|
1532
1486
|
return type;
|
|
1533
1487
|
}
|
|
1534
|
-
const format = this.getCellValueFormat(address)?.toLowerCase() ??
|
|
1535
|
-
if (format.includes(
|
|
1536
|
-
if (format.includes(
|
|
1537
|
-
return
|
|
1488
|
+
const format = this.getCellValueFormat(address)?.toLowerCase() ?? '';
|
|
1489
|
+
if (format.includes('yy') || format.includes('dd')) {
|
|
1490
|
+
if (format.includes('h') || format.includes('s')) {
|
|
1491
|
+
return 'DATETIME';
|
|
1538
1492
|
}
|
|
1539
|
-
return
|
|
1493
|
+
return 'DATE';
|
|
1540
1494
|
}
|
|
1541
|
-
if (format.includes(
|
|
1542
|
-
return
|
|
1495
|
+
if (format.includes('h') || format.includes('s')) {
|
|
1496
|
+
return 'TIME';
|
|
1543
1497
|
}
|
|
1544
1498
|
return type;
|
|
1545
1499
|
}
|
|
@@ -1577,7 +1531,7 @@ export class WorkPaper {
|
|
|
1577
1531
|
throw new WorkPaperNamedExpressionNameIsAlreadyTakenError(expressionName);
|
|
1578
1532
|
}
|
|
1579
1533
|
return this.captureChanges({
|
|
1580
|
-
eventName:
|
|
1534
|
+
eventName: 'namedExpressionAdded',
|
|
1581
1535
|
payload: {
|
|
1582
1536
|
name: expressionName.trim(),
|
|
1583
1537
|
scope,
|
|
@@ -1601,7 +1555,7 @@ export class WorkPaper {
|
|
|
1601
1555
|
}
|
|
1602
1556
|
const existing = this.namedExpressionRecord(expressionName, scope);
|
|
1603
1557
|
return this.captureChanges({
|
|
1604
|
-
eventName:
|
|
1558
|
+
eventName: 'namedExpressionRemoved',
|
|
1605
1559
|
payload: {
|
|
1606
1560
|
name: existing.publicName,
|
|
1607
1561
|
scope: existing.scope,
|
|
@@ -1629,7 +1583,7 @@ export class WorkPaper {
|
|
|
1629
1583
|
.toSorted((left, right) => (left.scope ?? -1) - (right.scope ?? -1) || left.name.localeCompare(right.name));
|
|
1630
1584
|
}
|
|
1631
1585
|
normalizeFormula(formula) {
|
|
1632
|
-
if (!formula.trim().startsWith(
|
|
1586
|
+
if (!formula.trim().startsWith('=')) {
|
|
1633
1587
|
throw new WorkPaperNotAFormulaError();
|
|
1634
1588
|
}
|
|
1635
1589
|
try {
|
|
@@ -1640,7 +1594,7 @@ export class WorkPaper {
|
|
|
1640
1594
|
}
|
|
1641
1595
|
}
|
|
1642
1596
|
calculateFormula(formula, scope) {
|
|
1643
|
-
if (!formula.trim().startsWith(
|
|
1597
|
+
if (!formula.trim().startsWith('=')) {
|
|
1644
1598
|
throw new WorkPaperNotAFormulaError();
|
|
1645
1599
|
}
|
|
1646
1600
|
try {
|
|
@@ -1675,11 +1629,11 @@ export class WorkPaper {
|
|
|
1675
1629
|
});
|
|
1676
1630
|
}
|
|
1677
1631
|
catch (error) {
|
|
1678
|
-
throw new WorkPaperParseError(this.messageOf(error,
|
|
1632
|
+
throw new WorkPaperParseError(this.messageOf(error, 'Unable to calculate formula'));
|
|
1679
1633
|
}
|
|
1680
1634
|
}
|
|
1681
1635
|
getNamedExpressionsFromFormula(formula) {
|
|
1682
|
-
if (!formula.trim().startsWith(
|
|
1636
|
+
if (!formula.trim().startsWith('=')) {
|
|
1683
1637
|
throw new WorkPaperNotAFormulaError();
|
|
1684
1638
|
}
|
|
1685
1639
|
try {
|
|
@@ -1689,11 +1643,11 @@ export class WorkPaper {
|
|
|
1689
1643
|
return [...names].toSorted(compareSheetNames);
|
|
1690
1644
|
}
|
|
1691
1645
|
catch (error) {
|
|
1692
|
-
throw new WorkPaperParseError(this.messageOf(error,
|
|
1646
|
+
throw new WorkPaperParseError(this.messageOf(error, 'Unable to inspect formula'));
|
|
1693
1647
|
}
|
|
1694
1648
|
}
|
|
1695
1649
|
validateFormula(formula) {
|
|
1696
|
-
if (!formula.trim().startsWith(
|
|
1650
|
+
if (!formula.trim().startsWith('=')) {
|
|
1697
1651
|
return false;
|
|
1698
1652
|
}
|
|
1699
1653
|
try {
|
|
@@ -1705,7 +1659,7 @@ export class WorkPaper {
|
|
|
1705
1659
|
}
|
|
1706
1660
|
}
|
|
1707
1661
|
getRegisteredFunctionNames(languageCode) {
|
|
1708
|
-
const code = languageCode ?? this.config.language ??
|
|
1662
|
+
const code = languageCode ?? this.config.language ?? 'enGB';
|
|
1709
1663
|
const language = WorkPaper.languageRegistry.get(code);
|
|
1710
1664
|
const functions = [...this.functionSnapshot.values()]
|
|
1711
1665
|
.filter((binding) => binding.publicName === binding.publicName.toUpperCase())
|
|
@@ -1770,12 +1724,11 @@ export class WorkPaper {
|
|
|
1770
1724
|
setCellContents(address, content) {
|
|
1771
1725
|
this.assertNotDisposed();
|
|
1772
1726
|
const sheet = this.sheetRecord(address.sheet);
|
|
1773
|
-
assertRowAndColumn(address.row,
|
|
1774
|
-
assertRowAndColumn(address.col,
|
|
1727
|
+
assertRowAndColumn(address.row, 'address.row');
|
|
1728
|
+
assertRowAndColumn(address.col, 'address.col');
|
|
1775
1729
|
if (!isWorkPaperSheetMatrix(content)) {
|
|
1776
|
-
if (address.row >= (this.config.maxRows ?? MAX_ROWS) ||
|
|
1777
|
-
|
|
1778
|
-
throw new WorkPaperOperationError("Cell contents cannot be set");
|
|
1730
|
+
if (address.row >= (this.config.maxRows ?? MAX_ROWS) || address.col >= (this.config.maxColumns ?? MAX_COLS)) {
|
|
1731
|
+
throw new WorkPaperOperationError('Cell contents cannot be set');
|
|
1779
1732
|
}
|
|
1780
1733
|
const existingCellIndex = sheet.grid.get(address.row, address.col);
|
|
1781
1734
|
if (this.enqueueSuspendedLiteralMutation(address.sheet, address.row, address.col, content, existingCellIndex)) {
|
|
@@ -1787,16 +1740,16 @@ export class WorkPaper {
|
|
|
1787
1740
|
const mutate = () => {
|
|
1788
1741
|
this.flushPendingBatchOps();
|
|
1789
1742
|
const mutation = content === null
|
|
1790
|
-
? { kind:
|
|
1791
|
-
: typeof content ===
|
|
1743
|
+
? { kind: 'clearCell', row: address.row, col: address.col }
|
|
1744
|
+
: typeof content === 'string' && content.trim().startsWith('=')
|
|
1792
1745
|
? {
|
|
1793
|
-
kind:
|
|
1746
|
+
kind: 'setCellFormula',
|
|
1794
1747
|
row: address.row,
|
|
1795
1748
|
col: address.col,
|
|
1796
1749
|
formula: this.rewriteFormulaForStorage(stripLeadingEquals(content), address.sheet),
|
|
1797
1750
|
}
|
|
1798
1751
|
: {
|
|
1799
|
-
kind:
|
|
1752
|
+
kind: 'setCellValue',
|
|
1800
1753
|
row: address.row,
|
|
1801
1754
|
col: address.col,
|
|
1802
1755
|
value: content,
|
|
@@ -1804,7 +1757,7 @@ export class WorkPaper {
|
|
|
1804
1757
|
this.applyCellMutationRefs([{ sheetId: address.sheet, mutation }], {
|
|
1805
1758
|
captureUndo: true,
|
|
1806
1759
|
potentialNewCells: content === null || existingCellIndex !== -1 ? 0 : 1,
|
|
1807
|
-
source:
|
|
1760
|
+
source: 'local',
|
|
1808
1761
|
returnUndoOps: false,
|
|
1809
1762
|
reuseRefs: true,
|
|
1810
1763
|
});
|
|
@@ -1817,7 +1770,7 @@ export class WorkPaper {
|
|
|
1817
1770
|
});
|
|
1818
1771
|
}
|
|
1819
1772
|
if (!this.isItPossibleToSetCellContents(address, content)) {
|
|
1820
|
-
throw new WorkPaperOperationError(
|
|
1773
|
+
throw new WorkPaperOperationError('Cell contents cannot be set');
|
|
1821
1774
|
}
|
|
1822
1775
|
return this.captureChanges(undefined, () => {
|
|
1823
1776
|
if (isWorkPaperSheetMatrix(content)) {
|
|
@@ -1828,9 +1781,9 @@ export class WorkPaper {
|
|
|
1828
1781
|
});
|
|
1829
1782
|
}
|
|
1830
1783
|
swapRowIndexes(sheetId, rowAOrMappings, rowB) {
|
|
1831
|
-
const mappings = this.normalizeAxisSwapMappings(
|
|
1784
|
+
const mappings = this.normalizeAxisSwapMappings('row', rowAOrMappings, rowB);
|
|
1832
1785
|
if (!this.isItPossibleToSwapRowIndexes(sheetId, mappings)) {
|
|
1833
|
-
throw new WorkPaperOperationError(
|
|
1786
|
+
throw new WorkPaperOperationError('Rows cannot be swapped');
|
|
1834
1787
|
}
|
|
1835
1788
|
return this.batch(() => {
|
|
1836
1789
|
mappings.forEach(([rowA, mappedRowB]) => {
|
|
@@ -1850,7 +1803,7 @@ export class WorkPaper {
|
|
|
1850
1803
|
}
|
|
1851
1804
|
setRowOrder(sheetId, rowOrder) {
|
|
1852
1805
|
if (!this.isItPossibleToSetRowOrder(sheetId, rowOrder)) {
|
|
1853
|
-
throw new WorkPaperOperationError(
|
|
1806
|
+
throw new WorkPaperOperationError('Row order is invalid');
|
|
1854
1807
|
}
|
|
1855
1808
|
const current = rowOrder.toSorted((left, right) => left - right);
|
|
1856
1809
|
return this.batch(() => {
|
|
@@ -1866,9 +1819,9 @@ export class WorkPaper {
|
|
|
1866
1819
|
});
|
|
1867
1820
|
}
|
|
1868
1821
|
swapColumnIndexes(sheetId, columnAOrMappings, columnB) {
|
|
1869
|
-
const mappings = this.normalizeAxisSwapMappings(
|
|
1822
|
+
const mappings = this.normalizeAxisSwapMappings('column', columnAOrMappings, columnB);
|
|
1870
1823
|
if (!this.isItPossibleToSwapColumnIndexes(sheetId, mappings)) {
|
|
1871
|
-
throw new WorkPaperOperationError(
|
|
1824
|
+
throw new WorkPaperOperationError('Columns cannot be swapped');
|
|
1872
1825
|
}
|
|
1873
1826
|
return this.batch(() => {
|
|
1874
1827
|
mappings.forEach(([columnA, mappedColumnB]) => {
|
|
@@ -1888,7 +1841,7 @@ export class WorkPaper {
|
|
|
1888
1841
|
}
|
|
1889
1842
|
setColumnOrder(sheetId, columnOrder) {
|
|
1890
1843
|
if (!this.isItPossibleToSetColumnOrder(sheetId, columnOrder)) {
|
|
1891
|
-
throw new WorkPaperOperationError(
|
|
1844
|
+
throw new WorkPaperOperationError('Column order is invalid');
|
|
1892
1845
|
}
|
|
1893
1846
|
const current = columnOrder.toSorted((left, right) => left - right);
|
|
1894
1847
|
return this.batch(() => {
|
|
@@ -1906,7 +1859,7 @@ export class WorkPaper {
|
|
|
1906
1859
|
addRows(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1907
1860
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1908
1861
|
if (!this.isItPossibleToAddRows(sheetId, ...indexes)) {
|
|
1909
|
-
throw new WorkPaperOperationError(
|
|
1862
|
+
throw new WorkPaperOperationError('Rows cannot be added');
|
|
1910
1863
|
}
|
|
1911
1864
|
return this.batchStructuralChanges(() => {
|
|
1912
1865
|
indexes.forEach(([start, amount]) => {
|
|
@@ -1917,7 +1870,7 @@ export class WorkPaper {
|
|
|
1917
1870
|
removeRows(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1918
1871
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1919
1872
|
if (!this.isItPossibleToRemoveRows(sheetId, ...indexes)) {
|
|
1920
|
-
throw new WorkPaperOperationError(
|
|
1873
|
+
throw new WorkPaperOperationError('Rows cannot be removed');
|
|
1921
1874
|
}
|
|
1922
1875
|
return this.batchStructuralChanges(() => {
|
|
1923
1876
|
indexes
|
|
@@ -1930,7 +1883,7 @@ export class WorkPaper {
|
|
|
1930
1883
|
addColumns(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1931
1884
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1932
1885
|
if (!this.isItPossibleToAddColumns(sheetId, ...indexes)) {
|
|
1933
|
-
throw new WorkPaperOperationError(
|
|
1886
|
+
throw new WorkPaperOperationError('Columns cannot be added');
|
|
1934
1887
|
}
|
|
1935
1888
|
return this.batchStructuralChanges(() => {
|
|
1936
1889
|
indexes.forEach(([start, amount]) => {
|
|
@@ -1941,7 +1894,7 @@ export class WorkPaper {
|
|
|
1941
1894
|
removeColumns(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
1942
1895
|
const indexes = this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals);
|
|
1943
1896
|
if (!this.isItPossibleToRemoveColumns(sheetId, ...indexes)) {
|
|
1944
|
-
throw new WorkPaperOperationError(
|
|
1897
|
+
throw new WorkPaperOperationError('Columns cannot be removed');
|
|
1945
1898
|
}
|
|
1946
1899
|
return this.batchStructuralChanges(() => {
|
|
1947
1900
|
indexes
|
|
@@ -1953,7 +1906,7 @@ export class WorkPaper {
|
|
|
1953
1906
|
}
|
|
1954
1907
|
moveCells(source, target) {
|
|
1955
1908
|
if (!this.isItPossibleToMoveCells(source, target)) {
|
|
1956
|
-
throw new WorkPaperOperationError(
|
|
1909
|
+
throw new WorkPaperOperationError('Cells cannot be moved');
|
|
1957
1910
|
}
|
|
1958
1911
|
const sourceHeight = source.end.row - source.start.row;
|
|
1959
1912
|
const sourceWidth = source.end.col - source.start.col;
|
|
@@ -1967,7 +1920,7 @@ export class WorkPaper {
|
|
|
1967
1920
|
}
|
|
1968
1921
|
moveRows(sheetId, start, count, target) {
|
|
1969
1922
|
if (!this.isItPossibleToMoveRows(sheetId, start, count, target)) {
|
|
1970
|
-
throw new WorkPaperOperationError(
|
|
1923
|
+
throw new WorkPaperOperationError('Rows cannot be moved');
|
|
1971
1924
|
}
|
|
1972
1925
|
return this.canUseTrackedStructuralFastPath()
|
|
1973
1926
|
? this.batchStructuralChanges(() => {
|
|
@@ -1979,7 +1932,7 @@ export class WorkPaper {
|
|
|
1979
1932
|
}
|
|
1980
1933
|
moveColumns(sheetId, start, count, target) {
|
|
1981
1934
|
if (!this.isItPossibleToMoveColumns(sheetId, start, count, target)) {
|
|
1982
|
-
throw new WorkPaperOperationError(
|
|
1935
|
+
throw new WorkPaperOperationError('Columns cannot be moved');
|
|
1983
1936
|
}
|
|
1984
1937
|
return this.canUseTrackedStructuralFastPath()
|
|
1985
1938
|
? this.batchStructuralChanges(() => {
|
|
@@ -2003,14 +1956,14 @@ export class WorkPaper {
|
|
|
2003
1956
|
const sheetId = this.requireSheetId(name);
|
|
2004
1957
|
const payload = { sheetId, sheetName: name };
|
|
2005
1958
|
if (this.shouldSuppressEvents()) {
|
|
2006
|
-
this.queuedEvents.push({ eventName:
|
|
1959
|
+
this.queuedEvents.push({ eventName: 'sheetAdded', payload });
|
|
2007
1960
|
}
|
|
2008
1961
|
else {
|
|
2009
|
-
this.emitter.emitDetailed({ eventName:
|
|
1962
|
+
this.emitter.emitDetailed({ eventName: 'sheetAdded', payload });
|
|
2010
1963
|
}
|
|
2011
1964
|
const changes = this.computeChangesAfterMutation(beforeVisibility, beforeNames);
|
|
2012
1965
|
if (!this.shouldSuppressEvents() && changes.length > 0) {
|
|
2013
|
-
this.emitter.emitDetailed({ eventName:
|
|
1966
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2014
1967
|
}
|
|
2015
1968
|
return name;
|
|
2016
1969
|
}
|
|
@@ -2020,7 +1973,7 @@ export class WorkPaper {
|
|
|
2020
1973
|
}
|
|
2021
1974
|
const sheetName = this.sheetName(sheetId);
|
|
2022
1975
|
return this.captureChanges({
|
|
2023
|
-
eventName:
|
|
1976
|
+
eventName: 'sheetRemoved',
|
|
2024
1977
|
payload: {
|
|
2025
1978
|
sheetId,
|
|
2026
1979
|
sheetName,
|
|
@@ -2042,7 +1995,7 @@ export class WorkPaper {
|
|
|
2042
1995
|
}
|
|
2043
1996
|
this.engine.clearRange({
|
|
2044
1997
|
sheetName: this.sheetName(sheetId),
|
|
2045
|
-
startAddress:
|
|
1998
|
+
startAddress: 'A1',
|
|
2046
1999
|
endAddress: formatAddress(dimensions.height - 1, dimensions.width - 1),
|
|
2047
2000
|
});
|
|
2048
2001
|
});
|
|
@@ -2062,7 +2015,7 @@ export class WorkPaper {
|
|
|
2062
2015
|
const oldName = this.sheetName(sheetId);
|
|
2063
2016
|
const newName = nextName.trim();
|
|
2064
2017
|
return this.captureChanges({
|
|
2065
|
-
eventName:
|
|
2018
|
+
eventName: 'sheetRenamed',
|
|
2066
2019
|
payload: {
|
|
2067
2020
|
sheetId,
|
|
2068
2021
|
oldName,
|
|
@@ -2078,58 +2031,53 @@ export class WorkPaper {
|
|
|
2078
2031
|
if (isCellRange(addressOrRange)) {
|
|
2079
2032
|
assertRange(addressOrRange);
|
|
2080
2033
|
this.sheetRecord(addressOrRange.start.sheet);
|
|
2081
|
-
return
|
|
2082
|
-
addressOrRange.end.col < (this.config.maxColumns ?? MAX_COLS));
|
|
2034
|
+
return addressOrRange.end.row < (this.config.maxRows ?? MAX_ROWS) && addressOrRange.end.col < (this.config.maxColumns ?? MAX_COLS);
|
|
2083
2035
|
}
|
|
2084
2036
|
this.sheetRecord(addressOrRange.sheet);
|
|
2085
|
-
assertRowAndColumn(addressOrRange.row,
|
|
2086
|
-
assertRowAndColumn(addressOrRange.col,
|
|
2037
|
+
assertRowAndColumn(addressOrRange.row, 'address.row');
|
|
2038
|
+
assertRowAndColumn(addressOrRange.col, 'address.col');
|
|
2087
2039
|
if (content === undefined) {
|
|
2088
|
-
return
|
|
2089
|
-
addressOrRange.col < (this.config.maxColumns ?? MAX_COLS));
|
|
2040
|
+
return addressOrRange.row < (this.config.maxRows ?? MAX_ROWS) && addressOrRange.col < (this.config.maxColumns ?? MAX_COLS);
|
|
2090
2041
|
}
|
|
2091
2042
|
if (Array.isArray(content)) {
|
|
2092
2043
|
if (!content.every((row) => Array.isArray(row))) {
|
|
2093
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
2044
|
+
throw new WorkPaperInvalidArgumentsError('Content matrix must be a two-dimensional array');
|
|
2094
2045
|
}
|
|
2095
2046
|
const height = content.length;
|
|
2096
2047
|
const width = Math.max(0, ...content.map((row) => row.length));
|
|
2097
2048
|
return (addressOrRange.row + height <= (this.config.maxRows ?? MAX_ROWS) &&
|
|
2098
2049
|
addressOrRange.col + width <= (this.config.maxColumns ?? MAX_COLS));
|
|
2099
2050
|
}
|
|
2100
|
-
return
|
|
2101
|
-
addressOrRange.col < (this.config.maxColumns ?? MAX_COLS));
|
|
2051
|
+
return addressOrRange.row < (this.config.maxRows ?? MAX_ROWS) && addressOrRange.col < (this.config.maxColumns ?? MAX_COLS);
|
|
2102
2052
|
}
|
|
2103
2053
|
isItPossibleToSwapRowIndexes(sheetId, rowAOrMappings, rowB) {
|
|
2104
2054
|
this.sheetRecord(sheetId);
|
|
2105
|
-
const mappings = this.normalizeAxisSwapMappings(
|
|
2055
|
+
const mappings = this.normalizeAxisSwapMappings('row', rowAOrMappings, rowB);
|
|
2106
2056
|
return mappings.every(([rowA, mappedRowB]) => {
|
|
2107
|
-
assertRowAndColumn(rowA,
|
|
2108
|
-
assertRowAndColumn(mappedRowB,
|
|
2057
|
+
assertRowAndColumn(rowA, 'rowA');
|
|
2058
|
+
assertRowAndColumn(mappedRowB, 'rowB');
|
|
2109
2059
|
return true;
|
|
2110
2060
|
});
|
|
2111
2061
|
}
|
|
2112
2062
|
isItPossibleToSetRowOrder(sheetId, rowOrder) {
|
|
2113
2063
|
this.sheetRecord(sheetId);
|
|
2114
|
-
if (new Set(rowOrder).size !== rowOrder.length ||
|
|
2115
|
-
rowOrder.some((value) => !Number.isInteger(value) || value < 0)) {
|
|
2064
|
+
if (new Set(rowOrder).size !== rowOrder.length || rowOrder.some((value) => !Number.isInteger(value) || value < 0)) {
|
|
2116
2065
|
return false;
|
|
2117
2066
|
}
|
|
2118
2067
|
return true;
|
|
2119
2068
|
}
|
|
2120
2069
|
isItPossibleToSwapColumnIndexes(sheetId, columnAOrMappings, columnB) {
|
|
2121
2070
|
this.sheetRecord(sheetId);
|
|
2122
|
-
const mappings = this.normalizeAxisSwapMappings(
|
|
2071
|
+
const mappings = this.normalizeAxisSwapMappings('column', columnAOrMappings, columnB);
|
|
2123
2072
|
return mappings.every(([columnA, mappedColumnB]) => {
|
|
2124
|
-
assertRowAndColumn(columnA,
|
|
2125
|
-
assertRowAndColumn(mappedColumnB,
|
|
2073
|
+
assertRowAndColumn(columnA, 'columnA');
|
|
2074
|
+
assertRowAndColumn(mappedColumnB, 'columnB');
|
|
2126
2075
|
return true;
|
|
2127
2076
|
});
|
|
2128
2077
|
}
|
|
2129
2078
|
isItPossibleToSetColumnOrder(sheetId, columnOrder) {
|
|
2130
2079
|
this.sheetRecord(sheetId);
|
|
2131
|
-
if (new Set(columnOrder).size !== columnOrder.length ||
|
|
2132
|
-
columnOrder.some((value) => !Number.isInteger(value) || value < 0)) {
|
|
2080
|
+
if (new Set(columnOrder).size !== columnOrder.length || columnOrder.some((value) => !Number.isInteger(value) || value < 0)) {
|
|
2133
2081
|
return false;
|
|
2134
2082
|
}
|
|
2135
2083
|
return true;
|
|
@@ -2137,60 +2085,60 @@ export class WorkPaper {
|
|
|
2137
2085
|
isItPossibleToAddRows(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
2138
2086
|
this.sheetRecord(sheetId);
|
|
2139
2087
|
return this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals).every(([start, count]) => {
|
|
2140
|
-
assertRowAndColumn(start,
|
|
2141
|
-
assertRowAndColumn(count,
|
|
2088
|
+
assertRowAndColumn(start, 'start');
|
|
2089
|
+
assertRowAndColumn(count, 'count');
|
|
2142
2090
|
return count > 0 && start + count <= (this.config.maxRows ?? MAX_ROWS);
|
|
2143
2091
|
});
|
|
2144
2092
|
}
|
|
2145
2093
|
isItPossibleToRemoveRows(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
2146
2094
|
this.sheetRecord(sheetId);
|
|
2147
2095
|
return this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals).every(([start, count]) => {
|
|
2148
|
-
assertRowAndColumn(start,
|
|
2149
|
-
assertRowAndColumn(count,
|
|
2096
|
+
assertRowAndColumn(start, 'start');
|
|
2097
|
+
assertRowAndColumn(count, 'count');
|
|
2150
2098
|
return count > 0;
|
|
2151
2099
|
});
|
|
2152
2100
|
}
|
|
2153
2101
|
isItPossibleToAddColumns(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
2154
2102
|
this.sheetRecord(sheetId);
|
|
2155
2103
|
return this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals).every(([start, count]) => {
|
|
2156
|
-
assertRowAndColumn(start,
|
|
2157
|
-
assertRowAndColumn(count,
|
|
2104
|
+
assertRowAndColumn(start, 'start');
|
|
2105
|
+
assertRowAndColumn(count, 'count');
|
|
2158
2106
|
return count > 0 && start + count <= (this.config.maxColumns ?? MAX_COLS);
|
|
2159
2107
|
});
|
|
2160
2108
|
}
|
|
2161
2109
|
isItPossibleToRemoveColumns(sheetId, startOrInterval, countOrInterval, ...restIntervals) {
|
|
2162
2110
|
this.sheetRecord(sheetId);
|
|
2163
2111
|
return this.normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals).every(([start, count]) => {
|
|
2164
|
-
assertRowAndColumn(start,
|
|
2165
|
-
assertRowAndColumn(count,
|
|
2112
|
+
assertRowAndColumn(start, 'start');
|
|
2113
|
+
assertRowAndColumn(count, 'count');
|
|
2166
2114
|
return count > 0;
|
|
2167
2115
|
});
|
|
2168
2116
|
}
|
|
2169
2117
|
isItPossibleToMoveCells(source, target) {
|
|
2170
2118
|
assertRange(source);
|
|
2171
|
-
assertRowAndColumn(target.sheet,
|
|
2172
|
-
assertRowAndColumn(target.row,
|
|
2173
|
-
assertRowAndColumn(target.col,
|
|
2119
|
+
assertRowAndColumn(target.sheet, 'target.sheet');
|
|
2120
|
+
assertRowAndColumn(target.row, 'target.row');
|
|
2121
|
+
assertRowAndColumn(target.col, 'target.col');
|
|
2174
2122
|
return source.start.sheet === target.sheet;
|
|
2175
2123
|
}
|
|
2176
2124
|
isItPossibleToMoveRows(sheetId, start, count, target) {
|
|
2177
2125
|
this.sheetRecord(sheetId);
|
|
2178
|
-
assertRowAndColumn(start,
|
|
2179
|
-
assertRowAndColumn(count,
|
|
2180
|
-
assertRowAndColumn(target,
|
|
2126
|
+
assertRowAndColumn(start, 'start');
|
|
2127
|
+
assertRowAndColumn(count, 'count');
|
|
2128
|
+
assertRowAndColumn(target, 'target');
|
|
2181
2129
|
return count > 0;
|
|
2182
2130
|
}
|
|
2183
2131
|
isItPossibleToMoveColumns(sheetId, start, count, target) {
|
|
2184
2132
|
this.sheetRecord(sheetId);
|
|
2185
|
-
assertRowAndColumn(start,
|
|
2186
|
-
assertRowAndColumn(count,
|
|
2187
|
-
assertRowAndColumn(target,
|
|
2133
|
+
assertRowAndColumn(start, 'start');
|
|
2134
|
+
assertRowAndColumn(count, 'count');
|
|
2135
|
+
assertRowAndColumn(target, 'target');
|
|
2188
2136
|
return count > 0;
|
|
2189
2137
|
}
|
|
2190
2138
|
isItPossibleToAddSheet(sheetName) {
|
|
2191
2139
|
const trimmed = sheetName.trim();
|
|
2192
2140
|
if (trimmed.length === 0) {
|
|
2193
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
2141
|
+
throw new WorkPaperInvalidArgumentsError('Sheet name must be non-empty');
|
|
2194
2142
|
}
|
|
2195
2143
|
return !this.doesSheetExist(trimmed);
|
|
2196
2144
|
}
|
|
@@ -2203,17 +2151,17 @@ export class WorkPaper {
|
|
|
2203
2151
|
isItPossibleToReplaceSheetContent(sheetId, content) {
|
|
2204
2152
|
this.sheetRecord(sheetId);
|
|
2205
2153
|
if (!content.every((row) => Array.isArray(row))) {
|
|
2206
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
2154
|
+
throw new WorkPaperInvalidArgumentsError('Sheet content must be a two-dimensional array');
|
|
2207
2155
|
}
|
|
2208
2156
|
const height = content.length;
|
|
2209
2157
|
const width = Math.max(0, ...content.map((row) => row.length));
|
|
2210
|
-
return
|
|
2158
|
+
return height <= (this.config.maxRows ?? MAX_ROWS) && width <= (this.config.maxColumns ?? MAX_COLS);
|
|
2211
2159
|
}
|
|
2212
2160
|
isItPossibleToRenameSheet(sheetId, nextName) {
|
|
2213
2161
|
this.sheetRecord(sheetId);
|
|
2214
2162
|
const trimmed = nextName.trim();
|
|
2215
2163
|
if (trimmed.length === 0) {
|
|
2216
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
2164
|
+
throw new WorkPaperInvalidArgumentsError('Sheet name must be non-empty');
|
|
2217
2165
|
}
|
|
2218
2166
|
const existing = this.engine.workbook.getSheet(trimmed);
|
|
2219
2167
|
return !existing || existing.id === sheetId;
|
|
@@ -2290,9 +2238,7 @@ export class WorkPaper {
|
|
|
2290
2238
|
ensureNamedExpressionValueCache() {
|
|
2291
2239
|
if (!this.namedExpressionValueCache) {
|
|
2292
2240
|
this.namedExpressionValueCache =
|
|
2293
|
-
this.namedExpressions.size > 0
|
|
2294
|
-
? this.captureNamedExpressionValueSnapshot()
|
|
2295
|
-
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2241
|
+
this.namedExpressions.size > 0 ? this.captureNamedExpressionValueSnapshot() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2296
2242
|
}
|
|
2297
2243
|
return this.namedExpressionValueCache;
|
|
2298
2244
|
}
|
|
@@ -2307,13 +2253,13 @@ export class WorkPaper {
|
|
|
2307
2253
|
this.engine.applyCellMutationsAtWithOptions(ops, {
|
|
2308
2254
|
captureUndo: true,
|
|
2309
2255
|
potentialNewCells: potentialNewCells > 0 ? potentialNewCells : undefined,
|
|
2310
|
-
source:
|
|
2256
|
+
source: 'local',
|
|
2311
2257
|
returnUndoOps: false,
|
|
2312
2258
|
reuseRefs: true,
|
|
2313
2259
|
});
|
|
2314
2260
|
}
|
|
2315
2261
|
applyCellMutationRefs(refs, options) {
|
|
2316
|
-
if (this.evaluationSuspended && (options.source ??
|
|
2262
|
+
if (this.evaluationSuspended && (options.source ?? 'local') === 'local') {
|
|
2317
2263
|
for (let index = 0; index < refs.length; index += 1) {
|
|
2318
2264
|
const ref = refs[index];
|
|
2319
2265
|
if (!ref) {
|
|
@@ -2322,30 +2268,29 @@ export class WorkPaper {
|
|
|
2322
2268
|
const mutation = ref.mutation;
|
|
2323
2269
|
this.suspendedCellMutationRefs.push({
|
|
2324
2270
|
sheetId: ref.sheetId,
|
|
2325
|
-
mutation: mutation.kind ===
|
|
2271
|
+
mutation: mutation.kind === 'setCellValue'
|
|
2326
2272
|
? {
|
|
2327
|
-
kind:
|
|
2273
|
+
kind: 'setCellValue',
|
|
2328
2274
|
row: mutation.row,
|
|
2329
2275
|
col: mutation.col,
|
|
2330
2276
|
value: mutation.value,
|
|
2331
2277
|
}
|
|
2332
|
-
: mutation.kind ===
|
|
2278
|
+
: mutation.kind === 'setCellFormula'
|
|
2333
2279
|
? {
|
|
2334
|
-
kind:
|
|
2280
|
+
kind: 'setCellFormula',
|
|
2335
2281
|
row: mutation.row,
|
|
2336
2282
|
col: mutation.col,
|
|
2337
2283
|
formula: mutation.formula,
|
|
2338
2284
|
}
|
|
2339
2285
|
: {
|
|
2340
|
-
kind:
|
|
2286
|
+
kind: 'clearCell',
|
|
2341
2287
|
row: mutation.row,
|
|
2342
2288
|
col: mutation.col,
|
|
2343
2289
|
},
|
|
2344
2290
|
});
|
|
2345
2291
|
}
|
|
2346
2292
|
this.suspendedCellMutationPotentialNewCells +=
|
|
2347
|
-
options.potentialNewCells ??
|
|
2348
|
-
refs.reduce((count, ref) => (ref?.mutation.kind === "clearCell" ? count : count + 1), 0);
|
|
2293
|
+
options.potentialNewCells ?? refs.reduce((count, ref) => (ref?.mutation.kind === 'clearCell' ? count : count + 1), 0);
|
|
2349
2294
|
return;
|
|
2350
2295
|
}
|
|
2351
2296
|
this.engine.applyCellMutationsAtWithOptions(refs, options);
|
|
@@ -2361,24 +2306,22 @@ export class WorkPaper {
|
|
|
2361
2306
|
this.engine.applyCellMutationsAtWithOptions(refs, {
|
|
2362
2307
|
captureUndo: true,
|
|
2363
2308
|
potentialNewCells: potentialNewCells > 0 ? potentialNewCells : undefined,
|
|
2364
|
-
source:
|
|
2309
|
+
source: 'local',
|
|
2365
2310
|
returnUndoOps: false,
|
|
2366
2311
|
reuseRefs: true,
|
|
2367
2312
|
});
|
|
2368
2313
|
}
|
|
2369
2314
|
enqueueSuspendedLiteralMutation(sheetId, row, col, content, existingCellIndex = this.engine.workbook.getSheetById(sheetId)?.grid.get(row, col) ?? -1) {
|
|
2370
|
-
if (!this.evaluationSuspended ||
|
|
2371
|
-
!isDeferredBatchLiteralContent(content) ||
|
|
2372
|
-
isFormulaContent(content)) {
|
|
2315
|
+
if (!this.evaluationSuspended || !isDeferredBatchLiteralContent(content) || isFormulaContent(content)) {
|
|
2373
2316
|
return false;
|
|
2374
2317
|
}
|
|
2375
2318
|
if (content === null) {
|
|
2376
|
-
this.suspendedCellMutationRefs.push({ sheetId, mutation: { kind:
|
|
2319
|
+
this.suspendedCellMutationRefs.push({ sheetId, mutation: { kind: 'clearCell', row, col } });
|
|
2377
2320
|
return true;
|
|
2378
2321
|
}
|
|
2379
2322
|
this.suspendedCellMutationRefs.push({
|
|
2380
2323
|
sheetId,
|
|
2381
|
-
mutation: { kind:
|
|
2324
|
+
mutation: { kind: 'setCellValue', row, col, value: content },
|
|
2382
2325
|
});
|
|
2383
2326
|
if (existingCellIndex === -1) {
|
|
2384
2327
|
this.suspendedCellMutationPotentialNewCells += 1;
|
|
@@ -2386,19 +2329,16 @@ export class WorkPaper {
|
|
|
2386
2329
|
return true;
|
|
2387
2330
|
}
|
|
2388
2331
|
enqueueDeferredBatchLiteral(sheetId, row, col, content, existingCellIndex = this.engine.workbook.getSheetById(sheetId)?.grid.get(row, col) ?? -1) {
|
|
2389
|
-
if (this.batchDepth === 0 ||
|
|
2390
|
-
this.evaluationSuspended ||
|
|
2391
|
-
!isDeferredBatchLiteralContent(content) ||
|
|
2392
|
-
isFormulaContent(content)) {
|
|
2332
|
+
if (this.batchDepth === 0 || this.evaluationSuspended || !isDeferredBatchLiteralContent(content) || isFormulaContent(content)) {
|
|
2393
2333
|
return false;
|
|
2394
2334
|
}
|
|
2395
2335
|
if (content === null) {
|
|
2396
|
-
this.pendingBatchOps.push({ sheetId, mutation: { kind:
|
|
2336
|
+
this.pendingBatchOps.push({ sheetId, mutation: { kind: 'clearCell', row, col } });
|
|
2397
2337
|
return true;
|
|
2398
2338
|
}
|
|
2399
2339
|
this.pendingBatchOps.push({
|
|
2400
2340
|
sheetId,
|
|
2401
|
-
mutation: { kind:
|
|
2341
|
+
mutation: { kind: 'setCellValue', row, col, value: content },
|
|
2402
2342
|
});
|
|
2403
2343
|
if (existingCellIndex === -1) {
|
|
2404
2344
|
this.pendingBatchPotentialNewCells += 1;
|
|
@@ -2411,7 +2351,7 @@ export class WorkPaper {
|
|
|
2411
2351
|
}
|
|
2412
2352
|
assertNotDisposed() {
|
|
2413
2353
|
if (this.disposed) {
|
|
2414
|
-
throw new WorkPaperOperationError(
|
|
2354
|
+
throw new WorkPaperOperationError('Workbook has been disposed');
|
|
2415
2355
|
}
|
|
2416
2356
|
}
|
|
2417
2357
|
assertReadable() {
|
|
@@ -2510,10 +2450,7 @@ export class WorkPaper {
|
|
|
2510
2450
|
const cellChanges = [];
|
|
2511
2451
|
afterVisibility.forEach((afterSheet, sheetId) => {
|
|
2512
2452
|
const beforeSheet = beforeVisibility.get(sheetId);
|
|
2513
|
-
const cellKeys = new Set([
|
|
2514
|
-
...(beforeSheet?.cells.keys() ?? []),
|
|
2515
|
-
...afterSheet.cells.keys(),
|
|
2516
|
-
]);
|
|
2453
|
+
const cellKeys = new Set([...(beforeSheet?.cells.keys() ?? []), ...afterSheet.cells.keys()]);
|
|
2517
2454
|
[...cellKeys]
|
|
2518
2455
|
.toSorted((left, right) => left - right)
|
|
2519
2456
|
.forEach((cellKey) => {
|
|
@@ -2527,7 +2464,7 @@ export class WorkPaper {
|
|
|
2527
2464
|
const col = localKey % MAX_COLS;
|
|
2528
2465
|
const address = formatAddress(row, col);
|
|
2529
2466
|
cellChanges.push({
|
|
2530
|
-
kind:
|
|
2467
|
+
kind: 'cell',
|
|
2531
2468
|
address: { sheet: sheetId, row, col },
|
|
2532
2469
|
sheetName: afterSheet.sheetName,
|
|
2533
2470
|
a1: address,
|
|
@@ -2538,7 +2475,7 @@ export class WorkPaper {
|
|
|
2538
2475
|
return orderWorkPaperCellChanges(cellChanges, this.listSheetRecords());
|
|
2539
2476
|
}
|
|
2540
2477
|
computeCellChangesFromTrackedEvents(beforeVisibility, events) {
|
|
2541
|
-
if (events.some((event) => event.invalidation ===
|
|
2478
|
+
if (events.some((event) => event.invalidation === 'full')) {
|
|
2542
2479
|
return null;
|
|
2543
2480
|
}
|
|
2544
2481
|
const nextVisibility = beforeVisibility;
|
|
@@ -2599,8 +2536,7 @@ export class WorkPaper {
|
|
|
2599
2536
|
const sheet = ensureMutableSheet(change.address.sheet, change.sheetName);
|
|
2600
2537
|
if (sheet.order < previousSheetOrder ||
|
|
2601
2538
|
(sheet.order === previousSheetOrder &&
|
|
2602
|
-
(change.address.row < previousRow ||
|
|
2603
|
-
(change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
2539
|
+
(change.address.row < previousRow || (change.address.row === previousRow && change.address.col < previousCol)))) {
|
|
2604
2540
|
alreadySorted = false;
|
|
2605
2541
|
}
|
|
2606
2542
|
if (change.newValue.tag === ValueTag.Empty) {
|
|
@@ -2610,7 +2546,7 @@ export class WorkPaper {
|
|
|
2610
2546
|
sheet.cells.set(cellKey, change.newValue);
|
|
2611
2547
|
}
|
|
2612
2548
|
directChanges[index] = {
|
|
2613
|
-
kind:
|
|
2549
|
+
kind: 'cell',
|
|
2614
2550
|
address: change.address,
|
|
2615
2551
|
sheetName: change.sheetName,
|
|
2616
2552
|
a1: change.a1,
|
|
@@ -2635,7 +2571,7 @@ export class WorkPaper {
|
|
|
2635
2571
|
const cellKey = makeCellKey(change.address.sheet, change.address.row, change.address.col);
|
|
2636
2572
|
latestChangesByKey.delete(cellKey);
|
|
2637
2573
|
latestChangesByKey.set(cellKey, {
|
|
2638
|
-
kind:
|
|
2574
|
+
kind: 'cell',
|
|
2639
2575
|
address: change.address,
|
|
2640
2576
|
sheetName: change.sheetName,
|
|
2641
2577
|
a1: change.a1,
|
|
@@ -2671,7 +2607,7 @@ export class WorkPaper {
|
|
|
2671
2607
|
return;
|
|
2672
2608
|
}
|
|
2673
2609
|
namedExpressionChanges.push({
|
|
2674
|
-
kind:
|
|
2610
|
+
kind: 'named-expression',
|
|
2675
2611
|
name: expression.publicName,
|
|
2676
2612
|
scope: expression.scope,
|
|
2677
2613
|
newValue: cloneNamedExpressionValue(afterValue),
|
|
@@ -2680,32 +2616,23 @@ export class WorkPaper {
|
|
|
2680
2616
|
return namedExpressionChanges.toSorted(compareWorkPaperNamedExpressionChanges);
|
|
2681
2617
|
}
|
|
2682
2618
|
canUseTrackedStructuralFastPath() {
|
|
2683
|
-
return
|
|
2684
|
-
!this.evaluationSuspended &&
|
|
2685
|
-
this.visibilityCache === null &&
|
|
2686
|
-
this.namedExpressions.size === 0);
|
|
2619
|
+
return this.batchDepth === 0 && !this.evaluationSuspended && this.visibilityCache === null && this.namedExpressions.size === 0;
|
|
2687
2620
|
}
|
|
2688
2621
|
canUseTrackedMutationFastPath() {
|
|
2689
|
-
return
|
|
2690
|
-
!this.evaluationSuspended &&
|
|
2691
|
-
this.visibilityCache === null &&
|
|
2692
|
-
this.namedExpressions.size === 0);
|
|
2622
|
+
return this.batchDepth === 0 && !this.evaluationSuspended && this.visibilityCache === null && this.namedExpressions.size === 0;
|
|
2693
2623
|
}
|
|
2694
2624
|
downgradeTrackedBatchFastPath() {
|
|
2695
2625
|
if (!this.batchUsesTrackedFastPath || this.batchDepth === 0) {
|
|
2696
2626
|
return;
|
|
2697
2627
|
}
|
|
2698
2628
|
this.batchStartVisibility = this.ensureVisibilityCache();
|
|
2699
|
-
this.batchStartNamedValues =
|
|
2700
|
-
this.namedExpressions.size > 0
|
|
2701
|
-
? this.ensureNamedExpressionValueCache()
|
|
2702
|
-
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2629
|
+
this.batchStartNamedValues = this.namedExpressions.size > 0 ? this.ensureNamedExpressionValueCache() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2703
2630
|
this.batchUsesTrackedFastPath = false;
|
|
2704
2631
|
}
|
|
2705
2632
|
computeTrackedChangesWithoutVisibilityCache(events) {
|
|
2706
2633
|
const fastPath = this.computeCellChangesFromTrackedEvents(new Map(), events);
|
|
2707
2634
|
if (!fastPath) {
|
|
2708
|
-
throw new WorkPaperOperationError(
|
|
2635
|
+
throw new WorkPaperOperationError('Mutation emitted an unsupported invalidation pattern for tracked changes');
|
|
2709
2636
|
}
|
|
2710
2637
|
return fastPath.changes;
|
|
2711
2638
|
}
|
|
@@ -2719,11 +2646,11 @@ export class WorkPaper {
|
|
|
2719
2646
|
if (error instanceof Error && WORKPAPER_PUBLIC_ERROR_NAMES.has(error.name)) {
|
|
2720
2647
|
throw error;
|
|
2721
2648
|
}
|
|
2722
|
-
throw new WorkPaperOperationError(this.messageOf(error,
|
|
2649
|
+
throw new WorkPaperOperationError(this.messageOf(error, 'Mutation failed'));
|
|
2723
2650
|
}
|
|
2724
2651
|
const changes = this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents());
|
|
2725
2652
|
if (changes.length > 0) {
|
|
2726
|
-
this.emitter.emitDetailed({ eventName:
|
|
2653
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2727
2654
|
}
|
|
2728
2655
|
return changes;
|
|
2729
2656
|
}
|
|
@@ -2747,15 +2674,13 @@ export class WorkPaper {
|
|
|
2747
2674
|
const changes = this.computeTrackedChangesWithoutVisibilityCache(this.drainTrackedEngineEvents());
|
|
2748
2675
|
this.flushQueuedEvents();
|
|
2749
2676
|
if (changes.length > 0) {
|
|
2750
|
-
this.emitter.emitDetailed({ eventName:
|
|
2677
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2751
2678
|
}
|
|
2752
2679
|
return changes;
|
|
2753
2680
|
}
|
|
2754
2681
|
computeChangesAfterMutation(beforeVisibility, beforeNames) {
|
|
2755
2682
|
const hasNamedExpressions = this.namedExpressions.size > 0;
|
|
2756
|
-
const afterNames = hasNamedExpressions
|
|
2757
|
-
? this.captureNamedExpressionValueSnapshot()
|
|
2758
|
-
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2683
|
+
const afterNames = hasNamedExpressions ? this.captureNamedExpressionValueSnapshot() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2759
2684
|
const fastPath = this.computeCellChangesFromTrackedEvents(beforeVisibility, this.drainTrackedEngineEvents());
|
|
2760
2685
|
let cellChanges;
|
|
2761
2686
|
if (fastPath) {
|
|
@@ -2768,9 +2693,7 @@ export class WorkPaper {
|
|
|
2768
2693
|
this.visibilityCache = afterVisibility;
|
|
2769
2694
|
}
|
|
2770
2695
|
this.namedExpressionValueCache = afterNames;
|
|
2771
|
-
return hasNamedExpressions
|
|
2772
|
-
? [...cellChanges, ...this.computeNamedExpressionChanges(beforeNames, afterNames)]
|
|
2773
|
-
: cellChanges;
|
|
2696
|
+
return hasNamedExpressions ? [...cellChanges, ...this.computeNamedExpressionChanges(beforeNames, afterNames)] : cellChanges;
|
|
2774
2697
|
}
|
|
2775
2698
|
captureChanges(semanticEvent, mutate) {
|
|
2776
2699
|
this.assertNotDisposed();
|
|
@@ -2786,7 +2709,7 @@ export class WorkPaper {
|
|
|
2786
2709
|
if (error instanceof Error && WORKPAPER_PUBLIC_ERROR_NAMES.has(error.name)) {
|
|
2787
2710
|
throw error;
|
|
2788
2711
|
}
|
|
2789
|
-
throw new WorkPaperOperationError(this.messageOf(error,
|
|
2712
|
+
throw new WorkPaperOperationError(this.messageOf(error, 'Mutation failed'));
|
|
2790
2713
|
}
|
|
2791
2714
|
if (semanticEvent) {
|
|
2792
2715
|
this.queuedEvents.push(semanticEvent);
|
|
@@ -2794,9 +2717,7 @@ export class WorkPaper {
|
|
|
2794
2717
|
return [];
|
|
2795
2718
|
}
|
|
2796
2719
|
const beforeVisibility = this.ensureVisibilityCache();
|
|
2797
|
-
const beforeNames = this.namedExpressions.size > 0
|
|
2798
|
-
? this.ensureNamedExpressionValueCache()
|
|
2799
|
-
: EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2720
|
+
const beforeNames = this.namedExpressions.size > 0 ? this.ensureNamedExpressionValueCache() : EMPTY_NAMED_EXPRESSION_VALUES;
|
|
2800
2721
|
this.drainTrackedEngineEvents();
|
|
2801
2722
|
try {
|
|
2802
2723
|
mutate();
|
|
@@ -2805,7 +2726,7 @@ export class WorkPaper {
|
|
|
2805
2726
|
if (error instanceof Error && WORKPAPER_PUBLIC_ERROR_NAMES.has(error.name)) {
|
|
2806
2727
|
throw error;
|
|
2807
2728
|
}
|
|
2808
|
-
throw new WorkPaperOperationError(this.messageOf(error,
|
|
2729
|
+
throw new WorkPaperOperationError(this.messageOf(error, 'Mutation failed'));
|
|
2809
2730
|
}
|
|
2810
2731
|
const changes = semanticEvent === undefined
|
|
2811
2732
|
? this.computeChangesAfterMutation(beforeVisibility, beforeNames)
|
|
@@ -2829,7 +2750,7 @@ export class WorkPaper {
|
|
|
2829
2750
|
}
|
|
2830
2751
|
}
|
|
2831
2752
|
if (!this.shouldSuppressEvents() && changes.length > 0) {
|
|
2832
|
-
this.emitter.emitDetailed({ eventName:
|
|
2753
|
+
this.emitter.emitDetailed({ eventName: 'valuesUpdated', payload: { changes } });
|
|
2833
2754
|
}
|
|
2834
2755
|
return changes;
|
|
2835
2756
|
}
|
|
@@ -2844,14 +2765,14 @@ export class WorkPaper {
|
|
|
2844
2765
|
});
|
|
2845
2766
|
}
|
|
2846
2767
|
getUndoStack() {
|
|
2847
|
-
const stack = Reflect.get(this.engine,
|
|
2768
|
+
const stack = Reflect.get(this.engine, 'undoStack');
|
|
2848
2769
|
if (!isHistoryRecordArray(stack)) {
|
|
2849
2770
|
return [];
|
|
2850
2771
|
}
|
|
2851
2772
|
return stack;
|
|
2852
2773
|
}
|
|
2853
2774
|
getRedoStack() {
|
|
2854
|
-
const stack = Reflect.get(this.engine,
|
|
2775
|
+
const stack = Reflect.get(this.engine, 'redoStack');
|
|
2855
2776
|
if (!isHistoryRecordArray(stack)) {
|
|
2856
2777
|
return [];
|
|
2857
2778
|
}
|
|
@@ -2863,9 +2784,9 @@ export class WorkPaper {
|
|
|
2863
2784
|
}
|
|
2864
2785
|
historyTransactionOps(record) {
|
|
2865
2786
|
switch (record.kind) {
|
|
2866
|
-
case
|
|
2787
|
+
case 'ops':
|
|
2867
2788
|
return record.ops;
|
|
2868
|
-
case
|
|
2789
|
+
case 'single-op':
|
|
2869
2790
|
return [record.op];
|
|
2870
2791
|
}
|
|
2871
2792
|
}
|
|
@@ -2877,12 +2798,12 @@ export class WorkPaper {
|
|
|
2877
2798
|
const entries = undoStack.splice(startIndex);
|
|
2878
2799
|
const merged = {
|
|
2879
2800
|
forward: {
|
|
2880
|
-
kind:
|
|
2801
|
+
kind: 'ops',
|
|
2881
2802
|
ops: entries.flatMap((entry) => this.historyTransactionOps(entry.forward)),
|
|
2882
2803
|
potentialNewCells: sumNumbers(entries.map((entry) => entry.forward.potentialNewCells)),
|
|
2883
2804
|
},
|
|
2884
2805
|
inverse: {
|
|
2885
|
-
kind:
|
|
2806
|
+
kind: 'ops',
|
|
2886
2807
|
ops: entries.toReversed().flatMap((entry) => this.historyTransactionOps(entry.inverse)),
|
|
2887
2808
|
potentialNewCells: sumNumbers(entries.map((entry) => entry.inverse.potentialNewCells)),
|
|
2888
2809
|
},
|
|
@@ -2912,7 +2833,7 @@ export class WorkPaper {
|
|
|
2912
2833
|
col: targetLeftCorner.col + columnOffset,
|
|
2913
2834
|
};
|
|
2914
2835
|
let nextValue = raw;
|
|
2915
|
-
if (typeof raw ===
|
|
2836
|
+
if (typeof raw === 'string' && raw.startsWith('=')) {
|
|
2916
2837
|
nextValue = `=${translateFormulaReferences(raw.slice(1), destination.row - (sourceAnchor.row + rowOffset), destination.col - (sourceAnchor.col + columnOffset))}`;
|
|
2917
2838
|
}
|
|
2918
2839
|
this.applyRawContent(destination, nextValue);
|
|
@@ -2931,7 +2852,7 @@ export class WorkPaper {
|
|
|
2931
2852
|
this.applyCellMutationRefs(refs, {
|
|
2932
2853
|
captureUndo: true,
|
|
2933
2854
|
potentialNewCells,
|
|
2934
|
-
source:
|
|
2855
|
+
source: 'local',
|
|
2935
2856
|
returnUndoOps: false,
|
|
2936
2857
|
reuseRefs: true,
|
|
2937
2858
|
});
|
|
@@ -2954,7 +2875,7 @@ export class WorkPaper {
|
|
|
2954
2875
|
}
|
|
2955
2876
|
this.applyCellMutationRefs(phaseRefs, applyOptions);
|
|
2956
2877
|
};
|
|
2957
|
-
const phaseSource = options.captureUndo === false ?
|
|
2878
|
+
const phaseSource = options.captureUndo === false ? 'restore' : 'local';
|
|
2958
2879
|
if (formulaRefs.length === 0) {
|
|
2959
2880
|
applyPlannedRefs(refs, {
|
|
2960
2881
|
captureUndo: options.captureUndo,
|
|
@@ -3005,16 +2926,16 @@ export class WorkPaper {
|
|
|
3005
2926
|
applyRawContent(address, content) {
|
|
3006
2927
|
const existingCellIndex = this.engine.workbook.getSheetById(address.sheet)?.grid.get(address.row, address.col) ?? -1;
|
|
3007
2928
|
const mutation = content === null
|
|
3008
|
-
? { kind:
|
|
3009
|
-
: typeof content ===
|
|
2929
|
+
? { kind: 'clearCell', row: address.row, col: address.col }
|
|
2930
|
+
: typeof content === 'string' && content.trim().startsWith('=')
|
|
3010
2931
|
? {
|
|
3011
|
-
kind:
|
|
2932
|
+
kind: 'setCellFormula',
|
|
3012
2933
|
row: address.row,
|
|
3013
2934
|
col: address.col,
|
|
3014
2935
|
formula: this.rewriteFormulaForStorage(stripLeadingEquals(content), address.sheet),
|
|
3015
2936
|
}
|
|
3016
2937
|
: {
|
|
3017
|
-
kind:
|
|
2938
|
+
kind: 'setCellValue',
|
|
3018
2939
|
row: address.row,
|
|
3019
2940
|
col: address.col,
|
|
3020
2941
|
value: content,
|
|
@@ -3022,7 +2943,7 @@ export class WorkPaper {
|
|
|
3022
2943
|
this.applyCellMutationRefs([{ sheetId: address.sheet, mutation }], {
|
|
3023
2944
|
captureUndo: true,
|
|
3024
2945
|
potentialNewCells: content === null || existingCellIndex !== -1 ? 0 : 1,
|
|
3025
|
-
source:
|
|
2946
|
+
source: 'local',
|
|
3026
2947
|
returnUndoOps: false,
|
|
3027
2948
|
reuseRefs: true,
|
|
3028
2949
|
});
|
|
@@ -3072,8 +2993,7 @@ export class WorkPaper {
|
|
|
3072
2993
|
validateCurrentSheetsWithinLimits(nextConfig) {
|
|
3073
2994
|
this.listSheetRecords().forEach((sheet) => {
|
|
3074
2995
|
const dimensions = this.getSheetDimensions(sheet.id);
|
|
3075
|
-
if (dimensions.height > (nextConfig.maxRows ?? MAX_ROWS) ||
|
|
3076
|
-
dimensions.width > (nextConfig.maxColumns ?? MAX_COLS)) {
|
|
2996
|
+
if (dimensions.height > (nextConfig.maxRows ?? MAX_ROWS) || dimensions.width > (nextConfig.maxColumns ?? MAX_COLS)) {
|
|
3077
2997
|
throw new WorkPaperSheetSizeLimitExceededError();
|
|
3078
2998
|
}
|
|
3079
2999
|
});
|
|
@@ -3095,7 +3015,7 @@ export class WorkPaper {
|
|
|
3095
3015
|
return true;
|
|
3096
3016
|
}
|
|
3097
3017
|
canApplyRuntimeOnlyConfigUpdate(changedKeys) {
|
|
3098
|
-
return changedKeys.every((key) => key ===
|
|
3018
|
+
return changedKeys.every((key) => key === 'useColumnIndex' || key === 'useStats');
|
|
3099
3019
|
}
|
|
3100
3020
|
applyRuntimeOnlyConfigUpdate(nextConfig) {
|
|
3101
3021
|
if (this.config.useColumnIndex !== nextConfig.useColumnIndex) {
|
|
@@ -3113,9 +3033,7 @@ export class WorkPaper {
|
|
|
3113
3033
|
validateSheetWithinLimits(sheetName, sheet, nextConfig);
|
|
3114
3034
|
});
|
|
3115
3035
|
}
|
|
3116
|
-
const serializedNamedExpressions = canReuseSnapshot
|
|
3117
|
-
? null
|
|
3118
|
-
: this.getAllNamedExpressionsSerialized();
|
|
3036
|
+
const serializedNamedExpressions = canReuseSnapshot ? null : this.getAllNamedExpressionsSerialized();
|
|
3119
3037
|
const suspended = this.evaluationSuspended;
|
|
3120
3038
|
const clipboard = this.clipboard
|
|
3121
3039
|
? {
|
|
@@ -3129,7 +3047,7 @@ export class WorkPaper {
|
|
|
3129
3047
|
this.namedExpressions.clear();
|
|
3130
3048
|
}
|
|
3131
3049
|
this.engine = new SpreadsheetEngine({
|
|
3132
|
-
workbookName:
|
|
3050
|
+
workbookName: 'Workbook',
|
|
3133
3051
|
useColumnIndex: this.config.useColumnIndex,
|
|
3134
3052
|
trackReplicaVersions: false,
|
|
3135
3053
|
});
|
|
@@ -3165,20 +3083,20 @@ export class WorkPaper {
|
|
|
3165
3083
|
}
|
|
3166
3084
|
}
|
|
3167
3085
|
normalizeAxisIntervals(startOrInterval, countOrInterval, restIntervals = []) {
|
|
3168
|
-
if (typeof startOrInterval ===
|
|
3086
|
+
if (typeof startOrInterval === 'number') {
|
|
3169
3087
|
if (Array.isArray(countOrInterval)) {
|
|
3170
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
3088
|
+
throw new WorkPaperInvalidArgumentsError('Axis interval count must be a number');
|
|
3171
3089
|
}
|
|
3172
|
-
const resolvedCount = typeof countOrInterval ===
|
|
3090
|
+
const resolvedCount = typeof countOrInterval === 'number' ? countOrInterval : 1;
|
|
3173
3091
|
return [[startOrInterval, resolvedCount]];
|
|
3174
3092
|
}
|
|
3175
|
-
if (typeof countOrInterval ===
|
|
3176
|
-
throw new WorkPaperInvalidArgumentsError(
|
|
3093
|
+
if (typeof countOrInterval === 'number') {
|
|
3094
|
+
throw new WorkPaperInvalidArgumentsError('Axis interval count is only valid with a numeric start');
|
|
3177
3095
|
}
|
|
3178
3096
|
return [startOrInterval, ...(countOrInterval ? [countOrInterval] : []), ...restIntervals].map(([start, count]) => [start, count ?? 1]);
|
|
3179
3097
|
}
|
|
3180
3098
|
normalizeAxisSwapMappings(label, startOrMappings, end) {
|
|
3181
|
-
if (typeof startOrMappings ===
|
|
3099
|
+
if (typeof startOrMappings === 'number') {
|
|
3182
3100
|
if (end === undefined) {
|
|
3183
3101
|
throw new WorkPaperInvalidArgumentsError(`${label} swap requires two indexes`);
|
|
3184
3102
|
}
|
|
@@ -3193,9 +3111,9 @@ export class WorkPaper {
|
|
|
3193
3111
|
this.getDenseRange(range, (address) => address).forEach((row) => {
|
|
3194
3112
|
row.forEach((address) => {
|
|
3195
3113
|
this.toDependencyRefs(readDependencies(address)).forEach((dependency) => {
|
|
3196
|
-
const key = dependency.kind ===
|
|
3114
|
+
const key = dependency.kind === 'cell'
|
|
3197
3115
|
? `cell:${dependency.address.sheet}:${dependency.address.row}:${dependency.address.col}`
|
|
3198
|
-
: dependency.kind ===
|
|
3116
|
+
: dependency.kind === 'range'
|
|
3199
3117
|
? `range:${dependency.range.start.sheet}:${dependency.range.start.row}:${dependency.range.start.col}:${dependency.range.end.row}:${dependency.range.end.col}`
|
|
3200
3118
|
: `name:${dependency.name}`;
|
|
3201
3119
|
if (seen.has(key)) {
|
|
@@ -3214,10 +3132,10 @@ export class WorkPaper {
|
|
|
3214
3132
|
}
|
|
3215
3133
|
try {
|
|
3216
3134
|
const transformed = transformFormulaNode(parseFormula(stripLeadingEquals(formula)), (node) => {
|
|
3217
|
-
if (node.kind ===
|
|
3135
|
+
if (node.kind === 'NameRef') {
|
|
3218
3136
|
return this.rewriteNameRefForStorage(node, ownerSheetId);
|
|
3219
3137
|
}
|
|
3220
|
-
if (node.kind ===
|
|
3138
|
+
if (node.kind === 'CallExpr') {
|
|
3221
3139
|
return this.rewriteCallForStorage(node);
|
|
3222
3140
|
}
|
|
3223
3141
|
return node;
|
|
@@ -3225,7 +3143,7 @@ export class WorkPaper {
|
|
|
3225
3143
|
return serializeFormula(transformed);
|
|
3226
3144
|
}
|
|
3227
3145
|
catch (error) {
|
|
3228
|
-
throw new WorkPaperParseError(this.messageOf(error,
|
|
3146
|
+
throw new WorkPaperParseError(this.messageOf(error, 'Unable to store formula'));
|
|
3229
3147
|
}
|
|
3230
3148
|
}
|
|
3231
3149
|
restorePublicFormula(formula, ownerSheetId) {
|
|
@@ -3233,10 +3151,10 @@ export class WorkPaper {
|
|
|
3233
3151
|
return formula;
|
|
3234
3152
|
}
|
|
3235
3153
|
const transformed = transformFormulaNode(parseFormula(formula), (node) => {
|
|
3236
|
-
if (node.kind ===
|
|
3154
|
+
if (node.kind === 'NameRef') {
|
|
3237
3155
|
return this.rewriteNameRefForPublic(node, ownerSheetId);
|
|
3238
3156
|
}
|
|
3239
|
-
if (node.kind ===
|
|
3157
|
+
if (node.kind === 'CallExpr') {
|
|
3240
3158
|
return this.rewriteCallForPublic(node);
|
|
3241
3159
|
}
|
|
3242
3160
|
return node;
|
|
@@ -3320,12 +3238,12 @@ export class WorkPaper {
|
|
|
3320
3238
|
}
|
|
3321
3239
|
}
|
|
3322
3240
|
toDefinedNameSnapshot(expression, scope) {
|
|
3323
|
-
if (expression === null || typeof expression ===
|
|
3241
|
+
if (expression === null || typeof expression === 'number' || typeof expression === 'boolean') {
|
|
3324
3242
|
return expression;
|
|
3325
3243
|
}
|
|
3326
|
-
if (typeof expression ===
|
|
3244
|
+
if (typeof expression === 'string' && expression.trim().startsWith('=')) {
|
|
3327
3245
|
return {
|
|
3328
|
-
kind:
|
|
3246
|
+
kind: 'formula',
|
|
3329
3247
|
formula: `=${this.rewriteFormulaForStorage(stripLeadingEquals(expression), scope ?? this.listSheetRecords()[0]?.id ?? 1)}`,
|
|
3330
3248
|
};
|
|
3331
3249
|
}
|
|
@@ -3340,10 +3258,10 @@ export class WorkPaper {
|
|
|
3340
3258
|
}
|
|
3341
3259
|
evaluateNamedExpression(expression) {
|
|
3342
3260
|
const raw = expression.expression;
|
|
3343
|
-
if (raw === null || typeof raw ===
|
|
3261
|
+
if (raw === null || typeof raw === 'number' || typeof raw === 'boolean') {
|
|
3344
3262
|
return scalarValueFromLiteral(raw);
|
|
3345
3263
|
}
|
|
3346
|
-
if (typeof raw ===
|
|
3264
|
+
if (typeof raw === 'string' && !raw.trim().startsWith('=')) {
|
|
3347
3265
|
return scalarValueFromLiteral(raw);
|
|
3348
3266
|
}
|
|
3349
3267
|
return this.calculateFormula(raw, expression.scope);
|
|
@@ -3372,7 +3290,7 @@ export class WorkPaper {
|
|
|
3372
3290
|
try {
|
|
3373
3291
|
const parsedCell = parseCellAddress(value);
|
|
3374
3292
|
return {
|
|
3375
|
-
kind:
|
|
3293
|
+
kind: 'cell',
|
|
3376
3294
|
address: {
|
|
3377
3295
|
sheet: this.requireSheetId(parsedCell.sheetName ?? this.listSheetRecords()[0].name),
|
|
3378
3296
|
row: parsedCell.row,
|
|
@@ -3383,9 +3301,9 @@ export class WorkPaper {
|
|
|
3383
3301
|
catch {
|
|
3384
3302
|
try {
|
|
3385
3303
|
const parsedRange = parseRangeAddress(value);
|
|
3386
|
-
if (parsedRange.kind ===
|
|
3304
|
+
if (parsedRange.kind === 'cells') {
|
|
3387
3305
|
return {
|
|
3388
|
-
kind:
|
|
3306
|
+
kind: 'range',
|
|
3389
3307
|
range: {
|
|
3390
3308
|
start: {
|
|
3391
3309
|
sheet: this.requireSheetId(parsedRange.sheetName ?? this.listSheetRecords()[0].name),
|
|
@@ -3402,10 +3320,10 @@ export class WorkPaper {
|
|
|
3402
3320
|
}
|
|
3403
3321
|
}
|
|
3404
3322
|
catch {
|
|
3405
|
-
return { kind:
|
|
3323
|
+
return { kind: 'name', name: value };
|
|
3406
3324
|
}
|
|
3407
3325
|
}
|
|
3408
|
-
return { kind:
|
|
3326
|
+
return { kind: 'name', name: value };
|
|
3409
3327
|
});
|
|
3410
3328
|
}
|
|
3411
3329
|
messageOf(error, fallback) {
|
|
@@ -3419,7 +3337,7 @@ function cloneNamedExpressionValue(value) {
|
|
|
3419
3337
|
return value.map((row) => row.map((cell) => cloneCellValue(cell)));
|
|
3420
3338
|
}
|
|
3421
3339
|
function compareWorkPaperNamedExpressionChanges(left, right) {
|
|
3422
|
-
if (left.kind !==
|
|
3340
|
+
if (left.kind !== 'named-expression' || right.kind !== 'named-expression') {
|
|
3423
3341
|
return 0;
|
|
3424
3342
|
}
|
|
3425
3343
|
return (left.scope ?? -1) - (right.scope ?? -1) || left.name.localeCompare(right.name);
|
|
@@ -3432,7 +3350,7 @@ function sourceRangeRef(sheetName, range) {
|
|
|
3432
3350
|
};
|
|
3433
3351
|
}
|
|
3434
3352
|
function sumNumbers(values) {
|
|
3435
|
-
const filtered = values.filter((value) => typeof value ===
|
|
3353
|
+
const filtered = values.filter((value) => typeof value === 'number');
|
|
3436
3354
|
if (filtered.length === 0) {
|
|
3437
3355
|
return undefined;
|
|
3438
3356
|
}
|