@speed-sheet/extension-formula 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +191 -0
- package/dist/index.js +1331 -0
- package/dist/index.js.map +1 -0
- package/package.json +37 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1331 @@
|
|
|
1
|
+
import { Extension, SheetState, transactSystem, buildIdIndexes } from '@speed-sheet/core';
|
|
2
|
+
import { depKey, parseDepKey, AXIS_ID_PATTERN } from '@speed-sheet/shared';
|
|
3
|
+
import * as formulajs from '@formulajs/formulajs';
|
|
4
|
+
|
|
5
|
+
// src/extension.ts
|
|
6
|
+
function getSheetState(sheetsMap, sheetId) {
|
|
7
|
+
const ySheet = sheetsMap.get(sheetId);
|
|
8
|
+
if (!ySheet) return null;
|
|
9
|
+
return new SheetState(ySheet);
|
|
10
|
+
}
|
|
11
|
+
function createFormulaContext(sheet) {
|
|
12
|
+
const ydoc = sheet.ydoc;
|
|
13
|
+
const sheetsMap = ydoc.getMap("sheets");
|
|
14
|
+
return {
|
|
15
|
+
activeSheetId: sheet.getActiveSheetId(),
|
|
16
|
+
getSheetName(sheetId) {
|
|
17
|
+
const ySheet = sheetsMap.get(sheetId);
|
|
18
|
+
return ySheet?.get("name") ?? sheetId;
|
|
19
|
+
},
|
|
20
|
+
resolveSheetId(nameOrId) {
|
|
21
|
+
if (!nameOrId) return sheet.getActiveSheetId();
|
|
22
|
+
if (sheetsMap.has(nameOrId)) return nameOrId;
|
|
23
|
+
for (const id of sheet.getSheetIds()) {
|
|
24
|
+
const ySheet = sheetsMap.get(id);
|
|
25
|
+
const name = ySheet?.get("name");
|
|
26
|
+
if (name === nameOrId) return id;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
},
|
|
30
|
+
resolveCellIds(sheetId, r, c) {
|
|
31
|
+
const state = getSheetState(sheetsMap, sheetId);
|
|
32
|
+
return state?.resolveCellIds(r, c) ?? null;
|
|
33
|
+
},
|
|
34
|
+
idsToDisplay(sheetId, rowId, colId) {
|
|
35
|
+
const state = getSheetState(sheetsMap, sheetId);
|
|
36
|
+
if (!state) return null;
|
|
37
|
+
const { rowIndex, colIndex } = buildIdIndexes(state.rowOrder, state.colOrder);
|
|
38
|
+
const r = rowIndex.get(rowId);
|
|
39
|
+
const c = colIndex.get(colId);
|
|
40
|
+
if (r === void 0 || c === void 0) return null;
|
|
41
|
+
return { r, c };
|
|
42
|
+
},
|
|
43
|
+
expandIdRange(sheetId, rowId0, colId0, rowId1, colId1) {
|
|
44
|
+
const state = getSheetState(sheetsMap, sheetId);
|
|
45
|
+
if (!state) return [];
|
|
46
|
+
const { rowIndex, colIndex } = buildIdIndexes(state.rowOrder, state.colOrder);
|
|
47
|
+
const r0 = rowIndex.get(rowId0);
|
|
48
|
+
const c0 = colIndex.get(colId0);
|
|
49
|
+
const r1 = rowIndex.get(rowId1);
|
|
50
|
+
const c1 = colIndex.get(colId1);
|
|
51
|
+
if (r0 === void 0 || c0 === void 0 || r1 === void 0 || c1 === void 0) return [];
|
|
52
|
+
const out = [];
|
|
53
|
+
const ra = Math.min(r0, r1);
|
|
54
|
+
const rb = Math.max(r0, r1);
|
|
55
|
+
const ca = Math.min(c0, c1);
|
|
56
|
+
const cb = Math.max(c0, c1);
|
|
57
|
+
for (let r = ra; r <= rb; r++) {
|
|
58
|
+
for (let c = ca; c <= cb; c++) {
|
|
59
|
+
const rowId = state.rowOrder.get(r);
|
|
60
|
+
const colId = state.colOrder.get(c);
|
|
61
|
+
if (rowId && colId) out.push({ rowId, colId });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
},
|
|
66
|
+
getScalarById(sheetId, rowId, colId, visiting) {
|
|
67
|
+
const visitKey = depKey(sheetId, rowId, colId);
|
|
68
|
+
if (visiting.has(visitKey)) return null;
|
|
69
|
+
const state = getSheetState(sheetsMap, sheetId);
|
|
70
|
+
if (!state) return null;
|
|
71
|
+
const { rowIndex, colIndex } = buildIdIndexes(state.rowOrder, state.colOrder);
|
|
72
|
+
const r = rowIndex.get(rowId);
|
|
73
|
+
const c = colIndex.get(colId);
|
|
74
|
+
if (r === void 0 || c === void 0) return null;
|
|
75
|
+
const data = state.getCellData(r, c);
|
|
76
|
+
if (!data) return null;
|
|
77
|
+
const v = data.v;
|
|
78
|
+
if (v === null || v === void 0) return null;
|
|
79
|
+
return v;
|
|
80
|
+
},
|
|
81
|
+
getScalar(sheetId, r, c, visiting) {
|
|
82
|
+
const ids = this.resolveCellIds(sheetId, r, c);
|
|
83
|
+
if (!ids) return null;
|
|
84
|
+
return this.getScalarById(sheetId, ids.rowId, ids.colId, visiting);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function coerceNumber(v) {
|
|
89
|
+
if (v === null || v === "") return 0;
|
|
90
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
91
|
+
const n = Number(v);
|
|
92
|
+
return Number.isFinite(n) ? n : 0;
|
|
93
|
+
}
|
|
94
|
+
function collectIdRangeScalars(ctx, sheetId, rowId0, colId0, rowId1, colId1, visiting) {
|
|
95
|
+
const cells = ctx.expandIdRange(sheetId, rowId0, colId0, rowId1, colId1);
|
|
96
|
+
return cells.map(
|
|
97
|
+
({ rowId, colId }) => coerceNumber(ctx.getScalarById(sheetId, rowId, colId, visiting))
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// src/errors.ts
|
|
102
|
+
var FORMULA_ERRORS = {
|
|
103
|
+
ERROR: {
|
|
104
|
+
code: "ERROR",
|
|
105
|
+
display: "#ERROR!",
|
|
106
|
+
message: "\u516C\u5F0F\u89E3\u6790\u9519\u8BEF"
|
|
107
|
+
},
|
|
108
|
+
VALUE: {
|
|
109
|
+
code: "VALUE",
|
|
110
|
+
display: "#VALUE!",
|
|
111
|
+
message: "\u503C\u7C7B\u578B\u9519\u8BEF\uFF1A\u8FD0\u7B97\u6216\u51FD\u6570\u53C2\u6570\u7C7B\u578B\u4E0D\u5339\u914D\uFF08\u4F8B\u5982\u6570\u5B57\u4E0E\u6587\u672C\u76F8\u52A0\uFF09"
|
|
112
|
+
},
|
|
113
|
+
NAME: {
|
|
114
|
+
code: "NAME",
|
|
115
|
+
display: "#NAME?",
|
|
116
|
+
message: "\u65E0\u6CD5\u8BC6\u522B\u540D\u79F0\uFF1A\u51FD\u6570\u540D\u62FC\u5199\u9519\u8BEF\u3001\u6587\u672C\u672A\u52A0\u53CC\u5F15\u53F7\uFF0C\u6216\u5DE5\u4F5C\u8868\u540D\u5F15\u7528\u4E0D\u6B63\u786E"
|
|
117
|
+
},
|
|
118
|
+
REF: {
|
|
119
|
+
code: "REF",
|
|
120
|
+
display: "#REF!",
|
|
121
|
+
message: "\u65E0\u6548\u5F15\u7528\uFF1A\u5F15\u7528\u7684\u5355\u5143\u683C\u3001\u533A\u57DF\u6216\u5DE5\u4F5C\u8868\u4E0D\u5B58\u5728"
|
|
122
|
+
},
|
|
123
|
+
DIV0: {
|
|
124
|
+
code: "DIV0",
|
|
125
|
+
display: "#DIV/0!",
|
|
126
|
+
message: "\u9664\u6570\u4E3A\u96F6\uFF1A\u516C\u5F0F\u4E2D\u9664\u4EE5 0 \u6216\u7A7A\u5355\u5143\u683C"
|
|
127
|
+
},
|
|
128
|
+
NA: {
|
|
129
|
+
code: "NA",
|
|
130
|
+
display: "#N/A",
|
|
131
|
+
message: "\u627E\u4E0D\u5230\u5339\u914D\u503C\uFF1A\u67E5\u627E\u6216\u5339\u914D\u51FD\u6570\u672A\u627E\u5230\u5BF9\u5E94\u6570\u636E"
|
|
132
|
+
},
|
|
133
|
+
NUM: {
|
|
134
|
+
code: "NUM",
|
|
135
|
+
display: "#NUM!",
|
|
136
|
+
message: "\u6570\u503C\u9519\u8BEF\uFF1A\u7ED3\u679C\u8D85\u51FA\u53EF\u8BA1\u7B97\u8303\u56F4\u6216\u6570\u503C\u4E0D\u5408\u6CD5"
|
|
137
|
+
},
|
|
138
|
+
NULL: {
|
|
139
|
+
code: "NULL",
|
|
140
|
+
display: "#NULL!",
|
|
141
|
+
message: "\u7A7A\u4EA4\u96C6\u9519\u8BEF\uFF1A\u533A\u57DF\u5F15\u7528\u4E4B\u95F4\u4F7F\u7528\u4E86\u7A7A\u683C\u800C\u975E\u9017\u53F7\u6216\u5192\u53F7"
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var ERROR_DISPLAY_SET = new Set(
|
|
145
|
+
Object.values(FORMULA_ERRORS).map((e) => e.display)
|
|
146
|
+
);
|
|
147
|
+
function formulaErrorResult(code) {
|
|
148
|
+
const def = FORMULA_ERRORS[code];
|
|
149
|
+
return {
|
|
150
|
+
value: null,
|
|
151
|
+
m: def.display,
|
|
152
|
+
error: def.code,
|
|
153
|
+
errorMessage: def.message
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function isFormulaErrorDisplay(text) {
|
|
157
|
+
return ERROR_DISPLAY_SET.has(text.trim());
|
|
158
|
+
}
|
|
159
|
+
function getFormulaErrorMessage(code, fallback) {
|
|
160
|
+
if (code && code in FORMULA_ERRORS) {
|
|
161
|
+
return FORMULA_ERRORS[code].message;
|
|
162
|
+
}
|
|
163
|
+
return fallback ?? FORMULA_ERRORS.ERROR.message;
|
|
164
|
+
}
|
|
165
|
+
var ID = AXIS_ID_PATTERN;
|
|
166
|
+
var INTERNAL_REF_FULL_RE = new RegExp(
|
|
167
|
+
`#(?:@[^|#]+\\|)?${ID}:${ID}(?:~${ID}:${ID})?#`,
|
|
168
|
+
"g"
|
|
169
|
+
);
|
|
170
|
+
function getInternalRefRanges(formula) {
|
|
171
|
+
const ranges = [];
|
|
172
|
+
INTERNAL_REF_FULL_RE.lastIndex = 0;
|
|
173
|
+
let m;
|
|
174
|
+
while ((m = INTERNAL_REF_FULL_RE.exec(formula)) !== null) {
|
|
175
|
+
ranges.push({ start: m.index, end: m.index + m[0].length });
|
|
176
|
+
}
|
|
177
|
+
return ranges;
|
|
178
|
+
}
|
|
179
|
+
function overlapsInternalRef(formula, start, end) {
|
|
180
|
+
for (const r of getInternalRefRanges(formula)) {
|
|
181
|
+
if (start < r.end && end > r.start) return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
function hasInternalRefs(formula) {
|
|
186
|
+
INTERNAL_REF_FULL_RE.lastIndex = 0;
|
|
187
|
+
return INTERNAL_REF_FULL_RE.test(formula);
|
|
188
|
+
}
|
|
189
|
+
function extractInternalRefTokens(formula) {
|
|
190
|
+
const tokens = [];
|
|
191
|
+
INTERNAL_REF_FULL_RE.lastIndex = 0;
|
|
192
|
+
let m;
|
|
193
|
+
while ((m = INTERNAL_REF_FULL_RE.exec(formula)) !== null) {
|
|
194
|
+
tokens.push(m[0]);
|
|
195
|
+
}
|
|
196
|
+
return tokens;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// src/refs.ts
|
|
200
|
+
function letterToCol(letters) {
|
|
201
|
+
let n = 0;
|
|
202
|
+
const s = letters.toUpperCase();
|
|
203
|
+
for (let i = 0; i < s.length; i++) {
|
|
204
|
+
n = n * 26 + (s.charCodeAt(i) - 64);
|
|
205
|
+
}
|
|
206
|
+
return n - 1;
|
|
207
|
+
}
|
|
208
|
+
function colToLetter(c) {
|
|
209
|
+
let s = "";
|
|
210
|
+
let n = c;
|
|
211
|
+
do {
|
|
212
|
+
s = String.fromCharCode(65 + n % 26) + s;
|
|
213
|
+
n = Math.floor(n / 26) - 1;
|
|
214
|
+
} while (n >= 0);
|
|
215
|
+
return s;
|
|
216
|
+
}
|
|
217
|
+
function formatA1(r, c) {
|
|
218
|
+
return `${colToLetter(c)}${r + 1}`;
|
|
219
|
+
}
|
|
220
|
+
function parseRefToken(token) {
|
|
221
|
+
const trimmed = token.trim();
|
|
222
|
+
if (!trimmed) return null;
|
|
223
|
+
let sheet;
|
|
224
|
+
let local = trimmed;
|
|
225
|
+
const bang = trimmed.indexOf("!");
|
|
226
|
+
if (bang >= 0) {
|
|
227
|
+
let sheetPart = trimmed.slice(0, bang);
|
|
228
|
+
if (sheetPart.startsWith("'") && sheetPart.endsWith("'")) {
|
|
229
|
+
sheetPart = sheetPart.slice(1, -1);
|
|
230
|
+
}
|
|
231
|
+
sheet = sheetPart;
|
|
232
|
+
local = trimmed.slice(bang + 1);
|
|
233
|
+
}
|
|
234
|
+
const rangeMatch = /^([A-Za-z]+)(\d+):([A-Za-z]+)(\d+)$/.exec(local);
|
|
235
|
+
if (rangeMatch) {
|
|
236
|
+
const r0 = Number(rangeMatch[2]) - 1;
|
|
237
|
+
const c0 = letterToCol(rangeMatch[1]);
|
|
238
|
+
const r1 = Number(rangeMatch[4]) - 1;
|
|
239
|
+
const c1 = letterToCol(rangeMatch[3]);
|
|
240
|
+
return {
|
|
241
|
+
sheet,
|
|
242
|
+
range: {
|
|
243
|
+
row: [Math.min(r0, r1), Math.max(r0, r1)],
|
|
244
|
+
column: [Math.min(c0, c1), Math.max(c0, c1)]
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
const cellMatch = /^([A-Za-z]+)(\d+)$/.exec(local);
|
|
249
|
+
if (cellMatch) {
|
|
250
|
+
return {
|
|
251
|
+
sheet,
|
|
252
|
+
cell: {
|
|
253
|
+
r: Number(cellMatch[2]) - 1,
|
|
254
|
+
c: letterToCol(cellMatch[1])
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
function extractRefTokens(formula) {
|
|
261
|
+
const tokens = [];
|
|
262
|
+
const re = /(?:'[^']+'|[\w\u4e00-\u9fff\u3400-\u9fff]+!)?[A-Za-z]{1,4}\d+(?::[A-Za-z]{1,4}\d+)?/g;
|
|
263
|
+
let m;
|
|
264
|
+
while ((m = re.exec(formula)) !== null) {
|
|
265
|
+
const start = m.index;
|
|
266
|
+
const end = start + m[0].length;
|
|
267
|
+
if (overlapsInternalRef(formula, start, end)) continue;
|
|
268
|
+
tokens.push(m[0]);
|
|
269
|
+
}
|
|
270
|
+
return tokens;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/formula-bindings.ts
|
|
274
|
+
var ID2 = AXIS_ID_PATTERN;
|
|
275
|
+
function formatInternalCellRef(rowId, colId, sheetId) {
|
|
276
|
+
if (sheetId) return `#@${sheetId}|${rowId}:${colId}#`;
|
|
277
|
+
return `#${rowId}:${colId}#`;
|
|
278
|
+
}
|
|
279
|
+
function formatInternalRangeRef(rowId0, colId0, rowId1, colId1, sheetId) {
|
|
280
|
+
if (sheetId) return `#@${sheetId}|${rowId0}:${colId0}~${rowId1}:${colId1}#`;
|
|
281
|
+
return `#${rowId0}:${colId0}~${rowId1}:${colId1}#`;
|
|
282
|
+
}
|
|
283
|
+
function parseInternalRefToken(token) {
|
|
284
|
+
const range = new RegExp(`^#(?:@([^|#]+)\\|)?(${ID2}):(${ID2})~(${ID2}):(${ID2})#$`).exec(token);
|
|
285
|
+
if (range) {
|
|
286
|
+
return {
|
|
287
|
+
sheetId: range[1],
|
|
288
|
+
rowId: range[2],
|
|
289
|
+
colId: range[3],
|
|
290
|
+
endRowId: range[4],
|
|
291
|
+
endColId: range[5]
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
const cell = new RegExp(`^#(?:@([^|#]+)\\|)?(${ID2}):(${ID2})#$`).exec(token);
|
|
295
|
+
if (cell) {
|
|
296
|
+
return { sheetId: cell[1], rowId: cell[2], colId: cell[3] };
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
function resolveIdsForA1(ctx, sheetId, r, c) {
|
|
301
|
+
return ctx.resolveCellIds(sheetId, r, c);
|
|
302
|
+
}
|
|
303
|
+
function a1RefToInternal(ctx, defaultSheetId, token) {
|
|
304
|
+
const ref = parseRefToken(token);
|
|
305
|
+
if (!ref) return null;
|
|
306
|
+
const sheetResolved = ref.sheet ? ctx.resolveSheetId(ref.sheet) : defaultSheetId;
|
|
307
|
+
if (!sheetResolved) return null;
|
|
308
|
+
if (ref.range) {
|
|
309
|
+
const a = resolveIdsForA1(ctx, sheetResolved, ref.range.row[0], ref.range.column[0]);
|
|
310
|
+
const b = resolveIdsForA1(ctx, sheetResolved, ref.range.row[1], ref.range.column[1]);
|
|
311
|
+
if (!a || !b) return null;
|
|
312
|
+
const cross = sheetResolved !== defaultSheetId ? sheetResolved : void 0;
|
|
313
|
+
return formatInternalRangeRef(a.rowId, a.colId, b.rowId, b.colId, cross);
|
|
314
|
+
}
|
|
315
|
+
if (ref.cell) {
|
|
316
|
+
const ids = resolveIdsForA1(ctx, sheetResolved, ref.cell.r, ref.cell.c);
|
|
317
|
+
if (!ids) return null;
|
|
318
|
+
const cross = sheetResolved !== defaultSheetId ? sheetResolved : void 0;
|
|
319
|
+
return formatInternalCellRef(ids.rowId, ids.colId, cross);
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
function displayFormulaToInternal(displayFormula, ctx, defaultSheetId) {
|
|
324
|
+
const text = displayFormula.trim();
|
|
325
|
+
if (!text.startsWith("=")) return text;
|
|
326
|
+
let out = text;
|
|
327
|
+
const tokens = [...extractRefTokens(text)].sort((a, b) => b.length - a.length);
|
|
328
|
+
for (const token of tokens) {
|
|
329
|
+
const internal = a1RefToInternal(ctx, defaultSheetId, token);
|
|
330
|
+
if (internal) out = out.split(token).join(internal);
|
|
331
|
+
}
|
|
332
|
+
return out;
|
|
333
|
+
}
|
|
334
|
+
function internalRefToA1(ctx, ref, defaultSheetId) {
|
|
335
|
+
const sheetId = ref.sheetId ?? defaultSheetId;
|
|
336
|
+
const toA1 = (rowId, colId) => {
|
|
337
|
+
const pos = ctx.idsToDisplay(sheetId, rowId, colId);
|
|
338
|
+
if (!pos) return null;
|
|
339
|
+
return formatA1(pos.r, pos.c);
|
|
340
|
+
};
|
|
341
|
+
if (ref.endRowId != null && ref.endColId != null) {
|
|
342
|
+
const a2 = toA1(ref.rowId, ref.colId);
|
|
343
|
+
const b = toA1(ref.endRowId, ref.endColId);
|
|
344
|
+
if (!a2 || !b) return null;
|
|
345
|
+
const local = a2 === b ? a2 : `${a2}:${b}`;
|
|
346
|
+
if (ref.sheetId && ref.sheetId !== defaultSheetId) {
|
|
347
|
+
const name = ctx.getSheetName(ref.sheetId);
|
|
348
|
+
return `'${name.replace(/'/g, "''")}'!${local}`;
|
|
349
|
+
}
|
|
350
|
+
return local;
|
|
351
|
+
}
|
|
352
|
+
const a = toA1(ref.rowId, ref.colId);
|
|
353
|
+
if (!a) return null;
|
|
354
|
+
if (ref.sheetId && ref.sheetId !== defaultSheetId) {
|
|
355
|
+
const name = ctx.getSheetName(ref.sheetId);
|
|
356
|
+
return `'${name.replace(/'/g, "''")}'!${a}`;
|
|
357
|
+
}
|
|
358
|
+
return a;
|
|
359
|
+
}
|
|
360
|
+
function internalFormulaToDisplay(internalFormula, ctx, defaultSheetId) {
|
|
361
|
+
const text = internalFormula.trim();
|
|
362
|
+
if (!text.startsWith("=")) return text;
|
|
363
|
+
if (!hasInternalRefs(text)) return text;
|
|
364
|
+
let out = text;
|
|
365
|
+
const tokens = [...extractInternalRefTokens(text)].sort((a, b) => b.length - a.length);
|
|
366
|
+
for (const token of tokens) {
|
|
367
|
+
const ref = parseInternalRefToken(token);
|
|
368
|
+
if (!ref) continue;
|
|
369
|
+
const a1 = internalRefToA1(ctx, ref, defaultSheetId);
|
|
370
|
+
if (a1) out = out.split(token).join(a1);
|
|
371
|
+
}
|
|
372
|
+
return out;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// src/registry/formulajs-catalog.json
|
|
376
|
+
var formulajs_catalog_default = { ABS: { category: "math", formulajs: true }, ACCRINT: { category: "financial", formulajs: true }, ACCRINTM: { category: "financial", formulajs: false }, ACOS: { category: "math", formulajs: true }, ACOSH: { category: "math", formulajs: true }, ACOT: { category: "math", formulajs: true }, ACOTH: { category: "math", formulajs: true }, AGGREGATE: { category: "math", formulajs: true }, ADDRESS: { category: "lookup", formulajs: false }, AMORDEGRC: { category: "financial", formulajs: false }, AMORLINC: { category: "financial", formulajs: false }, AND: { category: "logical", formulajs: true }, ARABIC: { category: "math", formulajs: true }, AREAS: { category: "lookup", formulajs: false }, ARRAYTOTEXT: { category: "text", formulajs: false }, ASC: { category: "text", formulajs: false }, ASIN: { category: "math", formulajs: true }, ASINH: { category: "math", formulajs: true }, ATAN: { category: "math", formulajs: true }, ATAN2: { category: "math", formulajs: true }, ATANH: { category: "math", formulajs: true }, AVEDEV: { category: "statistical", formulajs: true }, AVERAGE: { category: "statistical", formulajs: true }, AVERAGEA: { category: "statistical", formulajs: true }, AVERAGEIF: { category: "statistical", formulajs: true }, AVERAGEIFS: { category: "statistical", formulajs: true }, BAHTTEXT: { category: "text", formulajs: false }, BASE: { category: "math", formulajs: true }, BESSELI: { category: "engineering", formulajs: true }, BESSELJ: { category: "engineering", formulajs: true }, BESSELK: { category: "engineering", formulajs: true }, BESSELY: { category: "engineering", formulajs: true }, BETADIST: { category: "statistical", formulajs: true }, BETAINV: { category: "statistical", formulajs: true }, BIN2DEC: { category: "engineering", formulajs: true }, BIN2HEX: { category: "engineering", formulajs: true }, BIN2OCT: { category: "engineering", formulajs: true }, BINOMDIST: { category: "statistical", formulajs: true }, BINOMDISTRANGE: { category: "statistical", formulajs: true }, BINOMINV: { category: "statistical", formulajs: true }, BITAND: { category: "engineering", formulajs: true }, BITLSHIFT: { category: "engineering", formulajs: true }, BITOR: { category: "engineering", formulajs: true }, BITRSHIFT: { category: "engineering", formulajs: true }, BITXOR: { category: "engineering", formulajs: true }, BYCOL: { category: "logical", formulajs: false }, BYROW: { category: "logical", formulajs: false }, CEILING: { category: "compatibility", formulajs: true }, CEILINGMATH: { category: "math", formulajs: true }, CEILINGPRECISE: { category: "math", formulajs: true }, CELL: { category: "information", formulajs: false }, CHAR: { category: "text", formulajs: true }, CHIDIST: { category: "compatibility", formulajs: true }, CHIINV: { category: "compatibility", formulajs: true }, CHITEST: { category: "compatibility", formulajs: true }, CHISQDIST: { category: "statistical", formulajs: true }, CHISQDISTRT: { category: "statistical", formulajs: true }, CHISQINV: { category: "statistical", formulajs: true }, CHISQINVRT: { category: "statistical", formulajs: true }, CHISQTEST: { category: "statistical", formulajs: true }, CHOOSE: { category: "lookup", formulajs: true }, CHOOSECOLS: { category: "lookup", formulajs: false }, CHOOSEROWS: { category: "lookup", formulajs: false }, CLEAN: { category: "text", formulajs: true }, CODE: { category: "text", formulajs: true }, COLUMN: { category: "lookup", formulajs: true }, COLUMNS: { category: "lookup", formulajs: true }, COMBIN: { category: "math", formulajs: true }, COMBINA: { category: "math", formulajs: true }, COMPLEX: { category: "engineering", formulajs: true }, CONCAT: { category: "text", formulajs: true }, CONCATENATE: { category: "text", formulajs: true }, CONFIDENCE: { category: "compatibility", formulajs: false }, CONFIDENCENORM: { category: "statistical", formulajs: true }, CONFIDENCET: { category: "statistical", formulajs: true }, CONVERT: { category: "engineering", formulajs: true }, CORREL: { category: "statistical", formulajs: true }, COS: { category: "math", formulajs: true }, COSH: { category: "math", formulajs: true }, COT: { category: "math", formulajs: true }, COTH: { category: "math", formulajs: true }, COUNT: { category: "statistical", formulajs: true }, COUNTA: { category: "statistical", formulajs: true }, COUNTBLANK: { category: "statistical", formulajs: true }, COUNTIF: { category: "statistical", formulajs: true }, COUNTIFS: { category: "statistical", formulajs: true }, COUPDAYBS: { category: "financial", formulajs: false }, COUPDAYS: { category: "financial", formulajs: true }, COUPDAYSNC: { category: "financial", formulajs: false }, COUPNCD: { category: "financial", formulajs: false }, COUPNUM: { category: "financial", formulajs: false }, COUPPCD: { category: "financial", formulajs: false }, COVAR: { category: "compatibility", formulajs: true }, COVARIANCEP: { category: "statistical", formulajs: true }, COVARIANCES: { category: "statistical", formulajs: true }, CRITBINOM: { category: "compatibility", formulajs: true }, CSC: { category: "math", formulajs: true }, CSCH: { category: "math", formulajs: true }, CUMIPMT: { category: "financial", formulajs: true }, CUMPRINC: { category: "financial", formulajs: true }, DATE: { category: "date", formulajs: true }, DATEDIF: { category: "date", formulajs: true }, DATEVALUE: { category: "date", formulajs: true }, DAVERAGE: { category: "database", formulajs: true }, DAY: { category: "date", formulajs: true }, DAYS: { category: "date", formulajs: true }, DAYS360: { category: "date", formulajs: true }, DB: { category: "financial", formulajs: true }, DBCS: { category: "text", formulajs: false }, DCOUNT: { category: "database", formulajs: true }, DCOUNTA: { category: "database", formulajs: true }, DDB: { category: "financial", formulajs: true }, DEC2BIN: { category: "engineering", formulajs: true }, DEC2HEX: { category: "engineering", formulajs: true }, DEC2OCT: { category: "engineering", formulajs: true }, DECIMAL: { category: "math", formulajs: true }, DEGREES: { category: "math", formulajs: true }, DELTA: { category: "engineering", formulajs: true }, DETECTLANGUAGE: { category: "text", formulajs: false }, DEVSQ: { category: "statistical", formulajs: true }, DGET: { category: "database", formulajs: true }, DISC: { category: "financial", formulajs: true }, DMAX: { category: "database", formulajs: true }, DMIN: { category: "database", formulajs: true }, DOLLAR: { category: "text", formulajs: true }, DOLLARDE: { category: "financial", formulajs: true }, DOLLARFR: { category: "financial", formulajs: true }, DPRODUCT: { category: "database", formulajs: true }, DROP: { category: "lookup", formulajs: false }, DSTDEV: { category: "database", formulajs: true }, DSTDEVP: { category: "database", formulajs: true }, DSUM: { category: "database", formulajs: true }, DURATION: { category: "financial", formulajs: false }, DVAR: { category: "database", formulajs: true }, DVARP: { category: "database", formulajs: true }, EDATE: { category: "date", formulajs: true }, EFFECT: { category: "financial", formulajs: true }, EOMONTH: { category: "date", formulajs: true }, ERF: { category: "engineering", formulajs: true }, ERFPRECISE: { category: "engineering", formulajs: false }, ERFC: { category: "engineering", formulajs: true }, ERFCPRECISE: { category: "engineering", formulajs: false }, ERRORTYPE: { category: "information", formulajs: true }, EVEN: { category: "math", formulajs: true }, EXACT: { category: "text", formulajs: true }, EXP: { category: "math", formulajs: true }, EXPAND: { category: "lookup", formulajs: false }, EXPONDIST: { category: "compatibility", formulajs: true }, FACT: { category: "math", formulajs: true }, FACTDOUBLE: { category: "math", formulajs: true }, FALSE: { category: "logical", formulajs: true }, FDIST: { category: "compatibility", formulajs: true }, FDISTRT: { category: "statistical", formulajs: true }, FILTER: { category: "lookup", formulajs: false }, FIND: { category: "text", formulajs: true }, FINV: { category: "compatibility", formulajs: true }, FINVRT: { category: "statistical", formulajs: true }, FISHER: { category: "statistical", formulajs: true }, FISHERINV: { category: "statistical", formulajs: true }, FIXED: { category: "text", formulajs: true }, FLOOR: { category: "compatibility", formulajs: true }, FLOORMATH: { category: "math", formulajs: true }, FLOORPRECISE: { category: "math", formulajs: true }, FORECAST: { category: "statistical", formulajs: true }, FORECASTETS: { category: "statistical", formulajs: false }, FORECASTETSCONFINT: { category: "statistical", formulajs: false }, FORECASTETSSEASONALITY: { category: "statistical", formulajs: false }, FORECASTETSSTAT: { category: "statistical", formulajs: false }, FORECASTLINEAR: { category: "statistical", formulajs: false }, FORMULATEXT: { category: "lookup", formulajs: false }, FREQUENCY: { category: "statistical", formulajs: true }, FTEST: { category: "compatibility", formulajs: true }, FV: { category: "financial", formulajs: true }, FVSCHEDULE: { category: "financial", formulajs: true }, GAMMA: { category: "statistical", formulajs: true }, GAMMADIST: { category: "compatibility", formulajs: true }, GAMMAINV: { category: "compatibility", formulajs: true }, GAMMALN: { category: "statistical", formulajs: true }, GAMMALNPRECISE: { category: "statistical", formulajs: true }, GAUSS: { category: "statistical", formulajs: true }, GCD: { category: "math", formulajs: true }, GEOMEAN: { category: "statistical", formulajs: true }, GESTEP: { category: "engineering", formulajs: true }, GETPIVOTDATA: { category: "lookup", formulajs: false }, GROUPBY: { category: "lookup", formulajs: false }, GROWTH: { category: "statistical", formulajs: true }, HARMEAN: { category: "statistical", formulajs: true }, HEX2BIN: { category: "engineering", formulajs: true }, HEX2DEC: { category: "engineering", formulajs: true }, HEX2OCT: { category: "engineering", formulajs: true }, HLOOKUP: { category: "lookup", formulajs: true }, HOUR: { category: "date", formulajs: true }, HSTACK: { category: "lookup", formulajs: false }, HYPERLINK: { category: "lookup", formulajs: false }, HYPGEOMDIST: { category: "compatibility", formulajs: true }, IF: { category: "logical", formulajs: true }, IFERROR: { category: "logical", formulajs: true }, IFNA: { category: "logical", formulajs: true }, IFS: { category: "logical", formulajs: true }, IMABS: { category: "engineering", formulajs: true }, IMAGE: { category: "lookup", formulajs: false }, IMAGINARY: { category: "engineering", formulajs: true }, IMARGUMENT: { category: "engineering", formulajs: true }, IMCONJUGATE: { category: "engineering", formulajs: true }, IMCOS: { category: "engineering", formulajs: true }, IMCOSH: { category: "engineering", formulajs: true }, IMCOT: { category: "engineering", formulajs: true }, IMCSC: { category: "engineering", formulajs: true }, IMCSCH: { category: "engineering", formulajs: true }, IMDIV: { category: "engineering", formulajs: true }, IMEXP: { category: "engineering", formulajs: true }, IMLN: { category: "engineering", formulajs: true }, IMLOG10: { category: "engineering", formulajs: true }, IMLOG2: { category: "engineering", formulajs: true }, IMPOWER: { category: "engineering", formulajs: true }, IMPRODUCT: { category: "engineering", formulajs: true }, IMREAL: { category: "engineering", formulajs: true }, IMSEC: { category: "engineering", formulajs: true }, IMSECH: { category: "engineering", formulajs: true }, IMSIN: { category: "engineering", formulajs: true }, IMSINH: { category: "engineering", formulajs: true }, IMSQRT: { category: "engineering", formulajs: true }, IMSUB: { category: "engineering", formulajs: true }, IMSUM: { category: "engineering", formulajs: true }, IMTAN: { category: "engineering", formulajs: true }, INDEX: { category: "lookup", formulajs: true }, INDIRECT: { category: "lookup", formulajs: false }, INFO: { category: "information", formulajs: false }, INT: { category: "math", formulajs: true }, INTERCEPT: { category: "statistical", formulajs: true }, INTRATE: { category: "financial", formulajs: false }, IPMT: { category: "financial", formulajs: true }, IRR: { category: "financial", formulajs: true }, ISBLANK: { category: "information", formulajs: true }, ISERR: { category: "information", formulajs: true }, ISERROR: { category: "information", formulajs: true }, ISEVEN: { category: "information", formulajs: true }, ISFORMULA: { category: "information", formulajs: false }, ISLOGICAL: { category: "information", formulajs: true }, ISNA: { category: "information", formulajs: true }, ISNONTEXT: { category: "information", formulajs: true }, ISNUMBER: { category: "information", formulajs: true }, ISODD: { category: "information", formulajs: true }, ISOMITTED: { category: "information", formulajs: false }, ISREF: { category: "information", formulajs: false }, ISTEXT: { category: "information", formulajs: true }, ISOCEILING: { category: "math", formulajs: true }, ISOWEEKNUM: { category: "date", formulajs: true }, ISPMT: { category: "financial", formulajs: true }, JIS: { category: "text", formulajs: false }, KURT: { category: "statistical", formulajs: true }, LAMBDA: { category: "logical", formulajs: false }, LARGE: { category: "statistical", formulajs: true }, LCM: { category: "math", formulajs: true }, LEFT: { category: "text", formulajs: true }, LEN: { category: "text", formulajs: true }, LET: { category: "logical", formulajs: false }, LINEST: { category: "statistical", formulajs: true }, LN: { category: "math", formulajs: true }, LOG: { category: "math", formulajs: true }, LOG10: { category: "math", formulajs: true }, LOGEST: { category: "statistical", formulajs: true }, LOGINV: { category: "compatibility", formulajs: true }, LOGNORMDIST: { category: "compatibility", formulajs: true }, LOGNORMINV: { category: "statistical", formulajs: true }, LOOKUP: { category: "lookup", formulajs: true }, LOWER: { category: "text", formulajs: true }, MAKEARRAY: { category: "logical", formulajs: false }, MAP: { category: "logical", formulajs: false }, MATCH: { category: "lookup", formulajs: true }, MAX: { category: "statistical", formulajs: true }, MAXA: { category: "statistical", formulajs: true }, MAXIFS: { category: "statistical", formulajs: true }, MDETERM: { category: "math", formulajs: false }, MDURATION: { category: "financial", formulajs: false }, MEDIAN: { category: "statistical", formulajs: true }, MID: { category: "text", formulajs: true }, MIN: { category: "statistical", formulajs: true }, MINIFS: { category: "statistical", formulajs: true }, MINA: { category: "statistical", formulajs: true }, MINUTE: { category: "date", formulajs: true }, MINVERSE: { category: "math", formulajs: false }, MIRR: { category: "financial", formulajs: true }, MMULT: { category: "math", formulajs: true }, MOD: { category: "math", formulajs: true }, MODE: { category: "compatibility", formulajs: false }, MODEMULT: { category: "statistical", formulajs: true }, MODESNGL: { category: "statistical", formulajs: true }, MONTH: { category: "date", formulajs: true }, MROUND: { category: "math", formulajs: true }, MULTINOMIAL: { category: "math", formulajs: true }, MUNIT: { category: "math", formulajs: true }, N: { category: "information", formulajs: true }, NA: { category: "information", formulajs: true }, NEGBINOMDIST: { category: "compatibility", formulajs: true }, NETWORKDAYS: { category: "date", formulajs: true }, NETWORKDAYSINTL: { category: "date", formulajs: true }, NOMINAL: { category: "financial", formulajs: true }, NORMDIST: { category: "compatibility", formulajs: true }, NORMINV: { category: "compatibility", formulajs: true }, NORMSDIST: { category: "compatibility", formulajs: true }, NORMSINV: { category: "compatibility", formulajs: true }, NOT: { category: "logical", formulajs: true }, NOW: { category: "date", formulajs: true }, NPER: { category: "financial", formulajs: true }, NPV: { category: "financial", formulajs: true }, NUMBERVALUE: { category: "text", formulajs: true }, OCT2BIN: { category: "engineering", formulajs: true }, OCT2DEC: { category: "engineering", formulajs: true }, OCT2HEX: { category: "engineering", formulajs: true }, ODD: { category: "math", formulajs: true }, ODDFPRICE: { category: "financial", formulajs: false }, ODDFYIELD: { category: "financial", formulajs: false }, ODDLPRICE: { category: "financial", formulajs: false }, ODDLYIELD: { category: "financial", formulajs: false }, OFFSET: { category: "lookup", formulajs: false }, OR: { category: "logical", formulajs: true }, PDURATION: { category: "financial", formulajs: true }, PEARSON: { category: "statistical", formulajs: true }, PERCENTILEEXC: { category: "statistical", formulajs: true }, PERCENTILEINC: { category: "statistical", formulajs: true }, PERCENTILE: { category: "compatibility", formulajs: false }, PERCENTOF: { category: "math", formulajs: false }, PERCENTRANKEXC: { category: "statistical", formulajs: true }, PERCENTRANKINC: { category: "statistical", formulajs: true }, PERCENTRANK: { category: "compatibility", formulajs: false }, PERMUT: { category: "statistical", formulajs: true }, PERMUTATIONA: { category: "statistical", formulajs: true }, PHI: { category: "statistical", formulajs: true }, PHONETIC: { category: "text", formulajs: false }, PI: { category: "math", formulajs: true }, PIVOTBY: { category: "lookup", formulajs: false }, PMT: { category: "financial", formulajs: true }, POISSONDIST: { category: "statistical", formulajs: true }, POISSON: { category: "compatibility", formulajs: false }, POWER: { category: "math", formulajs: true }, PPMT: { category: "financial", formulajs: true }, PRICE: { category: "financial", formulajs: false }, PRICEDISC: { category: "financial", formulajs: true }, PRICEMAT: { category: "financial", formulajs: false }, PROB: { category: "statistical", formulajs: true }, PRODUCT: { category: "math", formulajs: true }, PROPER: { category: "text", formulajs: true }, PV: { category: "financial", formulajs: true }, QUARTILE: { category: "compatibility", formulajs: false }, QUARTILEEXC: { category: "statistical", formulajs: true }, QUARTILEINC: { category: "statistical", formulajs: true }, QUOTIENT: { category: "math", formulajs: true }, RADIANS: { category: "math", formulajs: true }, RAND: { category: "math", formulajs: true }, RANDARRAY: { category: "math", formulajs: false }, RANDBETWEEN: { category: "math", formulajs: true }, RANKAVG: { category: "statistical", formulajs: true }, RANKEQ: { category: "statistical", formulajs: true }, RANK: { category: "compatibility", formulajs: false }, RATE: { category: "financial", formulajs: true }, RECEIVED: { category: "financial", formulajs: false }, REDUCE: { category: "logical", formulajs: false }, REGEXEXTRACT: { category: "text", formulajs: false }, REGEXREPLACE: { category: "text", formulajs: false }, REGEXTEST: { category: "text", formulajs: false }, REPLACE: { category: "text", formulajs: true }, REPT: { category: "text", formulajs: true }, RIGHT: { category: "text", formulajs: true }, ROMAN: { category: "math", formulajs: true }, ROUND: { category: "math", formulajs: true }, ROUNDDOWN: { category: "math", formulajs: true }, ROUNDUP: { category: "math", formulajs: true }, ROW: { category: "lookup", formulajs: true }, ROWS: { category: "lookup", formulajs: true }, RRI: { category: "financial", formulajs: true }, RSQ: { category: "statistical", formulajs: true }, RTD: { category: "lookup", formulajs: false }, SCAN: { category: "logical", formulajs: false }, SEARCH: { category: "text", formulajs: true }, SEC: { category: "math", formulajs: true }, SECH: { category: "math", formulajs: true }, SECOND: { category: "date", formulajs: true }, SEQUENCE: { category: "math", formulajs: false }, SERIESSUM: { category: "math", formulajs: true }, SHEET: { category: "information", formulajs: false }, SHEETS: { category: "information", formulajs: false }, SIGN: { category: "math", formulajs: true }, SIN: { category: "math", formulajs: true }, SINH: { category: "math", formulajs: true }, SKEW: { category: "statistical", formulajs: true }, SKEWP: { category: "statistical", formulajs: true }, SLN: { category: "financial", formulajs: true }, SLOPE: { category: "statistical", formulajs: true }, SMALL: { category: "statistical", formulajs: true }, SORT: { category: "lookup", formulajs: true }, SORTBY: { category: "lookup", formulajs: false }, SQRT: { category: "math", formulajs: true }, SQRTPI: { category: "math", formulajs: true }, STANDARDIZE: { category: "statistical", formulajs: true }, STOCKHISTORY: { category: "information", formulajs: false }, STDEV: { category: "compatibility", formulajs: false }, STDEVP: { category: "compatibility", formulajs: true }, STDEVS: { category: "statistical", formulajs: true }, STDEVA: { category: "statistical", formulajs: true }, STDEVPA: { category: "statistical", formulajs: true }, STEYX: { category: "statistical", formulajs: true }, SUBSTITUTE: { category: "text", formulajs: true }, SUBTOTAL: { category: "math", formulajs: true }, SUM: { category: "math", formulajs: true }, SUMIF: { category: "math", formulajs: true }, SUMIFS: { category: "math", formulajs: true }, SUMPRODUCT: { category: "math", formulajs: true }, SUMSQ: { category: "math", formulajs: true }, SUMX2MY2: { category: "math", formulajs: true }, SUMX2PY2: { category: "math", formulajs: true }, SUMXMY2: { category: "math", formulajs: true }, SWITCH: { category: "logical", formulajs: true }, SYD: { category: "financial", formulajs: true }, T: { category: "text", formulajs: true }, TAN: { category: "math", formulajs: true }, TANH: { category: "math", formulajs: true }, TAKE: { category: "lookup", formulajs: false }, TBILLEQ: { category: "financial", formulajs: true }, TBILLPRICE: { category: "financial", formulajs: true }, TBILLYIELD: { category: "financial", formulajs: true }, TDIST: { category: "compatibility", formulajs: true }, TDIST2T: { category: "statistical", formulajs: true }, TDISTRT: { category: "statistical", formulajs: true }, TEXT: { category: "text", formulajs: true }, TEXTAFTER: { category: "text", formulajs: false }, TEXTBEFORE: { category: "text", formulajs: false }, TEXTJOIN: { category: "text", formulajs: true }, TEXTSPLIT: { category: "text", formulajs: false }, TIME: { category: "date", formulajs: true }, TIMEVALUE: { category: "date", formulajs: true }, TINV: { category: "compatibility", formulajs: true }, TINV2T: { category: "statistical", formulajs: true }, TOCOL: { category: "lookup", formulajs: false }, TOROW: { category: "lookup", formulajs: false }, TODAY: { category: "date", formulajs: true }, TRANSLATE: { category: "text", formulajs: false }, TRANSPOSE: { category: "lookup", formulajs: true }, TREND: { category: "statistical", formulajs: true }, TRIM: { category: "text", formulajs: true }, TRIMMEAN: { category: "statistical", formulajs: true }, TRIMRANGE: { category: "lookup", formulajs: false }, TRUE: { category: "logical", formulajs: true }, TRUNC: { category: "math", formulajs: true }, TTEST: { category: "compatibility", formulajs: true }, TYPE: { category: "information", formulajs: true }, UNICHAR: { category: "text", formulajs: true }, UNICODE: { category: "text", formulajs: true }, UNIQUE: { category: "lookup", formulajs: true }, UPPER: { category: "text", formulajs: true }, VALUE: { category: "text", formulajs: true }, VALUETOTEXT: { category: "text", formulajs: false }, VAR: { category: "compatibility", formulajs: false }, VARP: { category: "compatibility", formulajs: true }, VARS: { category: "statistical", formulajs: true }, VARA: { category: "statistical", formulajs: true }, VARPA: { category: "statistical", formulajs: true }, VDB: { category: "financial", formulajs: false }, VLOOKUP: { category: "lookup", formulajs: true }, WEEKDAY: { category: "date", formulajs: true }, WEEKNUM: { category: "date", formulajs: true }, WEIBULL: { category: "compatibility", formulajs: false }, WEIBULLDIST: { category: "statistical", formulajs: true }, WORKDAY: { category: "date", formulajs: true }, WORKDAYINTL: { category: "date", formulajs: true }, XIRR: { category: "financial", formulajs: true }, XLOOKUP: { category: "lookup", formulajs: false }, XMATCH: { category: "lookup", formulajs: false }, XNPV: { category: "financial", formulajs: true }, XOR: { category: "logical", formulajs: true }, YEAR: { category: "date", formulajs: true }, YEARFRAC: { category: "date", formulajs: true }, YIELD: { category: "financial", formulajs: false }, YIELDDISC: { category: "financial", formulajs: false }, YIELDMAT: { category: "financial", formulajs: false }, ZTEST: { category: "compatibility", formulajs: true } };
|
|
377
|
+
|
|
378
|
+
// src/registry/meta.ts
|
|
379
|
+
var FORMULA_META_OVERRIDES = [
|
|
380
|
+
{
|
|
381
|
+
name: "SUM",
|
|
382
|
+
category: "statistical",
|
|
383
|
+
label: "\u6C42\u548C",
|
|
384
|
+
syntax: "SUM(\u6570\u503C1, [\u6570\u503C2], \u2026)",
|
|
385
|
+
hint: "\u8FD4\u56DE\u4E00\u7EC4\u6570\u503C\u548C/\u6216\u5355\u5143\u683C\u7684\u603B\u548C\u3002",
|
|
386
|
+
example: "SUM(A1:A10)",
|
|
387
|
+
implemented: true,
|
|
388
|
+
featured: true
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: "AVERAGE",
|
|
392
|
+
category: "statistical",
|
|
393
|
+
label: "\u5E73\u5747\u503C",
|
|
394
|
+
syntax: "AVERAGE(\u6570\u503C1, [\u6570\u503C2], \u2026)",
|
|
395
|
+
hint: "\u8FD4\u56DE\u5176\u53C2\u6570\u7684\u7B97\u672F\u5E73\u5747\u503C\u3002",
|
|
396
|
+
example: "AVERAGE(A1:A10)",
|
|
397
|
+
implemented: true,
|
|
398
|
+
featured: true
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "COUNT",
|
|
402
|
+
category: "statistical",
|
|
403
|
+
label: "\u8BA1\u6570",
|
|
404
|
+
syntax: "COUNT(\u6570\u503C1, [\u6570\u503C2], \u2026)",
|
|
405
|
+
hint: "\u8BA1\u7B97\u53C2\u6570\u5217\u8868\u4E2D\u6570\u5B57\u7684\u4E2A\u6570\u3002",
|
|
406
|
+
example: "COUNT(A1:A10)",
|
|
407
|
+
implemented: true,
|
|
408
|
+
featured: true
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: "MAX",
|
|
412
|
+
category: "statistical",
|
|
413
|
+
label: "\u6700\u5927\u503C",
|
|
414
|
+
syntax: "MAX(\u6570\u503C1, [\u6570\u503C2], \u2026)",
|
|
415
|
+
hint: "\u8FD4\u56DE\u53C2\u6570\u5217\u8868\u4E2D\u7684\u6700\u5927\u503C\u3002",
|
|
416
|
+
implemented: true,
|
|
417
|
+
featured: true
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
name: "MIN",
|
|
421
|
+
category: "statistical",
|
|
422
|
+
label: "\u6700\u5C0F\u503C",
|
|
423
|
+
syntax: "MIN(\u6570\u503C1, [\u6570\u503C2], \u2026)",
|
|
424
|
+
hint: "\u8FD4\u56DE\u53C2\u6570\u5217\u8868\u4E2D\u7684\u6700\u5C0F\u503C\u3002",
|
|
425
|
+
implemented: true,
|
|
426
|
+
featured: true
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
name: "IF",
|
|
430
|
+
category: "logical",
|
|
431
|
+
label: "\u6761\u4EF6",
|
|
432
|
+
syntax: "IF(\u6761\u4EF6, \u771F\u503C, [\u5047\u503C])",
|
|
433
|
+
hint: "\u5224\u65AD\u662F\u5426\u6EE1\u8DB3\u67D0\u4E2A\u6761\u4EF6\uFF0C\u8FD4\u56DE\u4E0D\u540C\u7ED3\u679C\u3002",
|
|
434
|
+
featured: true
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: "AND",
|
|
438
|
+
category: "logical",
|
|
439
|
+
label: "\u4E14",
|
|
440
|
+
syntax: "AND(\u903B\u8F911, [\u903B\u8F912], \u2026)",
|
|
441
|
+
hint: "\u6240\u6709\u53C2\u6570\u4E3A TRUE \u65F6\u8FD4\u56DE TRUE\u3002",
|
|
442
|
+
featured: true
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: "OR",
|
|
446
|
+
category: "logical",
|
|
447
|
+
label: "\u6216",
|
|
448
|
+
syntax: "OR(\u903B\u8F911, [\u903B\u8F912], \u2026)",
|
|
449
|
+
hint: "\u4EFB\u4E00\u53C2\u6570\u4E3A TRUE \u65F6\u8FD4\u56DE TRUE\u3002",
|
|
450
|
+
featured: true
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: "ABS",
|
|
454
|
+
category: "math",
|
|
455
|
+
label: "\u7EDD\u5BF9\u503C",
|
|
456
|
+
syntax: "ABS(\u6570\u503C)",
|
|
457
|
+
hint: "\u8FD4\u56DE\u6570\u5B57\u7684\u7EDD\u5BF9\u503C\u3002"
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
name: "ROUND",
|
|
461
|
+
category: "math",
|
|
462
|
+
label: "\u56DB\u820D\u4E94\u5165",
|
|
463
|
+
syntax: "ROUND(\u6570\u503C, \u4F4D\u6570)",
|
|
464
|
+
hint: "\u6309\u6307\u5B9A\u4F4D\u6570\u56DB\u820D\u4E94\u5165\u3002"
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
name: "VLOOKUP",
|
|
468
|
+
category: "lookup",
|
|
469
|
+
label: "\u5782\u76F4\u67E5\u627E",
|
|
470
|
+
syntax: "VLOOKUP(\u67E5\u627E\u503C, \u8868\u533A\u57DF, \u5217\u5E8F, [\u5339\u914D])",
|
|
471
|
+
hint: "\u5728\u8868\u9996\u5217\u67E5\u627E\u5E76\u8FD4\u56DE\u540C\u884C\u6307\u5B9A\u5217\u3002"
|
|
472
|
+
}
|
|
473
|
+
];
|
|
474
|
+
function getMetaOverrideMap() {
|
|
475
|
+
return new Map(FORMULA_META_OVERRIDES.map((m) => [m.name, m]));
|
|
476
|
+
}
|
|
477
|
+
function defaultCategory() {
|
|
478
|
+
return "math";
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/registry/buildRegistry.ts
|
|
482
|
+
var catalogMap = formulajs_catalog_default;
|
|
483
|
+
function getFormulajsExportNames() {
|
|
484
|
+
const lib = formulajs;
|
|
485
|
+
return Object.keys(lib).filter((k) => /^[A-Z][A-Z0-9_]*$/.test(k) && typeof lib[k] === "function").sort();
|
|
486
|
+
}
|
|
487
|
+
function buildEntry(name) {
|
|
488
|
+
if (!catalogMap[name]) return null;
|
|
489
|
+
const row = catalogMap[name];
|
|
490
|
+
const override = getMetaOverrideMap().get(name);
|
|
491
|
+
const category = override?.category ?? row.category ?? defaultCategory();
|
|
492
|
+
return {
|
|
493
|
+
name,
|
|
494
|
+
category,
|
|
495
|
+
label: override?.label ?? name,
|
|
496
|
+
labelEn: override?.labelEn,
|
|
497
|
+
syntax: override?.syntax ?? `${name}(\u2026)`,
|
|
498
|
+
hint: override?.hint ?? "Excel \u517C\u5BB9\u51FD\u6570\uFF08@formulajs/formulajs\uFF09\u3002\u8BF4\u660E\u5F85\u8865\u5145\uFF0C\u53EF\u53C2\u8003 formulajs.info\u3002",
|
|
499
|
+
description: override?.description,
|
|
500
|
+
example: override?.example,
|
|
501
|
+
seeAlso: override?.seeAlso,
|
|
502
|
+
aliases: override?.aliases,
|
|
503
|
+
implemented: override?.implemented ?? false,
|
|
504
|
+
formulajs: row.formulajs,
|
|
505
|
+
featured: override?.featured ?? false
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function buildFormulaBuiltinRegistry() {
|
|
509
|
+
const names = getFormulajsExportNames();
|
|
510
|
+
const out = [];
|
|
511
|
+
for (const name of names) {
|
|
512
|
+
const entry = buildEntry(name);
|
|
513
|
+
if (entry) out.push(entry);
|
|
514
|
+
}
|
|
515
|
+
return out;
|
|
516
|
+
}
|
|
517
|
+
var FORMULA_BUILTIN_REGISTRY = buildFormulaBuiltinRegistry();
|
|
518
|
+
|
|
519
|
+
// src/registry/categories.ts
|
|
520
|
+
var FORMULA_CATEGORIES = [
|
|
521
|
+
{ id: "math", label: "\u6570\u5B66", labelEn: "Math", formulajsCategory: "Math and trigonometry" },
|
|
522
|
+
{ id: "statistical", label: "\u7EDF\u8BA1", labelEn: "Statistics", formulajsCategory: "Statistical" },
|
|
523
|
+
{ id: "financial", label: "\u8D22\u52A1", labelEn: "Financial", formulajsCategory: "Financial" },
|
|
524
|
+
{ id: "engineering", label: "\u5DE5\u7A0B", labelEn: "Engineering", formulajsCategory: "Engineering" },
|
|
525
|
+
{ id: "text", label: "\u6587\u672C", labelEn: "Text", formulajsCategory: "Text" },
|
|
526
|
+
{ id: "logical", label: "\u903B\u8F91", labelEn: "Logical", formulajsCategory: "Logical" },
|
|
527
|
+
{ id: "date", label: "\u65E5\u671F", labelEn: "Date", formulajsCategory: "Date and time" },
|
|
528
|
+
{ id: "lookup", label: "\u67E5\u627E", labelEn: "Lookup", formulajsCategory: "Lookup and reference" },
|
|
529
|
+
{ id: "information", label: "\u4FE1\u606F", labelEn: "Information", formulajsCategory: "Information" },
|
|
530
|
+
{ id: "database", label: "\u6570\u636E\u5E93", labelEn: "Database", formulajsCategory: "Database" },
|
|
531
|
+
{ id: "compatibility", label: "\u517C\u5BB9", labelEn: "Compatibility", formulajsCategory: "Compatibility" }
|
|
532
|
+
];
|
|
533
|
+
function getCategoryMeta(id) {
|
|
534
|
+
return FORMULA_CATEGORIES.find((c) => c.id === id);
|
|
535
|
+
}
|
|
536
|
+
function categoryLabel(id, locale = "zh") {
|
|
537
|
+
const meta = getCategoryMeta(id);
|
|
538
|
+
if (!meta) return id;
|
|
539
|
+
return locale === "en" ? meta.labelEn : meta.label;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/registry/index.ts
|
|
543
|
+
var _canonical = /* @__PURE__ */ new Map();
|
|
544
|
+
function rebuildCanonical() {
|
|
545
|
+
if (_canonical.size) return;
|
|
546
|
+
for (const b of FORMULA_BUILTIN_REGISTRY) {
|
|
547
|
+
const upper = b.name.toUpperCase();
|
|
548
|
+
_canonical.set(upper, upper);
|
|
549
|
+
for (const a of b.aliases ?? []) {
|
|
550
|
+
_canonical.set(a.toUpperCase(), upper);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
function resolveBuiltinName(name) {
|
|
555
|
+
rebuildCanonical();
|
|
556
|
+
return _canonical.get(name.toUpperCase());
|
|
557
|
+
}
|
|
558
|
+
function isRegisteredBuiltin(name) {
|
|
559
|
+
rebuildCanonical();
|
|
560
|
+
return _canonical.has(name.toUpperCase());
|
|
561
|
+
}
|
|
562
|
+
function getBuiltinEntry(name) {
|
|
563
|
+
const canonical = resolveBuiltinName(name);
|
|
564
|
+
if (!canonical) return void 0;
|
|
565
|
+
return FORMULA_BUILTIN_REGISTRY.find((b) => b.name === canonical);
|
|
566
|
+
}
|
|
567
|
+
function getFeaturedBuiltins() {
|
|
568
|
+
return FORMULA_BUILTIN_REGISTRY.filter((b) => b.featured);
|
|
569
|
+
}
|
|
570
|
+
function getBuiltinsByCategory(category) {
|
|
571
|
+
return FORMULA_BUILTIN_REGISTRY.filter((b) => b.category === category);
|
|
572
|
+
}
|
|
573
|
+
function getImplementedBuiltins() {
|
|
574
|
+
return FORMULA_BUILTIN_REGISTRY.filter((b) => b.implemented);
|
|
575
|
+
}
|
|
576
|
+
function getImplementedBuiltinNames() {
|
|
577
|
+
return getImplementedBuiltins().map((b) => b.name);
|
|
578
|
+
}
|
|
579
|
+
function getCategoriesWithBuiltins() {
|
|
580
|
+
const ids = /* @__PURE__ */ new Set();
|
|
581
|
+
for (const b of FORMULA_BUILTIN_REGISTRY) ids.add(b.category);
|
|
582
|
+
return FORMULA_CATEGORIES.map((c) => c.id).filter((id) => ids.has(id));
|
|
583
|
+
}
|
|
584
|
+
function searchBuiltins(query) {
|
|
585
|
+
const q = query.trim().toUpperCase();
|
|
586
|
+
if (!q) return [...FORMULA_BUILTIN_REGISTRY];
|
|
587
|
+
return FORMULA_BUILTIN_REGISTRY.filter((b) => {
|
|
588
|
+
if (b.name.includes(q)) return true;
|
|
589
|
+
if (b.label.includes(query.trim())) return true;
|
|
590
|
+
if (b.hint.includes(query.trim())) return true;
|
|
591
|
+
return (b.aliases ?? []).some((a) => a.toUpperCase().includes(q));
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
function parseActiveFunctionName(formula, caret) {
|
|
595
|
+
const head = formula.slice(0, caret);
|
|
596
|
+
const m = head.match(/([A-Za-z][\w.]*)$/);
|
|
597
|
+
if (!m) return null;
|
|
598
|
+
const name = m[1].toUpperCase().replace(/\./g, "");
|
|
599
|
+
if (!name) return null;
|
|
600
|
+
const after = head.slice(head.length - m[1].length);
|
|
601
|
+
if (!/^[A-Za-z]/.test(after)) return null;
|
|
602
|
+
return resolveBuiltinName(name) ?? null;
|
|
603
|
+
}
|
|
604
|
+
function buildFunctionNamePattern() {
|
|
605
|
+
const names = [...new Set(FORMULA_BUILTIN_REGISTRY.map((b) => b.name))];
|
|
606
|
+
const escaped = names.sort((a, b) => b.length - a.length).map(
|
|
607
|
+
(n) => n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
608
|
+
);
|
|
609
|
+
return new RegExp(`\\b(${escaped.join("|")})\\s*\\(`, "gi");
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// src/fnMap.ts
|
|
613
|
+
function buildFnMap() {
|
|
614
|
+
const map = {};
|
|
615
|
+
const lib = formulajs;
|
|
616
|
+
for (const name of getImplementedBuiltinNames()) {
|
|
617
|
+
const fn = lib[name];
|
|
618
|
+
if (typeof fn !== "function") continue;
|
|
619
|
+
map[name] = fn;
|
|
620
|
+
}
|
|
621
|
+
return map;
|
|
622
|
+
}
|
|
623
|
+
var FN_MAP = buildFnMap();
|
|
624
|
+
var FN_RE = buildFunctionNamePattern();
|
|
625
|
+
|
|
626
|
+
// src/edit.ts
|
|
627
|
+
var REF_TOKEN_RE = /(?:'[^']+'!)?[A-Za-z]{1,4}\d+(?::[A-Za-z]{1,4}\d+)?/gi;
|
|
628
|
+
var OP_END_RE = /[+\-*/,(]$/;
|
|
629
|
+
var REF_PICK_TRIGGER = /* @__PURE__ */ new Set([
|
|
630
|
+
"(",
|
|
631
|
+
",",
|
|
632
|
+
"=",
|
|
633
|
+
"+",
|
|
634
|
+
"-",
|
|
635
|
+
"*",
|
|
636
|
+
"/",
|
|
637
|
+
"%",
|
|
638
|
+
"&",
|
|
639
|
+
"^",
|
|
640
|
+
"<",
|
|
641
|
+
">",
|
|
642
|
+
"|",
|
|
643
|
+
":"
|
|
644
|
+
]);
|
|
645
|
+
function canPickFormulaRefAtCaret(formula, caret) {
|
|
646
|
+
if (!isFormulaInput(formula)) return false;
|
|
647
|
+
const trimmed = formula.trimStart();
|
|
648
|
+
if (trimmed === "=") return true;
|
|
649
|
+
const safeCaret = Math.max(0, Math.min(caret, formula.length));
|
|
650
|
+
const before = formula.slice(0, safeCaret).trimEnd();
|
|
651
|
+
if (!before.length) return false;
|
|
652
|
+
const last = before[before.length - 1];
|
|
653
|
+
return REF_PICK_TRIGGER.has(last);
|
|
654
|
+
}
|
|
655
|
+
function canPickFormulaRef(formula, caret, refPickSession) {
|
|
656
|
+
if (!isFormulaInput(formula)) return false;
|
|
657
|
+
return refPickSession || canPickFormulaRefAtCaret(formula, caret);
|
|
658
|
+
}
|
|
659
|
+
function isFormulaInput(text) {
|
|
660
|
+
return text.trimStart().startsWith("=");
|
|
661
|
+
}
|
|
662
|
+
var isFormulaText = isFormulaInput;
|
|
663
|
+
function getCellFormulaInitialFromCell(cell, sheet, sheetId) {
|
|
664
|
+
if (cell?.f) {
|
|
665
|
+
const f = String(cell.f);
|
|
666
|
+
if (sheet && f.startsWith("=")) {
|
|
667
|
+
const ctx = createFormulaContext(sheet);
|
|
668
|
+
const sid = sheetId ?? sheet.getActiveSheetId();
|
|
669
|
+
return internalFormulaToDisplay(f, ctx, sid);
|
|
670
|
+
}
|
|
671
|
+
return f;
|
|
672
|
+
}
|
|
673
|
+
const raw = cell?.m ?? cell?.v;
|
|
674
|
+
if (typeof raw === "string" && raw.startsWith("=")) return raw;
|
|
675
|
+
return "=";
|
|
676
|
+
}
|
|
677
|
+
function getCellFormulaInitial(sheet, r, c) {
|
|
678
|
+
return getCellFormulaInitialFromCell(
|
|
679
|
+
sheet.state.getCellData(r, c),
|
|
680
|
+
sheet,
|
|
681
|
+
sheet.getActiveSheetId()
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
function formatRangeA1(r0, c0, r1, c1) {
|
|
685
|
+
const ra = Math.min(r0, r1);
|
|
686
|
+
const rb = Math.max(r0, r1);
|
|
687
|
+
const ca = Math.min(c0, c1);
|
|
688
|
+
const cb = Math.max(c0, c1);
|
|
689
|
+
if (ra === rb && ca === cb) return formatA1(ra, ca);
|
|
690
|
+
return `${formatA1(ra, ca)}:${formatA1(rb, cb)}`;
|
|
691
|
+
}
|
|
692
|
+
function buildSheetRefToken(sheet, r0, c0, r1, c1, sheetId) {
|
|
693
|
+
const sid = sheetId ?? sheet.getActiveSheetId();
|
|
694
|
+
const activeId = sheet.getActiveSheetId();
|
|
695
|
+
const local = r1 !== void 0 && c1 !== void 0 ? formatRangeA1(r0, c0, r1, c1) : formatA1(r0, c0);
|
|
696
|
+
if (sid === activeId) return local;
|
|
697
|
+
const name = sheet.getSheetName(sid);
|
|
698
|
+
return `'${name.replace(/'/g, "''")}'!${local}`;
|
|
699
|
+
}
|
|
700
|
+
function findRefSpanAt(formula, index) {
|
|
701
|
+
REF_TOKEN_RE.lastIndex = 0;
|
|
702
|
+
let m;
|
|
703
|
+
while ((m = REF_TOKEN_RE.exec(formula)) !== null) {
|
|
704
|
+
const start = m.index;
|
|
705
|
+
const end = start + m[0].length;
|
|
706
|
+
if (overlapsInternalRef(formula, start, end)) continue;
|
|
707
|
+
if (index >= start && index <= end) return { start, end };
|
|
708
|
+
}
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
function findLastRefSpan(formula) {
|
|
712
|
+
REF_TOKEN_RE.lastIndex = 0;
|
|
713
|
+
let last = null;
|
|
714
|
+
let m;
|
|
715
|
+
while ((m = REF_TOKEN_RE.exec(formula)) !== null) {
|
|
716
|
+
const start = m.index;
|
|
717
|
+
const end = start + m[0].length;
|
|
718
|
+
if (overlapsInternalRef(formula, start, end)) continue;
|
|
719
|
+
last = { start, end };
|
|
720
|
+
}
|
|
721
|
+
return last;
|
|
722
|
+
}
|
|
723
|
+
function patchFormulaWithRef(formula, caret, refToken) {
|
|
724
|
+
if (!isFormulaInput(formula)) {
|
|
725
|
+
return { text: `=${refToken}`, caret: 1 + refToken.length };
|
|
726
|
+
}
|
|
727
|
+
const safeCaret = Math.max(0, Math.min(caret, formula.length));
|
|
728
|
+
const before = formula.slice(0, safeCaret);
|
|
729
|
+
const after = formula.slice(safeCaret);
|
|
730
|
+
if (formula === "=" || OP_END_RE.test(before.trimEnd())) {
|
|
731
|
+
const text2 = before + refToken + after;
|
|
732
|
+
return { text: text2, caret: before.length + refToken.length };
|
|
733
|
+
}
|
|
734
|
+
const span = findRefSpanAt(formula, safeCaret) ?? findRefSpanAt(formula, safeCaret - 1) ?? findLastRefSpan(formula);
|
|
735
|
+
if (span) {
|
|
736
|
+
const text2 = formula.slice(0, span.start) + refToken + formula.slice(span.end);
|
|
737
|
+
return { text: text2, caret: span.start + refToken.length };
|
|
738
|
+
}
|
|
739
|
+
const sep = OP_END_RE.test(before.trimEnd()) ? "" : "+";
|
|
740
|
+
const text = before + sep + refToken + after;
|
|
741
|
+
return { text, caret: before.length + sep.length + refToken.length };
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/evaluate.ts
|
|
745
|
+
var IDENT_FN_RE = /\b([A-Za-z_][\w.]*)\s*\(/g;
|
|
746
|
+
var NULL_RANGE_RE = /[A-Za-z]{1,4}\d+:[A-Za-z]{1,4}\d+\s+[A-Za-z]{1,4}/;
|
|
747
|
+
function splitTopLevelArgs(inner) {
|
|
748
|
+
const parts = [];
|
|
749
|
+
let depth = 0;
|
|
750
|
+
let start = 0;
|
|
751
|
+
for (let i = 0; i < inner.length; i++) {
|
|
752
|
+
const ch = inner[i];
|
|
753
|
+
if (ch === "(") depth++;
|
|
754
|
+
else if (ch === ")") depth--;
|
|
755
|
+
else if (ch === "," && depth === 0) {
|
|
756
|
+
parts.push(inner.slice(start, i).trim());
|
|
757
|
+
start = i + 1;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
parts.push(inner.slice(start).trim());
|
|
761
|
+
return parts.filter(Boolean);
|
|
762
|
+
}
|
|
763
|
+
function detectNameError(expr) {
|
|
764
|
+
IDENT_FN_RE.lastIndex = 0;
|
|
765
|
+
let m;
|
|
766
|
+
while ((m = IDENT_FN_RE.exec(expr)) !== null) {
|
|
767
|
+
const name = m[1].toUpperCase().replace(/\./g, "");
|
|
768
|
+
if (!isRegisteredBuiltin(name)) return true;
|
|
769
|
+
}
|
|
770
|
+
return false;
|
|
771
|
+
}
|
|
772
|
+
function detectNullRangeError(expr) {
|
|
773
|
+
return NULL_RANGE_RE.test(expr);
|
|
774
|
+
}
|
|
775
|
+
function resolveRefSheetId(ctx, sheetIdOrName) {
|
|
776
|
+
if (!sheetIdOrName) return ctx.activeSheetId;
|
|
777
|
+
const id = ctx.resolveSheetId(sheetIdOrName);
|
|
778
|
+
if (!id) return { error: "REF" };
|
|
779
|
+
return id;
|
|
780
|
+
}
|
|
781
|
+
function resolveArgNumbers(arg, ctx, visiting) {
|
|
782
|
+
if (arg.startsWith("#")) {
|
|
783
|
+
const ref = parseInternalRefToken(arg);
|
|
784
|
+
if (!ref) return { error: "REF" };
|
|
785
|
+
const sheetResolved = resolveRefSheetId(ctx, ref.sheetId);
|
|
786
|
+
if (typeof sheetResolved !== "string") return sheetResolved;
|
|
787
|
+
const sheetId = sheetResolved;
|
|
788
|
+
if (ref.endRowId != null && ref.endColId != null) {
|
|
789
|
+
return collectIdRangeScalars(
|
|
790
|
+
ctx,
|
|
791
|
+
sheetId,
|
|
792
|
+
ref.rowId,
|
|
793
|
+
ref.colId,
|
|
794
|
+
ref.endRowId,
|
|
795
|
+
ref.endColId,
|
|
796
|
+
visiting
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
const raw = ctx.getScalarById(sheetId, ref.rowId, ref.colId, visiting);
|
|
800
|
+
if (typeof raw === "string" && raw.startsWith("#")) return { error: "REF" };
|
|
801
|
+
if (typeof raw === "string" && raw !== "" && Number.isNaN(Number(raw))) {
|
|
802
|
+
return { error: "VALUE" };
|
|
803
|
+
}
|
|
804
|
+
return [coerceNumber(raw)];
|
|
805
|
+
}
|
|
806
|
+
const n = Number(arg);
|
|
807
|
+
if (Number.isFinite(n)) return [n];
|
|
808
|
+
if (arg.startsWith('"') || /[A-Za-z\u4e00-\u9fff]/.test(arg)) return { error: "VALUE" };
|
|
809
|
+
return [];
|
|
810
|
+
}
|
|
811
|
+
function evalFunctions(expr, ctx, visiting) {
|
|
812
|
+
let out = expr;
|
|
813
|
+
let guard = 0;
|
|
814
|
+
while (guard++ < 32) {
|
|
815
|
+
FN_RE.lastIndex = 0;
|
|
816
|
+
const m = FN_RE.exec(out);
|
|
817
|
+
if (!m) break;
|
|
818
|
+
const fnName = m[1].toUpperCase().replace(/\./g, "");
|
|
819
|
+
const fn = FN_MAP[fnName];
|
|
820
|
+
if (!fn) {
|
|
821
|
+
FN_RE.lastIndex = m.index + m[0].length;
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const open = m.index + m[0].length;
|
|
825
|
+
let depth = 1;
|
|
826
|
+
let i = open;
|
|
827
|
+
for (; i < out.length && depth > 0; i++) {
|
|
828
|
+
if (out[i] === "(") depth++;
|
|
829
|
+
else if (out[i] === ")") depth--;
|
|
830
|
+
}
|
|
831
|
+
if (depth !== 0) return { error: "ERROR" };
|
|
832
|
+
const inner = out.slice(open, i - 1);
|
|
833
|
+
const args = splitTopLevelArgs(inner);
|
|
834
|
+
const values = [];
|
|
835
|
+
for (const a of args) {
|
|
836
|
+
const resolved = resolveArgNumbers(a, ctx, visiting);
|
|
837
|
+
if (!Array.isArray(resolved)) return resolved;
|
|
838
|
+
values.push(...resolved);
|
|
839
|
+
}
|
|
840
|
+
let computed;
|
|
841
|
+
computed = fn(values);
|
|
842
|
+
if (!Number.isFinite(computed)) return { error: "NUM" };
|
|
843
|
+
const replacement = String(computed);
|
|
844
|
+
out = out.slice(0, m.index) + replacement + out.slice(i);
|
|
845
|
+
}
|
|
846
|
+
return out;
|
|
847
|
+
}
|
|
848
|
+
function replaceInternalRefs(expr, ctx, visiting) {
|
|
849
|
+
const tokens = extractInternalRefTokens(expr);
|
|
850
|
+
let out = expr;
|
|
851
|
+
for (const token of tokens) {
|
|
852
|
+
const ref = parseInternalRefToken(token);
|
|
853
|
+
if (!ref) continue;
|
|
854
|
+
const sheetResolved = resolveRefSheetId(ctx, ref.sheetId);
|
|
855
|
+
if (typeof sheetResolved !== "string") return sheetResolved;
|
|
856
|
+
const sheetId = sheetResolved;
|
|
857
|
+
if (ref.endRowId != null && ref.endColId != null) {
|
|
858
|
+
const nums = collectIdRangeScalars(
|
|
859
|
+
ctx,
|
|
860
|
+
sheetId,
|
|
861
|
+
ref.rowId,
|
|
862
|
+
ref.colId,
|
|
863
|
+
ref.endRowId,
|
|
864
|
+
ref.endColId,
|
|
865
|
+
visiting
|
|
866
|
+
);
|
|
867
|
+
const sumFn = FN_MAP.SUM;
|
|
868
|
+
if (!sumFn) return { error: "NAME" };
|
|
869
|
+
const sum = sumFn(nums);
|
|
870
|
+
if (!Number.isFinite(sum)) return { error: "NUM" };
|
|
871
|
+
out = out.split(token).join(String(sum));
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
const raw = ctx.getScalarById(sheetId, ref.rowId, ref.colId, visiting);
|
|
875
|
+
if (typeof raw === "string" && raw.startsWith("#")) return { error: "REF" };
|
|
876
|
+
if (typeof raw === "string" && raw !== "" && Number.isNaN(Number(raw))) {
|
|
877
|
+
return { error: "VALUE" };
|
|
878
|
+
}
|
|
879
|
+
const v = coerceNumber(raw);
|
|
880
|
+
out = out.split(token).join(String(v));
|
|
881
|
+
}
|
|
882
|
+
return out;
|
|
883
|
+
}
|
|
884
|
+
function safeArithmetic(expr) {
|
|
885
|
+
const trimmed = expr.replace(/\s/g, "");
|
|
886
|
+
if (!trimmed) return formulaErrorResult("ERROR");
|
|
887
|
+
if (/[a-zA-Z"]/.test(trimmed)) {
|
|
888
|
+
return formulaErrorResult("VALUE");
|
|
889
|
+
}
|
|
890
|
+
if (!/^[0-9+\-*/().]+$/.test(trimmed)) {
|
|
891
|
+
return formulaErrorResult("ERROR");
|
|
892
|
+
}
|
|
893
|
+
if (/\/\s*0+(?:\.0*)?(?:[+\-*/)]|$)/.test(trimmed)) {
|
|
894
|
+
return formulaErrorResult("DIV0");
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
const v = new Function(`return (${trimmed})`)();
|
|
898
|
+
if (typeof v !== "number" || Number.isNaN(v)) {
|
|
899
|
+
return formulaErrorResult("VALUE");
|
|
900
|
+
}
|
|
901
|
+
if (!Number.isFinite(v)) {
|
|
902
|
+
return formulaErrorResult(Math.abs(v) === Infinity ? "DIV0" : "NUM");
|
|
903
|
+
}
|
|
904
|
+
const m = Number.isInteger(v) ? String(v) : String(Math.round(v * 1e9) / 1e9);
|
|
905
|
+
return { value: v, m };
|
|
906
|
+
} catch {
|
|
907
|
+
return formulaErrorResult("ERROR");
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function evaluateFormulaString(raw, ctx, visiting = /* @__PURE__ */ new Set()) {
|
|
911
|
+
const text = raw.trim();
|
|
912
|
+
if (!text.startsWith("=")) {
|
|
913
|
+
return { value: text, m: text };
|
|
914
|
+
}
|
|
915
|
+
let expr = text.slice(1).trim();
|
|
916
|
+
if (!expr) {
|
|
917
|
+
return formulaErrorResult("ERROR");
|
|
918
|
+
}
|
|
919
|
+
if (detectNullRangeError(expr)) {
|
|
920
|
+
return formulaErrorResult("NULL");
|
|
921
|
+
}
|
|
922
|
+
if (detectNameError(expr)) {
|
|
923
|
+
return formulaErrorResult("NAME");
|
|
924
|
+
}
|
|
925
|
+
try {
|
|
926
|
+
const afterFn = evalFunctions(expr, ctx, visiting);
|
|
927
|
+
if (typeof afterFn !== "string") {
|
|
928
|
+
return formulaErrorResult(afterFn.error);
|
|
929
|
+
}
|
|
930
|
+
expr = afterFn;
|
|
931
|
+
if (hasInternalRefs(expr)) {
|
|
932
|
+
const afterRefs = replaceInternalRefs(expr, ctx, visiting);
|
|
933
|
+
if (typeof afterRefs !== "string") {
|
|
934
|
+
return formulaErrorResult(afterRefs.error);
|
|
935
|
+
}
|
|
936
|
+
expr = afterRefs;
|
|
937
|
+
}
|
|
938
|
+
return safeArithmetic(expr);
|
|
939
|
+
} catch {
|
|
940
|
+
return formulaErrorResult("ERROR");
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// src/result.ts
|
|
945
|
+
function cellPatchFromFormulaResult(formula, result) {
|
|
946
|
+
const f = formula.trim();
|
|
947
|
+
if (result.error) {
|
|
948
|
+
return {
|
|
949
|
+
f,
|
|
950
|
+
v: null,
|
|
951
|
+
m: result.m,
|
|
952
|
+
ef: result.error,
|
|
953
|
+
em: result.errorMessage ?? getFormulaErrorMessage(result.error)
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
return {
|
|
957
|
+
f,
|
|
958
|
+
v: result.value,
|
|
959
|
+
m: result.m,
|
|
960
|
+
ef: void 0,
|
|
961
|
+
em: void 0
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// src/refSpans.ts
|
|
966
|
+
var FORMULA_REF_COLORS = [
|
|
967
|
+
"#1a73e8",
|
|
968
|
+
"#0d9d57",
|
|
969
|
+
"#e37400",
|
|
970
|
+
"#9c27b0",
|
|
971
|
+
"#d93025"
|
|
972
|
+
];
|
|
973
|
+
function spansForTokens(formula, tokens) {
|
|
974
|
+
const spans = [];
|
|
975
|
+
let searchFrom = 0;
|
|
976
|
+
tokens.forEach((token, i) => {
|
|
977
|
+
const idx = formula.indexOf(token, searchFrom);
|
|
978
|
+
if (idx < 0) return;
|
|
979
|
+
spans.push({
|
|
980
|
+
start: idx,
|
|
981
|
+
end: idx + token.length,
|
|
982
|
+
token,
|
|
983
|
+
color: FORMULA_REF_COLORS[i % FORMULA_REF_COLORS.length]
|
|
984
|
+
});
|
|
985
|
+
searchFrom = idx + token.length;
|
|
986
|
+
});
|
|
987
|
+
return spans;
|
|
988
|
+
}
|
|
989
|
+
function getFormulaRefSpans(formula) {
|
|
990
|
+
if (hasInternalRefs(formula)) {
|
|
991
|
+
return spansForTokens(formula, extractInternalRefTokens(formula));
|
|
992
|
+
}
|
|
993
|
+
return spansForTokens(formula, extractRefTokens(formula));
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// src/engine.ts
|
|
997
|
+
var HIGHLIGHT_COLORS = [...FORMULA_REF_COLORS];
|
|
998
|
+
function refToHighlight(ctx, sheetId, r, c, color) {
|
|
999
|
+
return { sheetId, row: [r, r], column: [c, c], color };
|
|
1000
|
+
}
|
|
1001
|
+
function buildHighlightsFromFormula(formula, ctx) {
|
|
1002
|
+
const out = [];
|
|
1003
|
+
if (hasInternalRefs(formula)) {
|
|
1004
|
+
const tokens2 = extractInternalRefTokens(formula);
|
|
1005
|
+
tokens2.forEach((token, i) => {
|
|
1006
|
+
const ref = parseInternalRefToken(token);
|
|
1007
|
+
if (!ref) return;
|
|
1008
|
+
const sheetId = ref.sheetId ?? ctx.activeSheetId;
|
|
1009
|
+
const color = HIGHLIGHT_COLORS[i % HIGHLIGHT_COLORS.length];
|
|
1010
|
+
if (ref.endRowId != null && ref.endColId != null) {
|
|
1011
|
+
const cells = ctx.expandIdRange(
|
|
1012
|
+
sheetId,
|
|
1013
|
+
ref.rowId,
|
|
1014
|
+
ref.colId,
|
|
1015
|
+
ref.endRowId,
|
|
1016
|
+
ref.endColId
|
|
1017
|
+
);
|
|
1018
|
+
if (!cells.length) return;
|
|
1019
|
+
let rMin = Infinity;
|
|
1020
|
+
let rMax = -Infinity;
|
|
1021
|
+
let cMin = Infinity;
|
|
1022
|
+
let cMax = -Infinity;
|
|
1023
|
+
for (const { rowId, colId } of cells) {
|
|
1024
|
+
const pos = ctx.idsToDisplay(sheetId, rowId, colId);
|
|
1025
|
+
if (!pos) continue;
|
|
1026
|
+
rMin = Math.min(rMin, pos.r);
|
|
1027
|
+
rMax = Math.max(rMax, pos.r);
|
|
1028
|
+
cMin = Math.min(cMin, pos.c);
|
|
1029
|
+
cMax = Math.max(cMax, pos.c);
|
|
1030
|
+
}
|
|
1031
|
+
if (rMin !== Infinity) {
|
|
1032
|
+
out.push({ sheetId, row: [rMin, rMax], column: [cMin, cMax], color });
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
const pos = ctx.idsToDisplay(sheetId, ref.rowId, ref.colId);
|
|
1036
|
+
if (pos) out.push(refToHighlight(ctx, sheetId, pos.r, pos.c, color));
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
return out;
|
|
1040
|
+
}
|
|
1041
|
+
const tokens = extractRefTokens(formula);
|
|
1042
|
+
tokens.forEach((token, i) => {
|
|
1043
|
+
const ref = parseRefToken(token);
|
|
1044
|
+
if (!ref) return;
|
|
1045
|
+
const sheetId = ctx.resolveSheetId(ref.sheet) ?? ctx.activeSheetId;
|
|
1046
|
+
const color = HIGHLIGHT_COLORS[i % HIGHLIGHT_COLORS.length];
|
|
1047
|
+
if (ref.range) {
|
|
1048
|
+
out.push({ sheetId, row: ref.range.row, column: ref.range.column, color });
|
|
1049
|
+
} else if (ref.cell) {
|
|
1050
|
+
out.push(refToHighlight(ctx, sheetId, ref.cell.r, ref.cell.c, color));
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
return out;
|
|
1054
|
+
}
|
|
1055
|
+
function recalculateWorkbook(sheet) {
|
|
1056
|
+
const ctx = createFormulaContext(sheet);
|
|
1057
|
+
const sheetsMap = sheet.ydoc.getMap("sheets");
|
|
1058
|
+
const formulas = [];
|
|
1059
|
+
for (const sheetId of sheet.getSheetIds()) {
|
|
1060
|
+
const ySheet = sheetsMap.get(sheetId);
|
|
1061
|
+
if (!ySheet) continue;
|
|
1062
|
+
const state = new SheetState(ySheet);
|
|
1063
|
+
for (const { r, c, data } of state.getAllCells()) {
|
|
1064
|
+
if (data.f && String(data.f).startsWith("=") && hasInternalRefs(String(data.f))) {
|
|
1065
|
+
formulas.push({ sheetId, r, c, f: String(data.f) });
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
for (let pass = 0; pass < 8; pass++) {
|
|
1070
|
+
let changed = false;
|
|
1071
|
+
for (const item of formulas) {
|
|
1072
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
1073
|
+
const result = evaluateFormulaString(item.f, ctx, visiting);
|
|
1074
|
+
const ySheet = sheetsMap.get(item.sheetId);
|
|
1075
|
+
if (!ySheet) continue;
|
|
1076
|
+
const state = new SheetState(ySheet);
|
|
1077
|
+
const prev = state.getCellData(item.r, item.c);
|
|
1078
|
+
const patch = cellPatchFromFormulaResult(item.f, result);
|
|
1079
|
+
if (prev?.v !== patch.v || prev?.m !== patch.m || prev?.ef !== patch.ef) {
|
|
1080
|
+
changed = true;
|
|
1081
|
+
transactSystem(sheet.ydoc, () => {
|
|
1082
|
+
state.setCell(item.r, item.c, patch, false);
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (!changed) break;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
function normalizeWorkbookFormulas(sheet, dependents) {
|
|
1090
|
+
const ctx = createFormulaContext(sheet);
|
|
1091
|
+
const sheetsMap = sheet.ydoc.getMap("sheets");
|
|
1092
|
+
for (const sheetId of sheet.getSheetIds()) {
|
|
1093
|
+
const ySheet = sheetsMap.get(sheetId);
|
|
1094
|
+
if (!ySheet) continue;
|
|
1095
|
+
const state = new SheetState(ySheet);
|
|
1096
|
+
for (const { r, c, data } of state.getAllCells()) {
|
|
1097
|
+
const f = data.f ? String(data.f) : "";
|
|
1098
|
+
if (!f.startsWith("=") || hasInternalRefs(f)) continue;
|
|
1099
|
+
const internal = displayFormulaToInternal(f, ctx, sheetId);
|
|
1100
|
+
const ids = state.resolveCellIds(r, c);
|
|
1101
|
+
if (!ids) continue;
|
|
1102
|
+
transactSystem(sheet.ydoc, () => {
|
|
1103
|
+
state.setCell(r, c, { f: internal }, false);
|
|
1104
|
+
});
|
|
1105
|
+
registerFormulaDeps(sheetId, ids.rowId, ids.colId, internal, ctx, dependents);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
function updateDependents(sheet, changedSheetId, r, c, dependents) {
|
|
1110
|
+
const ySheet = sheet.ydoc.getMap("sheets").get(changedSheetId);
|
|
1111
|
+
if (!ySheet) return;
|
|
1112
|
+
const state = new SheetState(ySheet);
|
|
1113
|
+
const ids = state.resolveCellIds(r, c);
|
|
1114
|
+
if (!ids) return;
|
|
1115
|
+
const key = depKey(changedSheetId, ids.rowId, ids.colId);
|
|
1116
|
+
const targets = dependents.get(key);
|
|
1117
|
+
if (!targets?.size) return;
|
|
1118
|
+
const ctx = createFormulaContext(sheet);
|
|
1119
|
+
const sheetsMap = sheet.ydoc.getMap("sheets");
|
|
1120
|
+
for (const targetKey of targets) {
|
|
1121
|
+
const parsed = parseDepKey(targetKey);
|
|
1122
|
+
if (!parsed) continue;
|
|
1123
|
+
const { sheetId, rowId, colId } = parsed;
|
|
1124
|
+
const targetSheet = sheetsMap.get(sheetId);
|
|
1125
|
+
if (!targetSheet) continue;
|
|
1126
|
+
const targetState = new SheetState(targetSheet);
|
|
1127
|
+
const pos = ctx.idsToDisplay(sheetId, rowId, colId);
|
|
1128
|
+
if (!pos) continue;
|
|
1129
|
+
const data = targetState.getCellData(pos.r, pos.c);
|
|
1130
|
+
if (!data?.f) continue;
|
|
1131
|
+
const f = String(data.f);
|
|
1132
|
+
if (!hasInternalRefs(f)) continue;
|
|
1133
|
+
const result = evaluateFormulaString(f, ctx);
|
|
1134
|
+
transactSystem(sheet.ydoc, () => {
|
|
1135
|
+
targetState.setCell(pos.r, pos.c, cellPatchFromFormulaResult(f, result), false);
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
function registerFormulaDeps(sheetId, rowId, colId, internalFormula, ctx, dependents) {
|
|
1140
|
+
const targetKey = depKey(sheetId, rowId, colId);
|
|
1141
|
+
for (const dep of dependents.values()) {
|
|
1142
|
+
dep.delete(targetKey);
|
|
1143
|
+
}
|
|
1144
|
+
for (const k of [...dependents.keys()]) {
|
|
1145
|
+
const set = dependents.get(k);
|
|
1146
|
+
if (set.has(targetKey)) set.delete(targetKey);
|
|
1147
|
+
}
|
|
1148
|
+
for (const token of extractInternalRefTokens(internalFormula)) {
|
|
1149
|
+
const ref = parseInternalRefToken(token);
|
|
1150
|
+
if (!ref) continue;
|
|
1151
|
+
const refSheetId = ref.sheetId ?? sheetId;
|
|
1152
|
+
const link = (depRowId, depColId) => {
|
|
1153
|
+
const depKeyStr = depKey(refSheetId, depRowId, depColId);
|
|
1154
|
+
if (!dependents.has(depKeyStr)) dependents.set(depKeyStr, /* @__PURE__ */ new Set());
|
|
1155
|
+
dependents.get(depKeyStr).add(targetKey);
|
|
1156
|
+
};
|
|
1157
|
+
if (ref.endRowId != null && ref.endColId != null) {
|
|
1158
|
+
for (const cell of ctx.expandIdRange(
|
|
1159
|
+
refSheetId,
|
|
1160
|
+
ref.rowId,
|
|
1161
|
+
ref.colId,
|
|
1162
|
+
ref.endRowId,
|
|
1163
|
+
ref.endColId
|
|
1164
|
+
)) {
|
|
1165
|
+
link(cell.rowId, cell.colId);
|
|
1166
|
+
}
|
|
1167
|
+
} else {
|
|
1168
|
+
link(ref.rowId, ref.colId);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// src/extension.ts
|
|
1174
|
+
function afterLayoutChange(sheet, storage) {
|
|
1175
|
+
storage.evaluating = true;
|
|
1176
|
+
try {
|
|
1177
|
+
recalculateWorkbook(sheet);
|
|
1178
|
+
} finally {
|
|
1179
|
+
storage.evaluating = false;
|
|
1180
|
+
}
|
|
1181
|
+
sheet.notifyLayoutChange();
|
|
1182
|
+
}
|
|
1183
|
+
var FormulaExtension = Extension.create({
|
|
1184
|
+
name: "formula",
|
|
1185
|
+
priority: 10,
|
|
1186
|
+
addStorage() {
|
|
1187
|
+
return {
|
|
1188
|
+
evaluating: false,
|
|
1189
|
+
dependents: /* @__PURE__ */ new Map(),
|
|
1190
|
+
sheet: null
|
|
1191
|
+
};
|
|
1192
|
+
},
|
|
1193
|
+
addCommands(ctx) {
|
|
1194
|
+
const boundSheet = ctx.sheet;
|
|
1195
|
+
return {
|
|
1196
|
+
setCellValue: (props) => {
|
|
1197
|
+
return ({ state }) => {
|
|
1198
|
+
const raw = props.value;
|
|
1199
|
+
const sheetId = boundSheet.getActiveSheetId();
|
|
1200
|
+
const ids = state.resolveCellIds(props.r, props.c);
|
|
1201
|
+
if (isFormulaInput(raw)) {
|
|
1202
|
+
const fctx = createFormulaContext(boundSheet);
|
|
1203
|
+
const internal = displayFormulaToInternal(raw, fctx, sheetId);
|
|
1204
|
+
const result = evaluateFormulaString(internal, fctx);
|
|
1205
|
+
state.setCell(props.r, props.c, cellPatchFromFormulaResult(internal, result));
|
|
1206
|
+
if (ids) {
|
|
1207
|
+
registerFormulaDeps(
|
|
1208
|
+
sheetId,
|
|
1209
|
+
ids.rowId,
|
|
1210
|
+
ids.colId,
|
|
1211
|
+
internal,
|
|
1212
|
+
fctx,
|
|
1213
|
+
this.storage.dependents
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
} else {
|
|
1217
|
+
const num = Number(raw);
|
|
1218
|
+
const v = !Number.isNaN(num) && raw !== "" ? num : raw;
|
|
1219
|
+
state.setCell(props.r, props.c, {
|
|
1220
|
+
v,
|
|
1221
|
+
m: raw,
|
|
1222
|
+
ef: void 0,
|
|
1223
|
+
em: void 0
|
|
1224
|
+
});
|
|
1225
|
+
const cell = state.getCell(props.r, props.c);
|
|
1226
|
+
cell?.delete("f");
|
|
1227
|
+
if (ids) {
|
|
1228
|
+
const tk = depKey(sheetId, ids.rowId, ids.colId);
|
|
1229
|
+
for (const set of this.storage.dependents.values()) set.delete(tk);
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
return true;
|
|
1233
|
+
};
|
|
1234
|
+
},
|
|
1235
|
+
setCellFormula: (props) => {
|
|
1236
|
+
return ({ state }) => {
|
|
1237
|
+
const display = props.formula.startsWith("=") ? props.formula : `=${props.formula}`;
|
|
1238
|
+
const fctx = createFormulaContext(boundSheet);
|
|
1239
|
+
const sheetId = boundSheet.getActiveSheetId();
|
|
1240
|
+
const internal = displayFormulaToInternal(display, fctx, sheetId);
|
|
1241
|
+
const result = evaluateFormulaString(internal, fctx);
|
|
1242
|
+
const ids = state.resolveCellIds(props.r, props.c);
|
|
1243
|
+
state.setCell(props.r, props.c, cellPatchFromFormulaResult(internal, result));
|
|
1244
|
+
if (ids) {
|
|
1245
|
+
registerFormulaDeps(
|
|
1246
|
+
sheetId,
|
|
1247
|
+
ids.rowId,
|
|
1248
|
+
ids.colId,
|
|
1249
|
+
internal,
|
|
1250
|
+
fctx,
|
|
1251
|
+
this.storage.dependents
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
return true;
|
|
1255
|
+
};
|
|
1256
|
+
},
|
|
1257
|
+
recalculateFormulas: () => {
|
|
1258
|
+
return () => {
|
|
1259
|
+
this.storage.evaluating = true;
|
|
1260
|
+
try {
|
|
1261
|
+
recalculateWorkbook(boundSheet);
|
|
1262
|
+
} finally {
|
|
1263
|
+
this.storage.evaluating = false;
|
|
1264
|
+
}
|
|
1265
|
+
return true;
|
|
1266
|
+
};
|
|
1267
|
+
},
|
|
1268
|
+
insertRows: (props) => {
|
|
1269
|
+
return ({ state }) => {
|
|
1270
|
+
boundSheet.notifyBeforeLayoutChange();
|
|
1271
|
+
state.insertRows(props.at, props.count ?? 1);
|
|
1272
|
+
afterLayoutChange(boundSheet, this.storage);
|
|
1273
|
+
return true;
|
|
1274
|
+
};
|
|
1275
|
+
},
|
|
1276
|
+
deleteRows: (props) => {
|
|
1277
|
+
return ({ state }) => {
|
|
1278
|
+
boundSheet.notifyBeforeLayoutChange();
|
|
1279
|
+
state.deleteRows(props.at, props.count ?? 1);
|
|
1280
|
+
afterLayoutChange(boundSheet, this.storage);
|
|
1281
|
+
return true;
|
|
1282
|
+
};
|
|
1283
|
+
},
|
|
1284
|
+
insertCols: (props) => {
|
|
1285
|
+
return ({ state }) => {
|
|
1286
|
+
boundSheet.notifyBeforeLayoutChange();
|
|
1287
|
+
state.insertCols(props.at, props.count ?? 1);
|
|
1288
|
+
afterLayoutChange(boundSheet, this.storage);
|
|
1289
|
+
return true;
|
|
1290
|
+
};
|
|
1291
|
+
},
|
|
1292
|
+
deleteCols: (props) => {
|
|
1293
|
+
return ({ state }) => {
|
|
1294
|
+
boundSheet.notifyBeforeLayoutChange();
|
|
1295
|
+
state.deleteCols(props.at, props.count ?? 1);
|
|
1296
|
+
afterLayoutChange(boundSheet, this.storage);
|
|
1297
|
+
return true;
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
},
|
|
1302
|
+
onInit(sheet) {
|
|
1303
|
+
this.storage.sheet = sheet;
|
|
1304
|
+
this.storage.evaluating = true;
|
|
1305
|
+
try {
|
|
1306
|
+
normalizeWorkbookFormulas(sheet, this.storage.dependents);
|
|
1307
|
+
recalculateWorkbook(sheet);
|
|
1308
|
+
} finally {
|
|
1309
|
+
this.storage.evaluating = false;
|
|
1310
|
+
}
|
|
1311
|
+
},
|
|
1312
|
+
onCellChange(r, c) {
|
|
1313
|
+
if (this.storage.evaluating || !this.storage.sheet) return;
|
|
1314
|
+
updateDependents(
|
|
1315
|
+
this.storage.sheet,
|
|
1316
|
+
this.storage.sheet.getActiveSheetId(),
|
|
1317
|
+
r,
|
|
1318
|
+
c,
|
|
1319
|
+
this.storage.dependents
|
|
1320
|
+
);
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
function getFormulaHighlights(sheet, formula) {
|
|
1324
|
+
if (!isFormulaInput(formula)) return [];
|
|
1325
|
+
const ctx = createFormulaContext(sheet);
|
|
1326
|
+
return buildHighlightsFromFormula(formula, ctx);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
export { FORMULA_BUILTIN_REGISTRY, FORMULA_CATEGORIES, FORMULA_ERRORS, FORMULA_REF_COLORS, FormulaExtension, buildSheetRefToken, canPickFormulaRef, canPickFormulaRefAtCaret, categoryLabel, cellPatchFromFormulaResult, colToLetter, createFormulaContext, displayFormulaToInternal, evaluateFormulaString, extractRefTokens, formatA1, formatRangeA1, formulaErrorResult, getBuiltinEntry, getBuiltinsByCategory, getCategoriesWithBuiltins, getCellFormulaInitial, getCellFormulaInitialFromCell, getFeaturedBuiltins, getFormulaErrorMessage, getFormulaHighlights, getFormulaRefSpans, hasInternalRefs, internalFormulaToDisplay, isFormulaErrorDisplay, isFormulaInput, isFormulaText, isRegisteredBuiltin, letterToCol, parseActiveFunctionName, parseRefToken, patchFormulaWithRef, resolveBuiltinName, searchBuiltins };
|
|
1330
|
+
//# sourceMappingURL=index.js.map
|
|
1331
|
+
//# sourceMappingURL=index.js.map
|