@ai-matrx/office 0.8.12 → 0.8.13

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.
@@ -6,12 +6,11 @@ var covered = (family, ...allow) => ({ kind: "covered", family, allow: ["subUnit
6
6
  var never = (reason) => ({ kind: "never-travels", reason });
7
7
  var barrier = (until, reason) => ({ kind: "barrier", until, reason });
8
8
  var FORMULA_RESULT = "formula engine output; every seat computes it (dropped at capture)";
9
- var FORMULA = "formula state; C5b rewrites references";
10
9
  var RESOURCE = "ranged resource keyed by id; C5c";
11
10
  var UNIVER_COMMENTS = "Univer's own thread-comment store; our comments anchor elsewhere (design \xA78)";
12
11
  var SHEET_MUTATIONS = {
13
12
  // ── covered (C5a) ────────────────────────────────────────────────────────────────────────────────
14
- "sheet.mutation.set-range-values": covered("cell", "cellValue"),
13
+ "sheet.mutation.set-range-values": covered("cell", "cellValue", "refSheets", "derived"),
15
14
  "sheet.mutation.insert-row": covered("structure", "range", "rowInfo"),
16
15
  "sheet.mutation.remove-rows": covered("structure", "range"),
17
16
  "sheet.mutation.insert-col": covered("structure", "range", "colInfo"),
@@ -88,16 +87,16 @@ var SHEET_MUTATIONS = {
88
87
  "sheet.mutation.set-worksheet-protection": barrier("X", "permissions: not in C5a"),
89
88
  "sheet.mutation.delete-worksheet-protection": barrier("X", "permissions: not in C5a"),
90
89
  "sheet.mutation.set-worksheet-permission-points": barrier("X", "permissions: not in C5a"),
91
- // ── barriers until C5b (formulas) ────────────────────────────────────────────────────────────────
92
- "formula.mutation.set-defined-name": barrier("C5b", FORMULA),
93
- "formula.mutation.remove-defined-name": barrier("C5b", FORMULA),
94
- "formula.mutation.set-feature-calculation": barrier("C5b", FORMULA),
95
- "formula.mutation.remove-feature-calculation": barrier("C5b", FORMULA),
96
- "formula.mutation.set-other-formula": barrier("C5b", FORMULA),
97
- "formula.mutation.remove-other-formula": barrier("C5b", FORMULA),
98
- "formula.mutation.set-super-table": barrier("C5b", FORMULA),
99
- "formula.mutation.remove-super-table": barrier("C5b", FORMULA),
100
- "formula.mutation.set-super-table-option": barrier("C5b", FORMULA),
90
+ // ── formulas (C5b) ───────────────────────────────────────────────────────────────────────────────
91
+ "formula.mutation.set-defined-name": covered("defname", "id", "name", "formulaOrRefString", "formulaOrRefStringWithPrefix", "comment", "localSheetId", "hidden", "refSheets", "previousName", "derived"),
92
+ "formula.mutation.remove-defined-name": covered("defname", "id", "name"),
93
+ "formula.mutation.set-feature-calculation": barrier("X", "a plugin's registered calculation (feature id + dependency ranges); no transform yet"),
94
+ "formula.mutation.remove-feature-calculation": barrier("X", "a plugin's registered calculation; no transform yet"),
95
+ "formula.mutation.set-other-formula": barrier("C5c", "formulas owned by conditional formats and data validation travel with their rule"),
96
+ "formula.mutation.remove-other-formula": barrier("C5c", "formulas owned by conditional formats and data validation travel with their rule"),
97
+ "formula.mutation.set-super-table": barrier("C5c", "table names in structured references belong to tables (C5c)"),
98
+ "formula.mutation.remove-super-table": barrier("C5c", "table names in structured references belong to tables (C5c)"),
99
+ "formula.mutation.set-super-table-option": barrier("C5c", "table names in structured references belong to tables (C5c)"),
101
100
  // ── barriers until C5c (ranged resources) ────────────────────────────────────────────────────────
102
101
  "sheet.mutation.add-conditional-rule": barrier("C5c", RESOURCE),
103
102
  "sheet.mutation.set-conditional-rule": barrier("C5c", RESOURCE),
@@ -254,8 +253,237 @@ function transformAxis(x, y, side) {
254
253
  }
255
254
  }
256
255
 
256
+ // src/collab/sheets/transform/formula.ts
257
+ import { Direction, RANGE_TYPE, Rectangle, getIntersectRange } from "@univerjs/core";
258
+ import { LexerTreeBuilder, deserializeRangeWithSheet, generateStringWithSequence, sequenceNodeType, serializeRangeToRefString } from "@univerjs/engine-formula";
259
+ import {
260
+ EffectRefRangId,
261
+ handleIRemoveCol,
262
+ handleIRemoveRow,
263
+ handleInsertCol,
264
+ handleInsertRow,
265
+ handleMoveCols,
266
+ handleMoveRows,
267
+ runRefRangeMutations
268
+ } from "@univerjs/sheets";
269
+ var REF_ERROR = "#REF!";
270
+ var FULL = 16383 * 64;
271
+ var lexer = null;
272
+ var lex = (body) => (lexer ??= new LexerTreeBuilder()).sequenceNodesBuilder(body);
273
+ var isFormulaText = (f) => typeof f === "string" && f.length > 1 && f.startsWith("=");
274
+ var IDENT = /^[A-Za-z_\\][A-Za-z0-9_.\\]*$/;
275
+ var ERROR_TOKEN = /^(?:[^!]+!)?#(?:REF!|NAME\?|VALUE!|DIV\/0!|N\/A|NUM!|NULL!|SPILL!|CALC!)$/i;
276
+ function parse(text) {
277
+ const prefix = text.startsWith("=") ? "=" : "";
278
+ const nodes = lex(text.slice(prefix.length));
279
+ if (!nodes) return "the formula lexer could not read the text";
280
+ for (const n of nodes) {
281
+ if (typeof n === "string") continue;
282
+ if (n.nodeType === sequenceNodeType.TABLE || n.nodeType === sequenceNodeType.FUNCTION && n.token.includes("[")) return "a structured table reference (tables are C5c)";
283
+ if (n.nodeType === sequenceNodeType.FUNCTION && !IDENT.test(n.token) && !ERROR_TOKEN.test(n.token))
284
+ return `the lexer read "${n.token}" as one name (an intersection or a reference it cannot split)`;
285
+ if (n.nodeType === sequenceNodeType.REFERENCE) {
286
+ const d = deserializeRangeWithSheet(n.token);
287
+ if (d.unitId) return "a reference into another workbook";
288
+ }
289
+ }
290
+ return { prefix, nodes };
291
+ }
292
+ function targetSheet(sheetName, ctx) {
293
+ if (!sheetName) return ctx.ownSheet;
294
+ const id = ctx.refSheets?.[sheetName];
295
+ if (id === void 0) return void 0;
296
+ return id ?? "";
297
+ }
298
+ function formulaBarrier(text, ctx) {
299
+ const p = parse(text);
300
+ if (typeof p === "string") return p;
301
+ for (const n of p.nodes) {
302
+ if (typeof n === "string" || n.nodeType !== sequenceNodeType.REFERENCE) continue;
303
+ const { sheetName } = deserializeRangeWithSheet(n.token);
304
+ if (targetSheet(sheetName, ctx) === void 0) return `sheet "${sheetName}" is not in refSheets (capture must call annotateRefSheets)`;
305
+ }
306
+ return null;
307
+ }
308
+ function referencedSheetNames(text) {
309
+ const p = parse(text);
310
+ if (typeof p === "string") return [];
311
+ const out = /* @__PURE__ */ new Set();
312
+ for (const n of p.nodes) if (typeof n !== "string" && n.nodeType === sequenceNodeType.REFERENCE) {
313
+ const { sheetName } = deserializeRangeWithSheet(n.token);
314
+ if (sheetName) out.add(sheetName);
315
+ }
316
+ return [...out];
317
+ }
318
+ var fullRowsRange = (start, count) => ({ startRow: start, endRow: start + count - 1, startColumn: 0, endColumn: FULL, rangeType: RANGE_TYPE.ROW });
319
+ var fullColsRange = (start, count) => ({ startRow: 0, endRow: FULL, startColumn: start, endColumn: start + count - 1, rangeType: RANGE_TYPE.COLUMN });
320
+ var startV = (v) => Number.isNaN(v) || v == null ? -Infinity : v;
321
+ var endV = (v) => Number.isNaN(v) || v == null ? Infinity : v;
322
+ function startEnd(r) {
323
+ return { ...r, startRow: startV(r.startRow), endRow: endV(r.endRow), startColumn: startV(r.startColumn), endColumn: endV(r.endColumn) };
324
+ }
325
+ function checkMoveEdge(origin, from) {
326
+ const startRow = startV(origin.startRow), endRow = endV(origin.endRow), startColumn = startV(origin.startColumn), endColumn = endV(origin.endColumn);
327
+ const fromStartRow = startV(from.startRow), fromEndRow = endV(from.endRow), fromStartColumn = startV(from.startColumn), fromEndColumn = endV(from.endColumn);
328
+ const rowsCover = () => origin.rangeType === RANGE_TYPE.COLUMN && from.rangeType === RANGE_TYPE.COLUMN || startRow >= fromStartRow && endRow <= fromEndRow;
329
+ const columnsCover = () => origin.rangeType === RANGE_TYPE.ROW && from.rangeType === RANGE_TYPE.ROW || startColumn >= fromStartColumn && endColumn <= fromEndColumn;
330
+ const allCover = () => origin.rangeType === RANGE_TYPE.ALL && from.rangeType === RANGE_TYPE.ALL;
331
+ if (rowsCover() && columnsCover() || allCover()) return 4;
332
+ if (columnsCover() && startRow >= fromStartRow && startRow <= fromEndRow && endRow > fromEndRow) return 0;
333
+ if (columnsCover() && endRow >= fromStartRow && endRow <= fromEndRow && startRow < fromStartRow) return 1;
334
+ if (rowsCover() && startColumn >= fromStartColumn && startColumn <= fromEndColumn && endColumn > fromEndColumn) return 2;
335
+ if (rowsCover() && endColumn >= fromStartColumn && endColumn <= fromEndColumn && startColumn < fromStartColumn) return 3;
336
+ return null;
337
+ }
338
+ function getMoveNewRange(moveEdge, result, from, to, origin, remain) {
339
+ const { startRow, endRow, startColumn, endColumn, rangeType } = startEnd(result);
340
+ const f = startEnd(from);
341
+ const fromRangeType = from.rangeType ?? RANGE_TYPE.NORMAL;
342
+ const t = startEnd(to);
343
+ const rm = startEnd(remain);
344
+ const o = startEnd(origin);
345
+ const originRangeType = origin.rangeType ?? RANGE_TYPE.NORMAL;
346
+ const newRange = { ...origin };
347
+ const rowsCover = () => rangeType === RANGE_TYPE.COLUMN && originRangeType === RANGE_TYPE.COLUMN || startColumn >= o.startColumn && endColumn <= o.endColumn;
348
+ const columnsCover = () => rangeType === RANGE_TYPE.ROW && originRangeType === RANGE_TYPE.ROW || startRow >= o.startRow && endRow <= o.endRow;
349
+ if (moveEdge === 0) {
350
+ if (!rowsCover()) return void 0;
351
+ if (startRow < o.startRow) newRange.startRow = startRow;
352
+ else if (startRow >= o.endRow) newRange.endRow -= f.endRow + 1 - o.startRow;
353
+ else return void 0;
354
+ } else if (moveEdge === 1) {
355
+ if (!rowsCover()) return void 0;
356
+ if (endRow > o.endRow) newRange.endRow = endRow;
357
+ else if (endRow <= o.startRow) newRange.startRow += o.endRow - f.startRow + 1;
358
+ else return void 0;
359
+ } else if (moveEdge === 2) {
360
+ if (!columnsCover()) return void 0;
361
+ if (startColumn < o.startColumn) newRange.startColumn = startColumn;
362
+ else if (startColumn >= o.endColumn) newRange.endColumn -= f.endColumn + 1 - o.startColumn;
363
+ else return void 0;
364
+ } else if (moveEdge === 3) {
365
+ if (!columnsCover()) return void 0;
366
+ if (endColumn > o.endColumn) newRange.endColumn = endColumn;
367
+ else if (endColumn <= o.startColumn) newRange.startColumn += o.endColumn - f.startColumn + 1;
368
+ else return void 0;
369
+ } else if (moveEdge === 4) {
370
+ Object.assign(newRange, { startRow, startColumn, endRow, endColumn });
371
+ } else if (f.startColumn <= o.startColumn && f.endColumn >= o.endColumn || fromRangeType === RANGE_TYPE.ROW && originRangeType === RANGE_TYPE.ROW) {
372
+ const n = f.endRow - f.startRow + 1;
373
+ if (from.endRow < o.startRow) {
374
+ if (t.startRow >= o.startRow) newRange.startRow -= n;
375
+ if (t.startRow >= o.endRow) newRange.endRow -= n;
376
+ } else if (from.startRow > o.endRow) {
377
+ if (t.endRow <= o.endRow) newRange.endRow += n;
378
+ if (t.endRow <= o.startRow) newRange.startRow += n;
379
+ } else if (from.startRow >= o.startRow && from.endRow <= o.endRow) {
380
+ if (t.startRow <= o.startRow) newRange.startRow += n;
381
+ else if (t.startRow >= o.endRow) newRange.endRow -= n;
382
+ }
383
+ } else if (f.startRow <= o.startRow && f.endRow >= o.endRow || fromRangeType === RANGE_TYPE.COLUMN && originRangeType === RANGE_TYPE.COLUMN) {
384
+ const n = f.endColumn - f.startColumn + 1;
385
+ if (from.endColumn < o.startColumn) {
386
+ if (t.startColumn >= o.startColumn) newRange.startColumn -= n;
387
+ if (t.startColumn >= o.endColumn) newRange.endColumn -= n;
388
+ } else if (from.startColumn > o.endColumn) {
389
+ if (t.endColumn <= o.endColumn) newRange.endColumn += n;
390
+ if (t.endColumn <= o.startColumn) newRange.startColumn += n;
391
+ } else if (from.startColumn >= o.startColumn && from.endColumn <= o.endColumn) {
392
+ if (t.startColumn <= o.startColumn) newRange.startColumn += n;
393
+ else if (t.startColumn >= o.endColumn) newRange.endColumn -= n;
394
+ }
395
+ } else {
396
+ Object.assign(newRange, { startRow, startColumn, endRow, endColumn });
397
+ void rm;
398
+ }
399
+ return newRange;
400
+ }
401
+ function moveRange(range, axis, op) {
402
+ const isRow = axis === "row";
403
+ if (op.kind === "insert") {
404
+ const r = isRow ? fullRowsRange(op.at, op.count) : fullColsRange(op.at, op.count);
405
+ const ops2 = isRow ? handleInsertRow({ id: EffectRefRangId.InsertRowCommandId, params: { range: r, unitId: "", subUnitId: "", direction: Direction.DOWN } }, range) : handleInsertCol({ id: EffectRefRangId.InsertColCommandId, params: { range: r, unitId: "", subUnitId: "", direction: Direction.RIGHT } }, range);
406
+ const res2 = runRefRangeMutations(ops2, range);
407
+ return res2 == null ? void 0 : { ...range, ...res2 };
408
+ }
409
+ if (op.kind === "remove") {
410
+ const r = isRow ? fullRowsRange(op.start, op.count) : fullColsRange(op.start, op.count);
411
+ const ops2 = isRow ? handleIRemoveRow({ id: EffectRefRangId.RemoveRowCommandId, params: { range: r } }, range, []) : handleIRemoveCol({ id: EffectRefRangId.RemoveColCommandId, params: { range: r } }, range);
412
+ const res2 = runRefRangeMutations(ops2, range);
413
+ return res2 == null ? REF_ERROR : { ...range, ...res2 };
414
+ }
415
+ const from = isRow ? fullRowsRange(op.from, op.count) : fullColsRange(op.from, op.count);
416
+ const to = isRow ? fullRowsRange(op.to, op.count) : fullColsRange(op.to, op.count);
417
+ const moveEdge = checkMoveEdge(range, from);
418
+ let remain = getIntersectRange(range, from);
419
+ if (isRow) {
420
+ if (remain == null && (from.endRow < range.startRow && to.endRow <= range.startRow || from.startRow > range.endRow && to.startRow > range.endRow)) return void 0;
421
+ } else if (remain == null && (from.endColumn < range.startColumn && to.endColumn <= range.startColumn || from.startColumn > range.endColumn && to.startColumn > range.endColumn)) return void 0;
422
+ remain ??= { startRow: range.startRow, endRow: range.endRow, startColumn: range.startColumn, endColumn: range.endColumn, rangeType: RANGE_TYPE.NORMAL };
423
+ const ops = isRow ? handleMoveRows({ id: EffectRefRangId.MoveRowsCommandId, params: { toRange: to, fromRange: from } }, remain) : handleMoveCols({ id: EffectRefRangId.MoveColsCommandId, params: { toRange: to, fromRange: from } }, remain);
424
+ const res = runRefRangeMutations(ops, remain);
425
+ if (res == null) return REF_ERROR;
426
+ return getMoveNewRange(moveEdge, res, from, to, range, remain);
427
+ }
428
+ function rewriteFormula(text, ctx, change) {
429
+ const p = parse(text);
430
+ if (typeof p === "string") return p;
431
+ const nodes = p.nodes.slice();
432
+ const changedIds = [];
433
+ for (let i = 0; i < nodes.length; i++) {
434
+ const n = nodes[i];
435
+ if (typeof n === "string") continue;
436
+ if (change.kind === "defined-name") {
437
+ if ((n.nodeType === sequenceNodeType.DEFINED_NAME || n.nodeType === sequenceNodeType.FUNCTION) && n.token === change.from) {
438
+ nodes[i] = { ...n, token: change.to ?? REF_ERROR };
439
+ changedIds.push(i);
440
+ }
441
+ continue;
442
+ }
443
+ if (n.nodeType !== sequenceNodeType.REFERENCE) continue;
444
+ const { range, sheetName, unitId } = deserializeRangeWithSheet(n.token);
445
+ const target = targetSheet(sheetName, ctx);
446
+ if (target === void 0) return `sheet "${sheetName}" is not in refSheets (capture must call annotateRefSheets)`;
447
+ if (target !== change.sheet) continue;
448
+ let token;
449
+ if (change.kind === "rename") {
450
+ if (!sheetName) continue;
451
+ token = serializeRangeToRefString({ range, sheetName: change.name, unitId });
452
+ } else {
453
+ const moved = moveRange(Rectangle.moveOffset(range, 0, 0), change.axis, change.op);
454
+ if (moved === void 0) continue;
455
+ token = moved === REF_ERROR ? REF_ERROR : serializeRangeToRefString({ range: moved, sheetName: target === ctx.ownSheet && !ctx.keepQualifier ? "" : sheetName, unitId });
456
+ }
457
+ nodes[i] = { ...n, token };
458
+ changedIds.push(i);
459
+ }
460
+ if (!changedIds.length) return { text, changed: false };
461
+ for (let i = 0; i < nodes.length; i++) {
462
+ const n = nodes[i];
463
+ if (typeof n === "string" || n.nodeType !== sequenceNodeType.REFERENCE || changedIds.includes(i)) continue;
464
+ nodes[i] = { ...n, token: serializeRangeToRefString(deserializeRangeWithSheet(n.token)) };
465
+ }
466
+ const out = p.prefix + generateStringWithSequence(nodes);
467
+ return { text: out, changed: out !== text };
468
+ }
469
+ function refSheetsAfter(refSheets, change) {
470
+ if (change.kind !== "rename" || !refSheets) return refSheets;
471
+ const entries = Object.entries(refSheets);
472
+ if (!entries.some(([name, id]) => id === change.sheet || name === change.name)) return refSheets;
473
+ const next = {};
474
+ for (const [name, id] of entries) if (id !== change.sheet && name !== change.name) next[name] = id;
475
+ next[change.name] = change.sheet;
476
+ return next;
477
+ }
478
+ function refSheetsFor(texts, sheets) {
479
+ const byName = new Map(Object.entries(sheets).map(([id, name]) => [name, id]));
480
+ const out = {};
481
+ for (const t of texts) for (const name of referencedSheetNames(t)) out[name] = byName.get(name) ?? null;
482
+ return Object.keys(out).length ? out : void 0;
483
+ }
484
+
257
485
  // src/collab/sheets/transform/transform.ts
258
- var SHEET_TRANSFORMER_VERSION = "c5a-1";
486
+ var SHEET_TRANSFORMER_VERSION = "c5b-1";
259
487
  var ID = {
260
488
  values: "sheet.mutation.set-range-values",
261
489
  insertRow: "sheet.mutation.insert-row",
@@ -270,7 +498,9 @@ var ID = {
270
498
  rowIsAuto: "sheet.mutation.set-worksheet-row-is-auto-height",
271
499
  rowAutoHeight: "sheet.mutation.set-worksheet-row-auto-height",
272
500
  colWidth: "sheet.mutation.set-worksheet-col-width",
273
- name: "sheet.mutation.set-worksheet-name"
501
+ name: "sheet.mutation.set-worksheet-name",
502
+ setName: "formula.mutation.set-defined-name",
503
+ removeName: "formula.mutation.remove-defined-name"
274
504
  };
275
505
  var PAIR_BARRIERS = {};
276
506
  var pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
@@ -335,9 +565,9 @@ function buildStructural(template, axis, op) {
335
565
  return { id: template.id, params: { ...p, range: { ...p.range, [a0]: start, [a1]: start + op.count - 1 } } };
336
566
  }
337
567
  function univerMutationInfo(axis, op) {
338
- const FULL = { startRow: 0, endRow: Number.MAX_SAFE_INTEGER, startColumn: 0, endColumn: Number.MAX_SAFE_INTEGER };
568
+ const FULL2 = { startRow: 0, endRow: Number.MAX_SAFE_INTEGER, startColumn: 0, endColumn: Number.MAX_SAFE_INTEGER };
339
569
  const start = op.kind === "insert" ? op.at : op.start;
340
- const range = axis === "row" ? { ...FULL, startRow: start, endRow: start + op.count - 1 } : { ...FULL, startColumn: start, endColumn: start + op.count - 1 };
570
+ const range = axis === "row" ? { ...FULL2, startRow: start, endRow: start + op.count - 1 } : { ...FULL2, startColumn: start, endColumn: start + op.count - 1 };
341
571
  const id = op.kind === "insert" ? axis === "row" ? ID.insertRow : ID.insertCol : axis === "row" ? ID.removeRows : ID.removeCol;
342
572
  return { id, params: { unitId: "", subUnitId: "", range } };
343
573
  }
@@ -404,8 +634,8 @@ function exactMergeRemovals(group, merges) {
404
634
  var mergeRanges = (m) => m.params.ranges ?? [];
405
635
  var withRanges = (m, ranges) => ({ id: m.id, params: { ...m.params, ranges } });
406
636
  var mergeMutation = (id, sheet, ranges) => ({ id, params: { subUnitId: sheet, ranges } });
407
- var CONTENT_KEYS = ["v", "t", "p", "f", "si", "custom"];
408
- var CONTENT_TOUCH = ["v", "p", "f", "si", "custom"];
637
+ var CONTENT_KEYS = ["v", "t", "p", "f", "si", "custom", "ref", "xf", "ft", "fd"];
638
+ var CONTENT_TOUCH = ["v", "p", "f", "si", "custom", "ref", "xf", "ft", "fd"];
409
639
  var KNOWN_CELL_KEYS = /* @__PURE__ */ new Set([...CONTENT_KEYS, "s"]);
410
640
  function completeCellContent(entry, post) {
411
641
  if (entry === null) return null;
@@ -435,8 +665,10 @@ function cellBarrier(m) {
435
665
  forEachCell(cellsOf(m), (_r, _c, v) => {
436
666
  if (why || v === null) return;
437
667
  for (const k of Object.keys(v)) {
668
+ if (why) break;
438
669
  if (!KNOWN_CELL_KEYS.has(k)) why = `cell key "${k}" is not covered`;
439
- else if ((k === "f" || k === "si") && v[k] != null) why = "formula content (f/si) belongs to C5b";
670
+ else if (k === "si" && v.si != null) why = "shared formula (si): its text lives in a master cell this op does not carry";
671
+ else if (k === "f" && isFormulaText(v.f) || k === "ref" && typeof v.ref === "string") why = formulaBarrier(v[k], formulaCtx(m));
440
672
  }
441
673
  if (typeof v.s === "string") why = "a style id travels (style ids are seat-local; capture must send the style object)";
442
674
  if (!why && CONTENT_TOUCH.some((k) => k in v) && !CONTENT_KEYS.every((k) => k in v || k === "t" && v.v == null))
@@ -509,6 +741,9 @@ function transformCell(X, Y, side) {
509
741
  function transformCellsOverCells(x, y, side) {
510
742
  if (sheetOf(x) !== sheetOf(y)) return ok([x]);
511
743
  const ycells = cellsOf(y);
744
+ const xd = isDerived(x);
745
+ const yd = isDerived(y);
746
+ const eff = derivedSide(x, y, side);
512
747
  const passes = [{}];
513
748
  const lost = [];
514
749
  const dropped = [];
@@ -520,29 +755,218 @@ function transformCellsOverCells(x, y, side) {
520
755
  putCell(passes[0], r, c, X);
521
756
  return;
522
757
  }
523
- const res = transformCell(X, yRow[c] ?? null, side);
758
+ const res = transformCell(X, yRow[c] ?? null, eff);
524
759
  if (typeof res === "string") {
525
760
  why = res;
526
761
  return;
527
762
  }
528
763
  res.entries.forEach((e, i) => putCell(passes[i] ??= {}, r, c, e));
529
- if (res.xLost) dropped.push(cellRef(sheetOf(x), r, c));
530
- if (res.yLost) lost.push(cellRef(sheetOf(x), r, c));
764
+ if (res.xLost && !xd) dropped.push(cellRef(sheetOf(x), r, c));
765
+ if (res.yLost && !yd) lost.push(cellRef(sheetOf(x), r, c));
531
766
  });
532
767
  if (why) return barrier2(why);
533
768
  const op = passes.filter((p) => !isEmptyMatrix(p)).map((p) => cellsMutation(x, p));
534
769
  return ok(op, lost, dropped);
535
770
  }
536
- function transformCellsOverStructural(x, s) {
771
+ var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived"];
772
+ var isDerived = (m) => m.params.derived === true;
773
+ function derivedSide(x, y, side) {
774
+ const xd = isDerived(x);
775
+ const yd = isDerived(y);
776
+ return xd === yd ? side : xd ? "right" : "left";
777
+ }
778
+ function forApply(m) {
779
+ if (!SIDE_CAR_PARAMS.some((k) => k in m.params)) return m;
780
+ const params = { ...m.params };
781
+ for (const k of SIDE_CAR_PARAMS) delete params[k];
782
+ return { id: m.id, params };
783
+ }
784
+ var refSheetsOf = (m) => m.params.refSheets;
785
+ var formulaCtx = (m) => ({ ownSheet: sheetOf(m), refSheets: refSheetsOf(m) });
786
+ function withRefSheets(params, rs) {
787
+ const out = { ...params };
788
+ if (rs) out.refSheets = rs;
789
+ else delete out.refSheets;
790
+ return out;
791
+ }
792
+ var hasFormulaText = (v) => !!v && (isFormulaText(v.f) || typeof v.ref === "string");
793
+ function hasFormula(m) {
794
+ let any = false;
795
+ forEachCell(cellsOf(m), (_r, _c, v) => {
796
+ any ||= hasFormulaText(v);
797
+ });
798
+ return any;
799
+ }
800
+ var setNameWithoutPrevious = (m) => m.id === ID.setName && !("previousName" in m.params);
801
+ function refChangeOf(m) {
802
+ const st = structuralOf(m);
803
+ if (st) return { kind: "axis", sheet: sheetOf(m), axis: st.axis, op: st.op };
804
+ if (m.id === ID.name) return { kind: "rename", sheet: sheetOf(m), name: String(m.params.name) };
805
+ if (m.id === ID.removeName) return { kind: "defined-name", from: String(m.params.name), to: null };
806
+ if (m.id === ID.setName && typeof m.params.previousName === "string" && m.params.previousName !== m.params.name)
807
+ return { kind: "defined-name", from: m.params.previousName, to: String(m.params.name) };
808
+ return null;
809
+ }
810
+ function rewriteEntry(v, ctx, change) {
811
+ let entry = v;
812
+ let changed = false;
813
+ for (const k of ["f", "ref"]) {
814
+ const t = v[k];
815
+ if (k === "f" ? !isFormulaText(t) : typeof t !== "string") continue;
816
+ const r = rewriteFormula(t, ctx, change);
817
+ if (typeof r === "string") return r;
818
+ if (!r.changed) continue;
819
+ if (k === "ref" && r.text.includes(REF_ERROR)) return "an array formula's range was removed under it";
820
+ entry = { ...entry, [k]: r.text };
821
+ changed = true;
822
+ }
823
+ return { entry, changed };
824
+ }
825
+ function cellThrough(sheet, change, r, c) {
826
+ if (change.kind !== "axis" || change.sheet !== sheet) return [r, c];
827
+ const nr = change.axis === "row" ? mapIndex(change.op, r) : r;
828
+ const nc = change.axis === "col" ? mapIndex(change.op, c) : c;
829
+ return nr === null || nc === null ? null : [nr, nc];
830
+ }
831
+ function cellsOverChange(x, change) {
832
+ const ctx = formulaCtx(x);
537
833
  const out = {};
538
834
  const dropped = [];
835
+ let why = null;
539
836
  forEachCell(cellsOf(x), (r, c, v) => {
540
- const nr = s.axis === "row" ? mapIndex(s.op, r) : r;
541
- const nc = s.axis === "col" ? mapIndex(s.op, c) : c;
542
- if (nr === null || nc === null) dropped.push(cellRef(sheetOf(x), r, c));
543
- else putCell(out, nr, nc, v);
837
+ if (why) return;
838
+ const at = cellThrough(sheetOf(x), change, r, c);
839
+ if (!at) {
840
+ dropped.push(cellRef(sheetOf(x), r, c));
841
+ return;
842
+ }
843
+ let e = v;
844
+ if (hasFormulaText(v)) {
845
+ const res = rewriteEntry(v, ctx, change);
846
+ if (typeof res === "string") {
847
+ why = res;
848
+ return;
849
+ }
850
+ e = res.entry;
851
+ }
852
+ putCell(out, at[0], at[1], e);
853
+ });
854
+ if (why) return barrier2(why);
855
+ const params = { ...withRefSheets(x.params, refSheetsAfter(ctx.refSheets, change)), cellValue: out };
856
+ return ok(isEmptyMatrix(out) ? [] : [{ id: x.id, params }], [], dropped);
857
+ }
858
+ function rewritesOf(y, change) {
859
+ const ctx = formulaCtx(y);
860
+ const out = {};
861
+ let why = null;
862
+ forEachCell(cellsOf(y), (r, c, v) => {
863
+ if (why || !hasFormulaText(v)) return;
864
+ const at = cellThrough(sheetOf(y), change, r, c);
865
+ if (!at) return;
866
+ const res = rewriteEntry(v, ctx, change);
867
+ if (typeof res === "string") {
868
+ why = res;
869
+ return;
870
+ }
871
+ if (res.changed) putCell(out, at[0], at[1], contentOf(res.entry));
872
+ });
873
+ if (why) return why;
874
+ if (isEmptyMatrix(out)) return null;
875
+ return { id: ID.values, params: withRefSheets({ subUnitId: sheetOf(y), cellValue: out, derived: true }, refSheetsAfter(ctx.refSheets, change)) };
876
+ }
877
+ var NAME_TEXT_KEYS = ["formulaOrRefString", "formulaOrRefStringWithPrefix"];
878
+ function definedNameThrough(m, change) {
879
+ if (change.kind === "defined-name" || m.id !== ID.setName) return { op: m, changed: false };
880
+ const ctx = { ownSheet: change.sheet, refSheets: refSheetsOf(m), keepQualifier: true };
881
+ const params = { ...m.params };
882
+ let changed = false;
883
+ for (const k of NAME_TEXT_KEYS) {
884
+ if (typeof params[k] !== "string" || !params[k]) continue;
885
+ const r = rewriteFormula(params[k], ctx, change);
886
+ if (typeof r === "string") return r;
887
+ if (r.changed) {
888
+ params[k] = r.text;
889
+ changed = true;
890
+ }
891
+ }
892
+ return { op: { id: m.id, params: withRefSheets(params, refSheetsAfter(ctx.refSheets, change)) }, changed };
893
+ }
894
+ function asDerived(m) {
895
+ return { id: m.id, params: { ...m.params, derived: true } };
896
+ }
897
+ function formulaPair(x, y, side) {
898
+ const fx = familyOf(x.id);
899
+ const fy = familyOf(y.id);
900
+ const SET_NAME_BARRIER = "set-defined-name without previousName (capture must record the name it replaces) against formula text";
901
+ if (fx === "defname" && fy === "defname") {
902
+ if (String(x.params.id) !== String(y.params.id)) return ok([x]);
903
+ const eff = derivedSide(x, y, side);
904
+ if (eff === "left") return ok([x], isDerived(y) ? [] : [`defined name "${String(y.params.name)}"`]);
905
+ return ok([], [], isDerived(x) ? [] : [`defined name "${String(x.params.name)}"`]);
906
+ }
907
+ if (fx === "defname") {
908
+ if (fy === "cell") {
909
+ if (setNameWithoutPrevious(x) && hasFormula(y)) return barrier2(SET_NAME_BARRIER);
910
+ const ch2 = refChangeOf(x);
911
+ if (!ch2) return ok([x]);
912
+ const add = rewritesOf(y, ch2);
913
+ if (typeof add === "string") return barrier2(add);
914
+ return ok(add ? [x, add] : [x]);
915
+ }
916
+ const ch = refChangeOf(y);
917
+ if (!ch) return ok([x]);
918
+ const r = definedNameThrough(x, ch);
919
+ return typeof r === "string" ? barrier2(r) : ok([r.op]);
920
+ }
921
+ if (fy === "defname") {
922
+ if (fx === "cell") {
923
+ if (setNameWithoutPrevious(y) && hasFormula(x)) return barrier2(SET_NAME_BARRIER);
924
+ const ch2 = refChangeOf(y);
925
+ return ch2 ? cellsOverChange(x, ch2) : ok([x]);
926
+ }
927
+ const ch = refChangeOf(x);
928
+ if (!ch) return ok([x]);
929
+ const r = definedNameThrough(y, ch);
930
+ if (typeof r === "string") return barrier2(r);
931
+ return ok(r.changed ? [x, asDerived(r.op)] : [x]);
932
+ }
933
+ if (fx === "cell") {
934
+ const ch = refChangeOf(y);
935
+ return ch ? cellsOverChange(x, ch) : null;
936
+ }
937
+ if (fy === "cell") {
938
+ const ch = refChangeOf(x);
939
+ if (!ch) return null;
940
+ const lost = [];
941
+ forEachCell(cellsOf(y), (r, c) => {
942
+ if (!cellThrough(sheetOf(y), ch, r, c)) lost.push(cellRef(sheetOf(y), r, c));
943
+ });
944
+ const add = rewritesOf(y, ch);
945
+ if (typeof add === "string") return barrier2(add);
946
+ return ok(add ? [x, add] : [x], lost);
947
+ }
948
+ return null;
949
+ }
950
+ var withPrevious = (m, definedNames) => m.id === ID.setName && !("previousName" in m.params) ? { id: m.id, params: { ...m.params, previousName: definedNames[String(m.params.id)] ?? null } } : m;
951
+ function annotateFormulas(group, sheets, definedNames) {
952
+ let changed = false;
953
+ return group.map((m0) => {
954
+ const m = changed && (m0.id === ID.values || m0.id === ID.setName) ? asDerived(m0) : m0;
955
+ if (!changed && refChangeOf(withPrevious(m0, definedNames))) changed = true;
956
+ if (m.id === ID.values) {
957
+ const texts = [];
958
+ forEachCell(cellsOf(m), (_r, _c, v) => {
959
+ if (v && isFormulaText(v.f)) texts.push(v.f);
960
+ if (v && typeof v.ref === "string") texts.push(v.ref);
961
+ });
962
+ return { id: m.id, params: withRefSheets(m.params, refSheetsFor(texts, sheets)) };
963
+ }
964
+ if (m.id === ID.setName) {
965
+ const texts = NAME_TEXT_KEYS.map((k) => m.params[k]).filter((t) => typeof t === "string");
966
+ return { id: m.id, params: { ...withRefSheets(m.params, refSheetsFor(texts, sheets)), previousName: withPrevious(m, definedNames).params.previousName } };
967
+ }
968
+ return m;
544
969
  });
545
- return ok(isEmptyMatrix(out) ? [] : [cellsMutation(x, out)], [], dropped);
546
970
  }
547
971
  function sizeOf(m) {
548
972
  const p = m.params;
@@ -593,6 +1017,8 @@ function transformMutation(x, y, side) {
593
1017
  }
594
1018
  const pb = PAIR_BARRIERS[pairKey(x.id, y.id)];
595
1019
  if (pb) return barrier2(`${x.id} \xD7 ${y.id}: ${pb.reason}${pb.seed === null ? "" : ` (fuzz seed ${pb.seed})`}`);
1020
+ const fp = formulaPair(x, y, side);
1021
+ if (fp) return fp;
596
1022
  if (sheetOf(x) !== sheetOf(y)) return ok([x]);
597
1023
  const fx = familyOf(x.id);
598
1024
  const fy = familyOf(y.id);
@@ -606,7 +1032,6 @@ function transformMutation(x, y, side) {
606
1032
  return ok(out.map((o) => buildStructural(x, sx.axis, o)), [], dropped);
607
1033
  }
608
1034
  if (sy) {
609
- if (fx === "cell") return transformCellsOverStructural(x, sy);
610
1035
  if (fx === "merge") {
611
1036
  const kept = [];
612
1037
  const dropped = [];
@@ -631,13 +1056,6 @@ function transformMutation(x, y, side) {
631
1056
  return ok([x]);
632
1057
  }
633
1058
  if (sx) {
634
- if (fy === "cell") {
635
- const lost = [];
636
- forEachCell(cellsOf(y), (r, c) => {
637
- if ((sx.axis === "row" ? mapIndex(sx.op, r) : mapIndex(sx.op, c)) === null) lost.push(cellRef(sheetOf(y), r, c));
638
- });
639
- return ok([x], lost);
640
- }
641
1059
  if (fy !== "merge" || y.id !== ID.addMerge) return ok([x]);
642
1060
  const remove = [];
643
1061
  const add = [];
@@ -749,15 +1167,24 @@ var sheetTransformer = {
749
1167
  export {
750
1168
  CONTENT_KEYS,
751
1169
  PAIR_BARRIERS,
1170
+ REF_ERROR,
752
1171
  SHEET_MUTATIONS,
753
1172
  SHEET_TRANSFORMER_VERSION,
1173
+ SIDE_CAR_PARAMS,
754
1174
  allowParams,
1175
+ annotateFormulas,
755
1176
  cellRef,
756
1177
  completeCellContent,
757
1178
  exactMergeRemovals,
1179
+ forApply,
1180
+ formulaBarrier,
758
1181
  isBarrier,
1182
+ isFormulaText,
759
1183
  mapIndex,
760
1184
  mergeThrough,
1185
+ refSheetsAfter,
1186
+ refSheetsFor,
1187
+ rewriteFormula,
761
1188
  sheetTransformer,
762
1189
  transformAxis,
763
1190
  transformGroup,