@ai-matrx/office 0.8.22 → 0.8.23
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/CHANGELOG.md +20 -0
- package/dist/collab-sheets-transform.cjs +151 -79
- package/dist/collab-sheets-transform.cjs.map +1 -1
- package/dist/collab-sheets-transform.d.cts +1 -1
- package/dist/collab-sheets-transform.d.ts +1 -1
- package/dist/collab-sheets-transform.js +151 -79
- package/dist/collab-sheets-transform.js.map +1 -1
- package/dist/docs.cjs +74 -1
- package/dist/docs.cjs.map +1 -1
- package/dist/docs.d.cts +52 -1
- package/dist/docs.d.ts +52 -1
- package/dist/docs.js +73 -0
- package/dist/docs.js.map +1 -1
- package/dist/sheets-comment-anchors.cjs +217 -0
- package/dist/sheets-comment-anchors.cjs.map +1 -0
- package/dist/sheets-comment-anchors.d.cts +52 -0
- package/dist/sheets-comment-anchors.d.ts +52 -0
- package/dist/sheets-comment-anchors.js +217 -0
- package/dist/sheets-comment-anchors.js.map +1 -0
- package/dist/univer-collab.cjs +164 -92
- package/dist/univer-collab.cjs.map +1 -1
- package/dist/univer-collab.js +164 -92
- package/dist/univer-collab.js.map +1 -1
- package/package.json +13 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.23
|
|
4
|
+
### C9: comment anchors that follow concurrent edits
|
|
5
|
+
|
|
6
|
+
- **Docs:** `addCommentAnchorActions(body, start, end, id)` anchors a passage with a `COMMENT` custom range (Univer's own builder, so the op has the link family's shape C0 proved); `commentAnchorsOf(body)` reads every anchor back (split pieces joined, Univer's inclusive end as `[start, end)`); `removeCommentAnchorActions` takes a cancelled draft's anchor out; `commentPartAnchor` / `anchorIdOfPartAnchor` are the thread's `part_anchor` (`anchor:<id>`, quote as `label`). Fuzz on the C0 corpora: anchor × every captured family and anchor × anchor, 0 divergences in 180,000 checks at 2,000 seeds; red check without a transform.
|
|
7
|
+
- **Sheets:** `installSheetCommentAnchors(univer)` registers `matrx.mutation.set-comment-anchor`, the `SHEET_MATRX_COMMENT_ANCHORS_PLUGIN` resource and a listener that moves anchors through every applied row/column insert, remove and move (and drops them with their row, column or sheet). `transformCommentAnchor` (own file) is called first by `transformMutation`; the registry declares the id (family `anchor`), and the census exempts our own ids from the Univer scan. Fuzz: 40,000 checks, 0 divergences (it caught the same anchor id on two sheets being treated as one); two headless seats: B5 becomes B6 when a peer inserts a row above, survives save and reload, and is gone on both seats when its row is deleted. The fold installs the anchors too.
|
|
8
|
+
- New export `./sheets/comment-anchors`. Consumer action: call `installSheetCommentAnchors(univer)` before creating a workbook unit that co-edits comments.
|
|
9
|
+
|
|
3
10
|
## 0.8.22
|
|
4
11
|
### C5c: Sheets co-editing — merge divergence fixed, number formats, counts and sheets travel
|
|
5
12
|
|
|
@@ -14,6 +21,19 @@
|
|
|
14
21
|
- `SHEETS_COLLAB_PROTO` = 2: a room opened by this build is raised to proto 2 (when its checkpoint is at head) and older tabs open it read-only, so seats with and without merge completion never share a room.
|
|
15
22
|
- Consumer action: none.
|
|
16
23
|
|
|
24
|
+
## 0.8.21
|
|
25
|
+
### C5c: Sheets co-editing — merge divergence fixed, number formats, counts and sheets travel
|
|
26
|
+
|
|
27
|
+
- **Merge divergence across chained row/column deletes, fixed at its cause.** Univer 1.0.4's structural commands only fix the merges that intersect the COMMAND's range on the other axis (any `-by-range` call with a partial range, an API call) and only while its ref-range registration is current (never refreshed after a content load or a remove-merge). A cut merge it skipped stayed put on the sender while every transform moved it, so seats ended a column apart (the session fuzz's merge at column -1 vs 0). Capture now completes the command (`mergeFixupsOwed`): the cut merges still on the sheet are removed right before the structural mutation and re-added at their model position right after it, in the traveling group and on the sender alike. Multi-seat session fuzz: 0 hash mismatches (was 2–18 per run).
|
|
28
|
+
- **Number formats travel** (`sheet.mutation.set.numfmt` / `remove.numfmt`, family `numfmt`): typing a date or a currency no longer rolls back. Ranges follow row/column inserts, removes and moves cell by cell; against another write of the style key `n` (a numfmt, a style with `n`, a style clear, a cell delete) the later-committed op wins.
|
|
29
|
+
- **Row/column counts travel** (`set-worksheet-row-count` / `-column-count`, family `count`): a concurrent insert/remove on that axis shifts the count; two counts at once, the larger wins. "Refresh from table" growing a sheet is no longer rolled back.
|
|
30
|
+
- **Sheets: add, remove, order travel** (family `sheet`): the sheet order is transformed as one more axis. Capture records a removed sheet's index and how many sheets are left (`annotateSheetOps`, side-car `sheetIndex`, `sheetsLeft`); an insert-sheet's styles are inlined (`inlineSheetStyles`). An edit to a sheet removed at the same time is dropped and its author told; a formula naming a removed sheet, and two removes that would leave no sheet, are barriers.
|
|
31
|
+
- **Selections: a seat's own row/column/count/sheet change keeps its selection inside the sheet** (API calls set no selection; the editor crashed with "Range is out of bounds"); a remote count/sheet change clamps it too.
|
|
32
|
+
- **Transform speed:** the group transform walks the OT grid iteratively; untouched cell ops pass through by identity (no overlap, no formula, change elsewhere); barrier checks are cached per op; undo-stack rebase reuses each list's traveling shape and compares by identity.
|
|
33
|
+
- **Tests:** new chained TP1 fuzz (`sheet-transform-chain-fuzz.test.ts`, 3–5 real commands per side, plus capture fidelity); the multi-seat session fuzz is seed-repeatable (`createSheetsOtAdapter` hook `stateHash`, a synchronous hash in tests) and asserts zero incidents. `SHEET_TRANSFORMER_VERSION` = `c5c-1`.
|
|
34
|
+
- `SHEETS_COLLAB_PROTO` = 2: a room opened by this build is raised to proto 2 (when its checkpoint is at head) and older tabs open it read-only, so seats with and without merge completion never share a room.
|
|
35
|
+
- Consumer action: none.
|
|
36
|
+
|
|
17
37
|
## 0.8.20
|
|
18
38
|
|
|
19
39
|
SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
|
|
@@ -86,6 +86,8 @@ var SHEET_MUTATIONS = {
|
|
|
86
86
|
"sheet.mutation.insert-sheet": covered("sheet", "index", "sheet"),
|
|
87
87
|
"sheet.mutation.remove-sheet": covered("sheet", "sheetIndex", "sheetsLeft"),
|
|
88
88
|
"sheet.mutation.set-worksheet-order": covered("sheet", "fromOrder", "toOrder"),
|
|
89
|
+
// ── covered (C9): our own cell comment anchors (comment-anchor-transform.ts; not emitted by Univer) ──────
|
|
90
|
+
"matrx.mutation.set-comment-anchor": covered("anchor", "anchorId", "cell"),
|
|
89
91
|
"sheet.mutation.reorder-range": covered("sort", "range", "order"),
|
|
90
92
|
// ── never travels ────────────────────────────────────────────────────────────────────────────────
|
|
91
93
|
"formula.mutation.set-formula-calculation-start": never(FORMULA_RESULT),
|
|
@@ -528,6 +530,74 @@ function refSheetsFor(texts, sheets) {
|
|
|
528
530
|
return Object.keys(out).length ? out : void 0;
|
|
529
531
|
}
|
|
530
532
|
|
|
533
|
+
// src/collab/sheets/comment-anchor-transform.ts
|
|
534
|
+
var SET_SHEET_COMMENT_ANCHOR = "matrx.mutation.set-comment-anchor";
|
|
535
|
+
var REMOVE_SHEET = "sheet.mutation.remove-sheet";
|
|
536
|
+
function structuralAxisOp(id, params) {
|
|
537
|
+
const p = params;
|
|
538
|
+
switch (id) {
|
|
539
|
+
case "sheet.mutation.insert-row":
|
|
540
|
+
return p.range ? { axis: "row", op: { kind: "insert", at: p.range.startRow, count: p.range.endRow - p.range.startRow + 1 } } : null;
|
|
541
|
+
case "sheet.mutation.remove-rows":
|
|
542
|
+
return p.range ? { axis: "row", op: { kind: "remove", start: p.range.startRow, count: p.range.endRow - p.range.startRow + 1 } } : null;
|
|
543
|
+
case "sheet.mutation.insert-col":
|
|
544
|
+
return p.range ? { axis: "col", op: { kind: "insert", at: p.range.startColumn, count: p.range.endColumn - p.range.startColumn + 1 } } : null;
|
|
545
|
+
case "sheet.mutation.remove-col":
|
|
546
|
+
return p.range ? { axis: "col", op: { kind: "remove", start: p.range.startColumn, count: p.range.endColumn - p.range.startColumn + 1 } } : null;
|
|
547
|
+
case "sheet.mutation.move-rows":
|
|
548
|
+
return p.sourceRange && p.targetRange ? { axis: "row", op: { kind: "move", from: p.sourceRange.startRow, count: p.sourceRange.endRow - p.sourceRange.startRow + 1, to: p.targetRange.startRow } } : null;
|
|
549
|
+
case "sheet.mutation.move-columns":
|
|
550
|
+
return p.sourceRange && p.targetRange ? {
|
|
551
|
+
axis: "col",
|
|
552
|
+
op: { kind: "move", from: p.sourceRange.startColumn, count: p.sourceRange.endColumn - p.sourceRange.startColumn + 1, to: p.targetRange.startColumn }
|
|
553
|
+
} : null;
|
|
554
|
+
default:
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function moveSheetAnchorCell(cell, sheet, id, params) {
|
|
559
|
+
if (String(params.subUnitId ?? "") !== sheet) return cell;
|
|
560
|
+
if (id === REMOVE_SHEET) return null;
|
|
561
|
+
const s = structuralAxisOp(id, params);
|
|
562
|
+
if (!s) return cell;
|
|
563
|
+
if (s.axis === "row") {
|
|
564
|
+
const row = mapIndex(s.op, cell.row);
|
|
565
|
+
return row === null ? null : row === cell.row ? cell : { row, col: cell.col };
|
|
566
|
+
}
|
|
567
|
+
const col = mapIndex(s.op, cell.col);
|
|
568
|
+
return col === null ? null : col === cell.col ? cell : { row: cell.row, col };
|
|
569
|
+
}
|
|
570
|
+
function colName(c) {
|
|
571
|
+
let s = "";
|
|
572
|
+
for (let n = c + 1; n > 0; n = Math.floor((n - 1) / 26)) s = String.fromCharCode(65 + (n - 1) % 26) + s;
|
|
573
|
+
return s;
|
|
574
|
+
}
|
|
575
|
+
var sheetCellName = (cell) => `${colName(cell.col)}${cell.row + 1}`;
|
|
576
|
+
function cellOf(m) {
|
|
577
|
+
const c = m.params.cell;
|
|
578
|
+
return c && typeof c.row === "number" && typeof c.col === "number" ? c : null;
|
|
579
|
+
}
|
|
580
|
+
var ok = (op, lost = [], dropped = []) => ({ op, lost, dropped });
|
|
581
|
+
var label = (m) => {
|
|
582
|
+
const c = cellOf(m);
|
|
583
|
+
return `${String(m.params.subUnitId ?? "")}!${c ? sheetCellName(c) : "?"} comment`;
|
|
584
|
+
};
|
|
585
|
+
function transformCommentAnchor(x, y, side) {
|
|
586
|
+
const xa = x.id === SET_SHEET_COMMENT_ANCHOR;
|
|
587
|
+
const ya = y.id === SET_SHEET_COMMENT_ANCHOR;
|
|
588
|
+
if (!xa && !ya) return null;
|
|
589
|
+
if (!xa) return ok([x]);
|
|
590
|
+
if (ya) {
|
|
591
|
+
if (x.params.anchorId !== y.params.anchorId || x.params.subUnitId !== y.params.subUnitId) return ok([x]);
|
|
592
|
+
return side === "left" ? ok([x], [label(y)]) : ok([], [], [label(x)]);
|
|
593
|
+
}
|
|
594
|
+
const cell = cellOf(x);
|
|
595
|
+
if (!cell) return ok([x]);
|
|
596
|
+
const moved = moveSheetAnchorCell(cell, String(x.params.subUnitId ?? ""), y.id, y.params);
|
|
597
|
+
if (!moved) return ok([], [], [label(x)]);
|
|
598
|
+
return ok([moved === cell ? x : { id: x.id, params: { ...x.params, cell: moved } }]);
|
|
599
|
+
}
|
|
600
|
+
|
|
531
601
|
// src/collab/sheets/transform/transform.ts
|
|
532
602
|
var SHEET_TRANSFORMER_VERSION = "c5c-1";
|
|
533
603
|
var ID = {
|
|
@@ -559,16 +629,16 @@ var ID = {
|
|
|
559
629
|
var PAIR_BARRIERS = {};
|
|
560
630
|
var pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
561
631
|
var barrier2 = (reason) => ({ barrier: true, reason });
|
|
562
|
-
var
|
|
632
|
+
var ok2 = (op, lost = [], dropped = []) => ({ op, lost, dropped });
|
|
563
633
|
var flip = (side) => side === "left" ? "right" : "left";
|
|
564
634
|
var sheetOf = (m) => String(m.params.subUnitId ?? "");
|
|
565
|
-
function
|
|
635
|
+
function colName2(c) {
|
|
566
636
|
let s = "";
|
|
567
637
|
for (let n = c + 1; n > 0; n = Math.floor((n - 1) / 26)) s = String.fromCharCode(65 + (n - 1) % 26) + s;
|
|
568
638
|
return s;
|
|
569
639
|
}
|
|
570
|
-
var cellRef = (sheet, row, col) => `${sheet}!${
|
|
571
|
-
var rangeRef = (sheet, r) => `${sheet}!${
|
|
640
|
+
var cellRef = (sheet, row, col) => `${sheet}!${colName2(col)}${row + 1}`;
|
|
641
|
+
var rangeRef = (sheet, r) => `${sheet}!${colName2(r.startColumn)}${r.startRow + 1}:${colName2(r.endColumn)}${r.endRow + 1}`;
|
|
572
642
|
function structuralOf(m) {
|
|
573
643
|
const p = m.params;
|
|
574
644
|
switch (m.id) {
|
|
@@ -823,9 +893,9 @@ function transformCell(X, Y, side) {
|
|
|
823
893
|
return { entries: Object.keys(out).length ? [out, ...tail] : tail, xLost, yLost };
|
|
824
894
|
}
|
|
825
895
|
function transformCellsOverCells(x, y, side) {
|
|
826
|
-
if (sheetOf(x) !== sheetOf(y)) return
|
|
896
|
+
if (sheetOf(x) !== sheetOf(y)) return ok2([x]);
|
|
827
897
|
const ycells = cellsOf(y);
|
|
828
|
-
if (!cellsOverlap(cellsOf(x), ycells)) return
|
|
898
|
+
if (!cellsOverlap(cellsOf(x), ycells)) return ok2([x]);
|
|
829
899
|
const xd = isDerived(x);
|
|
830
900
|
const yd = isDerived(y);
|
|
831
901
|
const eff = derivedSide(x, y, side);
|
|
@@ -851,7 +921,7 @@ function transformCellsOverCells(x, y, side) {
|
|
|
851
921
|
});
|
|
852
922
|
if (why) return barrier2(why);
|
|
853
923
|
const op = passes.filter((p) => !isEmptyMatrix(p)).map((p) => cellsMutation(x, p));
|
|
854
|
-
return
|
|
924
|
+
return ok2(op, lost, dropped);
|
|
855
925
|
}
|
|
856
926
|
var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived", "sheetIndex", "sheetsLeft"];
|
|
857
927
|
var isDerived = (m) => m.params.derived === true;
|
|
@@ -922,7 +992,7 @@ function cellThrough(sheet, change, r, c) {
|
|
|
922
992
|
return nr === null || nc === null ? null : [nr, nc];
|
|
923
993
|
}
|
|
924
994
|
function cellsOverChange(x, change) {
|
|
925
|
-
if (!hasFormula(x) && (change.kind !== "axis" || change.sheet !== sheetOf(x))) return
|
|
995
|
+
if (!hasFormula(x) && (change.kind !== "axis" || change.sheet !== sheetOf(x))) return ok2([x]);
|
|
926
996
|
const ctx = formulaCtx(x);
|
|
927
997
|
const out = {};
|
|
928
998
|
const dropped = [];
|
|
@@ -951,9 +1021,9 @@ function cellsOverChange(x, change) {
|
|
|
951
1021
|
});
|
|
952
1022
|
if (why) return barrier2(why);
|
|
953
1023
|
const rsAfter = refSheetsAfter(ctx.refSheets, change);
|
|
954
|
-
if (!changed && JSON.stringify(rsAfter ?? null) === JSON.stringify(ctx.refSheets ?? null)) return
|
|
1024
|
+
if (!changed && JSON.stringify(rsAfter ?? null) === JSON.stringify(ctx.refSheets ?? null)) return ok2([x]);
|
|
955
1025
|
const params = { ...withRefSheets(x.params, rsAfter), cellValue: out };
|
|
956
|
-
return
|
|
1026
|
+
return ok2(isEmptyMatrix(out) ? [] : [{ id: x.id, params }], [], dropped);
|
|
957
1027
|
}
|
|
958
1028
|
function rewritesOf(y, change) {
|
|
959
1029
|
if (!hasFormula(y)) return null;
|
|
@@ -1000,36 +1070,36 @@ function formulaPair(x, y, side) {
|
|
|
1000
1070
|
const fy = familyOf(y.id);
|
|
1001
1071
|
const SET_NAME_BARRIER = "set-defined-name without previousName (capture must record the name it replaces) against formula text";
|
|
1002
1072
|
if (fx === "defname" && fy === "defname") {
|
|
1003
|
-
if (String(x.params.id) !== String(y.params.id)) return
|
|
1073
|
+
if (String(x.params.id) !== String(y.params.id)) return ok2([x]);
|
|
1004
1074
|
const eff = derivedSide(x, y, side);
|
|
1005
|
-
if (eff === "left") return
|
|
1006
|
-
return
|
|
1075
|
+
if (eff === "left") return ok2([x], isDerived(y) ? [] : [`defined name "${String(y.params.name)}"`]);
|
|
1076
|
+
return ok2([], [], isDerived(x) ? [] : [`defined name "${String(x.params.name)}"`]);
|
|
1007
1077
|
}
|
|
1008
1078
|
if (fx === "defname") {
|
|
1009
1079
|
if (fy === "cell") {
|
|
1010
1080
|
if (setNameWithoutPrevious(x) && hasFormula(y)) return barrier2(SET_NAME_BARRIER);
|
|
1011
1081
|
const ch2 = refChangeOf(x);
|
|
1012
|
-
if (!ch2) return
|
|
1082
|
+
if (!ch2) return ok2([x]);
|
|
1013
1083
|
const add = rewritesOf(y, ch2);
|
|
1014
1084
|
if (typeof add === "string") return barrier2(add);
|
|
1015
|
-
return
|
|
1085
|
+
return ok2(add ? [x, add] : [x]);
|
|
1016
1086
|
}
|
|
1017
1087
|
const ch = refChangeOf(y);
|
|
1018
|
-
if (!ch) return
|
|
1088
|
+
if (!ch) return ok2([x]);
|
|
1019
1089
|
const r = definedNameThrough(x, ch);
|
|
1020
|
-
return typeof r === "string" ? barrier2(r) :
|
|
1090
|
+
return typeof r === "string" ? barrier2(r) : ok2([r.op]);
|
|
1021
1091
|
}
|
|
1022
1092
|
if (fy === "defname") {
|
|
1023
1093
|
if (fx === "cell") {
|
|
1024
1094
|
if (setNameWithoutPrevious(y) && hasFormula(x)) return barrier2(SET_NAME_BARRIER);
|
|
1025
1095
|
const ch2 = refChangeOf(y);
|
|
1026
|
-
return ch2 ? cellsOverChange(x, ch2) :
|
|
1096
|
+
return ch2 ? cellsOverChange(x, ch2) : ok2([x]);
|
|
1027
1097
|
}
|
|
1028
1098
|
const ch = refChangeOf(x);
|
|
1029
|
-
if (!ch) return
|
|
1099
|
+
if (!ch) return ok2([x]);
|
|
1030
1100
|
const r = definedNameThrough(y, ch);
|
|
1031
1101
|
if (typeof r === "string") return barrier2(r);
|
|
1032
|
-
return
|
|
1102
|
+
return ok2(r.changed ? [x, asDerived(r.op)] : [x]);
|
|
1033
1103
|
}
|
|
1034
1104
|
if (fx === "cell") {
|
|
1035
1105
|
const ch = refChangeOf(y);
|
|
@@ -1044,7 +1114,7 @@ function formulaPair(x, y, side) {
|
|
|
1044
1114
|
});
|
|
1045
1115
|
const add = rewritesOf(y, ch);
|
|
1046
1116
|
if (typeof add === "string") return barrier2(add);
|
|
1047
|
-
return
|
|
1117
|
+
return ok2(add ? [x, add] : [x], lost);
|
|
1048
1118
|
}
|
|
1049
1119
|
return null;
|
|
1050
1120
|
}
|
|
@@ -1192,20 +1262,20 @@ function numfmtPair(x, y, side, sx, sy) {
|
|
|
1192
1262
|
const nx = isNumfmt(x);
|
|
1193
1263
|
const ny = isNumfmt(y);
|
|
1194
1264
|
if (!nx && !ny) return null;
|
|
1195
|
-
if (nx && sy) return
|
|
1196
|
-
if (ny && sx) return
|
|
1265
|
+
if (nx && sy) return ok2(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsThrough(sy, p.ranges) }))));
|
|
1266
|
+
if (ny && sx) return ok2([x]);
|
|
1197
1267
|
if (nx && ny) {
|
|
1198
|
-
if (side === "left") return
|
|
1268
|
+
if (side === "left") return ok2([x]);
|
|
1199
1269
|
const ys = nfParts(y).flatMap((p) => p.ranges);
|
|
1200
|
-
return
|
|
1270
|
+
return ok2(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsMinus(p.ranges, ys) }))));
|
|
1201
1271
|
}
|
|
1202
1272
|
if (nx && y.id === ID.values) {
|
|
1203
|
-
if (side === "left") return
|
|
1273
|
+
if (side === "left") return ok2([x]);
|
|
1204
1274
|
const ys = nWrites(y);
|
|
1205
|
-
return ys.length ?
|
|
1275
|
+
return ys.length ? ok2(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsMinus(p.ranges, ys) })))) : ok2([x]);
|
|
1206
1276
|
}
|
|
1207
1277
|
if (ny && x.id === ID.values) {
|
|
1208
|
-
if (side === "left") return
|
|
1278
|
+
if (side === "left") return ok2([x]);
|
|
1209
1279
|
const out = {};
|
|
1210
1280
|
const tail = {};
|
|
1211
1281
|
let touched = false;
|
|
@@ -1231,31 +1301,31 @@ function numfmtPair(x, y, side, sx, sy) {
|
|
|
1231
1301
|
}
|
|
1232
1302
|
putCell(out, r, c, v);
|
|
1233
1303
|
});
|
|
1234
|
-
if (!touched) return
|
|
1304
|
+
if (!touched) return ok2([x]);
|
|
1235
1305
|
const ops = [];
|
|
1236
1306
|
if (!isEmptyMatrix(out)) ops.push(cellsMutation(x, out));
|
|
1237
1307
|
if (!isEmptyMatrix(tail)) ops.push({ id: ID.values, params: { subUnitId: sheetOf(x), cellValue: tail } });
|
|
1238
|
-
return
|
|
1308
|
+
return ok2(ops);
|
|
1239
1309
|
}
|
|
1240
|
-
return
|
|
1310
|
+
return ok2([x]);
|
|
1241
1311
|
}
|
|
1242
1312
|
var isCount = (m) => m.id === ID.rowCount || m.id === ID.colCount;
|
|
1243
1313
|
var countAxis = (m) => m.id === ID.rowCount ? "row" : "col";
|
|
1244
1314
|
var countKey = (m) => m.id === ID.rowCount ? "rowCount" : "columnCount";
|
|
1245
1315
|
function countPair(x, y, sy) {
|
|
1246
|
-
if (!isCount(x)) return isCount(y) ?
|
|
1316
|
+
if (!isCount(x)) return isCount(y) ? ok2([x]) : null;
|
|
1247
1317
|
const k = countKey(x);
|
|
1248
1318
|
const v = Number(x.params[k]);
|
|
1249
1319
|
if (sy) {
|
|
1250
|
-
if (sy.axis !== countAxis(x) || sy.op.kind === "move") return
|
|
1320
|
+
if (sy.axis !== countAxis(x) || sy.op.kind === "move") return ok2([x]);
|
|
1251
1321
|
const d = sy.op.kind === "insert" ? sy.op.count : -sy.op.count;
|
|
1252
|
-
return
|
|
1322
|
+
return ok2([{ id: x.id, params: { ...x.params, [k]: Math.max(1, v + d) } }]);
|
|
1253
1323
|
}
|
|
1254
1324
|
if (isCount(y) && y.id === x.id) {
|
|
1255
1325
|
const w = Number(y.params[k]);
|
|
1256
|
-
return
|
|
1326
|
+
return ok2([w > v ? { id: x.id, params: { ...x.params, [k]: w } } : x]);
|
|
1257
1327
|
}
|
|
1258
|
-
return
|
|
1328
|
+
return ok2([x]);
|
|
1259
1329
|
}
|
|
1260
1330
|
var isSheetOp = (m) => m.id === ID.insertSheet || m.id === ID.removeSheet || m.id === ID.sheetOrder;
|
|
1261
1331
|
var sheetIdOf = (m) => m.id === ID.insertSheet ? String(m.params.sheet?.id ?? "") : sheetOf(m);
|
|
@@ -1288,31 +1358,31 @@ function sheetPair(x, y, side) {
|
|
|
1288
1358
|
const b = sheetAxisOf(y);
|
|
1289
1359
|
if (!a || !b) return barrier2("a sheet add or remove without its index (capture must call annotateSheetOps)");
|
|
1290
1360
|
if (x.id === ID.insertSheet && y.id === ID.insertSheet && sheetIdOf(x) === sheetIdOf(y)) {
|
|
1291
|
-
return JSON.stringify(x.params) === JSON.stringify(y.params) ?
|
|
1361
|
+
return JSON.stringify(x.params) === JSON.stringify(y.params) ? ok2([]) : barrier2("the same sheet id added twice with different content");
|
|
1292
1362
|
}
|
|
1293
1363
|
if (x.id === ID.removeSheet && typeof x.params.sheetsLeft === "number" && sheetIdOf(x) !== sheetIdOf(y) && (y.id === ID.removeSheet || y.id === ID.insertSheet)) {
|
|
1294
1364
|
const left = x.params.sheetsLeft + (y.id === ID.insertSheet ? 1 : -1);
|
|
1295
1365
|
if (left < 1) return barrier2("concurrent sheet removes would leave the workbook without a sheet");
|
|
1296
1366
|
const out2 = transformAxis(a, b, side);
|
|
1297
1367
|
if (!out2) return barrier2("sheet order: no transform");
|
|
1298
|
-
return
|
|
1368
|
+
return ok2(out2.map((o) => buildSheetOp({ id: x.id, params: { ...x.params, sheetsLeft: left } }, o)));
|
|
1299
1369
|
}
|
|
1300
1370
|
const out = transformAxis(a, b, side);
|
|
1301
1371
|
if (!out) return barrier2("sheet order: no transform");
|
|
1302
|
-
return
|
|
1372
|
+
return ok2(out.map((o) => buildSheetOp(x, o)), [], out.length === 0 && x.id === ID.sheetOrder ? [`${sheetIdOf(x)}: sheet order`] : []);
|
|
1303
1373
|
}
|
|
1304
1374
|
if (ys) {
|
|
1305
|
-
if (y.id !== ID.removeSheet) return
|
|
1375
|
+
if (y.id !== ID.removeSheet) return ok2([x]);
|
|
1306
1376
|
const gone2 = sheetOf(y);
|
|
1307
1377
|
if (namesSheet(x, gone2)) return barrier2("a formula names a sheet removed at the same time");
|
|
1308
1378
|
if (sheetOf(x) === gone2 && x.id !== ID.setName && x.id !== ID.removeName)
|
|
1309
|
-
return
|
|
1310
|
-
return
|
|
1379
|
+
return ok2([], [], isDerived(x) ? [] : [`${gone2}: the sheet was removed`]);
|
|
1380
|
+
return ok2([x]);
|
|
1311
1381
|
}
|
|
1312
|
-
if (x.id !== ID.removeSheet) return
|
|
1382
|
+
if (x.id !== ID.removeSheet) return ok2([x]);
|
|
1313
1383
|
const gone = sheetOf(x);
|
|
1314
1384
|
if (namesSheet(y, gone)) return barrier2("a formula names a sheet removed at the same time");
|
|
1315
|
-
return
|
|
1385
|
+
return ok2([x], sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : []);
|
|
1316
1386
|
}
|
|
1317
1387
|
function annotateSheetOps(group, sheetOrder) {
|
|
1318
1388
|
const order = [...sheetOrder];
|
|
@@ -1410,22 +1480,22 @@ function sortPair(x, y, sx, sy) {
|
|
|
1410
1480
|
const a = sortOf(x).range;
|
|
1411
1481
|
const b = sortOf(y).range;
|
|
1412
1482
|
const overlap = a.startRow <= b.endRow && b.startRow <= a.endRow && a.startColumn <= b.endColumn && b.startColumn <= a.endColumn;
|
|
1413
|
-
return overlap ? barrier2("two sorts of overlapping ranges at once") :
|
|
1483
|
+
return overlap ? barrier2("two sorts of overlapping ranges at once") : ok2([x]);
|
|
1414
1484
|
}
|
|
1415
1485
|
if (xs) {
|
|
1416
1486
|
if (sy) {
|
|
1417
1487
|
const t = sortThrough(sortOf(x), sy);
|
|
1418
|
-
return t ?
|
|
1488
|
+
return t ? ok2(buildSort(x, t)) : barrier2("a row or column move through a sorted range");
|
|
1419
1489
|
}
|
|
1420
|
-
return
|
|
1490
|
+
return ok2([x]);
|
|
1421
1491
|
}
|
|
1422
1492
|
const o = sortOf(y);
|
|
1423
1493
|
const Q = sortQ(o);
|
|
1424
1494
|
const moved = (r) => r >= o.range.startRow && r <= o.range.endRow && Q.has(r);
|
|
1425
1495
|
if (sx) {
|
|
1426
|
-
if (sx.axis === "col") return sx.op.kind === "move" && !sortThrough(o, sx) ? barrier2("a column move through a sorted range") :
|
|
1427
|
-
if (sx.op.kind === "insert") return
|
|
1428
|
-
if (sx.op.kind === "move") return sortThrough(o, sx) ?
|
|
1496
|
+
if (sx.axis === "col") return sx.op.kind === "move" && !sortThrough(o, sx) ? barrier2("a column move through a sorted range") : ok2([x]);
|
|
1497
|
+
if (sx.op.kind === "insert") return ok2([x]);
|
|
1498
|
+
if (sx.op.kind === "move") return sortThrough(o, sx) ? ok2([x]) : barrier2("a row move through a sorted range");
|
|
1429
1499
|
const rows = [];
|
|
1430
1500
|
let partial = false;
|
|
1431
1501
|
for (let i = sx.op.start; i < sx.op.start + sx.op.count; i++) {
|
|
@@ -1433,7 +1503,7 @@ function sortPair(x, y, sx, sy) {
|
|
|
1433
1503
|
rows.push(Q.get(i) ?? i);
|
|
1434
1504
|
}
|
|
1435
1505
|
if (partial) return barrier2("a row remove against a concurrent sort of those rows");
|
|
1436
|
-
return
|
|
1506
|
+
return ok2([x]);
|
|
1437
1507
|
}
|
|
1438
1508
|
if (x.id === ID.values) {
|
|
1439
1509
|
const out = {};
|
|
@@ -1444,7 +1514,7 @@ function sortPair(x, y, sx, sy) {
|
|
|
1444
1514
|
changed = true;
|
|
1445
1515
|
} else putCell(out, r, c, v);
|
|
1446
1516
|
});
|
|
1447
|
-
return changed ?
|
|
1517
|
+
return changed ? ok2([cellsMutation(x, out)]) : ok2([x]);
|
|
1448
1518
|
}
|
|
1449
1519
|
if (isNumfmt(x)) {
|
|
1450
1520
|
const parts = nfParts(x).map((p) => {
|
|
@@ -1460,9 +1530,9 @@ function sortPair(x, y, sx, sy) {
|
|
|
1460
1530
|
}
|
|
1461
1531
|
return { ...p, ranges };
|
|
1462
1532
|
});
|
|
1463
|
-
return
|
|
1533
|
+
return ok2(nfBuild(x, parts));
|
|
1464
1534
|
}
|
|
1465
|
-
return
|
|
1535
|
+
return ok2([x]);
|
|
1466
1536
|
}
|
|
1467
1537
|
function familyOf(id) {
|
|
1468
1538
|
const e = SHEET_MUTATIONS[id];
|
|
@@ -1478,13 +1548,15 @@ function transformMutation(x, y, side) {
|
|
|
1478
1548
|
if (why) return barrier2(why);
|
|
1479
1549
|
}
|
|
1480
1550
|
}
|
|
1551
|
+
const ca = transformCommentAnchor(x, y, side);
|
|
1552
|
+
if (ca) return ca;
|
|
1481
1553
|
const pb = PAIR_BARRIERS[pairKey(x.id, y.id)];
|
|
1482
1554
|
if (pb) return barrier2(`${x.id} \xD7 ${y.id}: ${pb.reason}${pb.seed === null ? "" : ` (fuzz seed ${pb.seed})`}`);
|
|
1483
1555
|
const sp = sheetPair(x, y, side);
|
|
1484
1556
|
if (sp) return sp;
|
|
1485
1557
|
const fp = formulaPair(x, y, side);
|
|
1486
1558
|
if (fp) return fp;
|
|
1487
|
-
if (sheetOf(x) !== sheetOf(y)) return
|
|
1559
|
+
if (sheetOf(x) !== sheetOf(y)) return ok2([x]);
|
|
1488
1560
|
const fx = familyOf(x.id);
|
|
1489
1561
|
const fy = familyOf(y.id);
|
|
1490
1562
|
const sy = structuralOf(y);
|
|
@@ -1496,11 +1568,11 @@ function transformMutation(x, y, side) {
|
|
|
1496
1568
|
const np = numfmtPair(x, y, side, sx, sy);
|
|
1497
1569
|
if (np) return np;
|
|
1498
1570
|
if (sx && sy) {
|
|
1499
|
-
if (sx.axis !== sy.axis) return
|
|
1571
|
+
if (sx.axis !== sy.axis) return ok2([x]);
|
|
1500
1572
|
const out = transformAxis(sx.op, sy.op, side);
|
|
1501
1573
|
if (!out) return barrier2(`${x.id} over ${y.id}: moved blocks overlap in part, or the block would split`);
|
|
1502
1574
|
const dropped = out.length === 0 && sx.op.kind === "move" ? [`${sheetOf(x)}: moved ${sx.axis}s`] : [];
|
|
1503
|
-
return
|
|
1575
|
+
return ok2(out.map((o) => buildStructural(x, sx.axis, o)), [], dropped);
|
|
1504
1576
|
}
|
|
1505
1577
|
if (sy) {
|
|
1506
1578
|
if (fx === "merge") {
|
|
@@ -1511,23 +1583,23 @@ function transformMutation(x, y, side) {
|
|
|
1511
1583
|
if (t) kept.push(t);
|
|
1512
1584
|
else dropped.push(rangeRef(sheetOf(x), r));
|
|
1513
1585
|
}
|
|
1514
|
-
return
|
|
1586
|
+
return ok2(kept.length ? [withRanges(x, kept)] : [], [], x.id === ID.addMerge ? dropped : []);
|
|
1515
1587
|
}
|
|
1516
1588
|
if (fx === "size") {
|
|
1517
1589
|
const size = sizeOf(x);
|
|
1518
|
-
if (size.axis !== sy.axis) return
|
|
1590
|
+
if (size.axis !== sy.axis) return ok2([x]);
|
|
1519
1591
|
const next = /* @__PURE__ */ new Map();
|
|
1520
1592
|
for (const [i, v] of size.values) {
|
|
1521
1593
|
const m = mapIndex(sy.op, i);
|
|
1522
1594
|
if (m !== null) next.set(m, v);
|
|
1523
1595
|
}
|
|
1524
1596
|
const built = buildSize(x, next);
|
|
1525
|
-
return
|
|
1597
|
+
return ok2(built ? [built] : []);
|
|
1526
1598
|
}
|
|
1527
|
-
return
|
|
1599
|
+
return ok2([x]);
|
|
1528
1600
|
}
|
|
1529
1601
|
if (sx) {
|
|
1530
|
-
if (fy !== "merge" || y.id !== ID.addMerge) return
|
|
1602
|
+
if (fy !== "merge" || y.id !== ID.addMerge) return ok2([x]);
|
|
1531
1603
|
const remove = [];
|
|
1532
1604
|
const add = [];
|
|
1533
1605
|
for (const r of mergeRanges(y)) {
|
|
@@ -1537,11 +1609,11 @@ function transformMutation(x, y, side) {
|
|
|
1537
1609
|
if (t.range) add.push(t.range);
|
|
1538
1610
|
}
|
|
1539
1611
|
const lostMerges = mergeRanges(y).filter((r) => mergeThrough(sx, r).range === null).map((r) => rangeRef(sheetOf(y), r));
|
|
1540
|
-
if (!remove.length) return
|
|
1612
|
+
if (!remove.length) return ok2([x], lostMerges);
|
|
1541
1613
|
const sheet = sheetOf(x);
|
|
1542
1614
|
const pre = sx.op.kind === "insert" ? [] : [mergeMutation(ID.removeMerge, sheet, remove)];
|
|
1543
1615
|
const mid = sx.op.kind === "insert" ? [mergeMutation(ID.removeMerge, sheet, remove)] : [];
|
|
1544
|
-
return
|
|
1616
|
+
return ok2([...pre, x, ...mid, ...add.length ? [mergeMutation(ID.addMerge, sheet, add)] : []], lostMerges);
|
|
1545
1617
|
}
|
|
1546
1618
|
if (fx === "cell" && fy === "cell") return transformCellsOverCells(x, y, side);
|
|
1547
1619
|
if (fx === "merge" && fy === "merge") {
|
|
@@ -1550,45 +1622,45 @@ function transformMutation(x, y, side) {
|
|
|
1550
1622
|
const hit = (r) => yr.some((q) => intersects(r, q));
|
|
1551
1623
|
const sheet = sheetOf(x);
|
|
1552
1624
|
if (x.id === ID.addMerge && y.id === ID.addMerge) {
|
|
1553
|
-
if (!xr.some(hit)) return
|
|
1625
|
+
if (!xr.some(hit)) return ok2([x]);
|
|
1554
1626
|
if (side === "left") {
|
|
1555
1627
|
const lost = yr.filter((q) => xr.some((r) => intersects(r, q))).map((q) => rangeRef(sheet, q));
|
|
1556
|
-
return
|
|
1628
|
+
return ok2([mergeMutation(ID.removeMerge, sheet, xr.filter(hit)), x], lost);
|
|
1557
1629
|
}
|
|
1558
1630
|
const kept = xr.filter((r) => !hit(r));
|
|
1559
|
-
return
|
|
1631
|
+
return ok2(kept.length ? [withRanges(x, kept)] : [], [], xr.filter(hit).map((r) => rangeRef(sheet, r)));
|
|
1560
1632
|
}
|
|
1561
1633
|
if (x.id === ID.addMerge && y.id === ID.removeMerge) {
|
|
1562
|
-
if (side === "left" || !xr.some(hit)) return
|
|
1634
|
+
if (side === "left" || !xr.some(hit)) return ok2([x], side === "left" && xr.some(hit) ? yr.filter((q) => xr.some((r) => intersects(r, q))).map((q) => rangeRef(sheet, q)) : []);
|
|
1563
1635
|
const kept = xr.filter((r) => !hit(r));
|
|
1564
|
-
return
|
|
1636
|
+
return ok2(kept.length ? [withRanges(x, kept)] : [], [], xr.filter(hit).map((r) => rangeRef(sheet, r)));
|
|
1565
1637
|
}
|
|
1566
1638
|
if (x.id === ID.removeMerge && y.id === ID.addMerge) {
|
|
1567
1639
|
const touched = yr.filter((q) => xr.some((r) => intersects(r, q)));
|
|
1568
|
-
if (!touched.length) return
|
|
1569
|
-
if (side === "left") return
|
|
1570
|
-
return
|
|
1640
|
+
if (!touched.length) return ok2([x]);
|
|
1641
|
+
if (side === "left") return ok2([x], touched.map((q) => rangeRef(sheet, q)));
|
|
1642
|
+
return ok2([x, mergeMutation(ID.addMerge, sheet, touched)], [], xr.filter(hit).map((r) => rangeRef(sheet, r)));
|
|
1571
1643
|
}
|
|
1572
|
-
return
|
|
1644
|
+
return ok2([x]);
|
|
1573
1645
|
}
|
|
1574
1646
|
if (fx === "size" && fy === "size") {
|
|
1575
1647
|
const a = sizeOf(x);
|
|
1576
1648
|
const b = sizeOf(y);
|
|
1577
|
-
if (a.axis !== b.axis || a.attr !== b.attr) return
|
|
1649
|
+
if (a.axis !== b.axis || a.attr !== b.attr) return ok2([x]);
|
|
1578
1650
|
if (side === "left") {
|
|
1579
1651
|
const lost = [...a.values.keys()].filter((i) => b.values.has(i)).map((i) => `${sheetOf(x)}: ${a.axis} ${i + 1} ${a.attr}`);
|
|
1580
|
-
return
|
|
1652
|
+
return ok2([x], lost);
|
|
1581
1653
|
}
|
|
1582
1654
|
const next = new Map([...a.values].filter(([i]) => !b.values.has(i)));
|
|
1583
1655
|
const built = buildSize(x, next);
|
|
1584
1656
|
const dropped = [...a.values.keys()].filter((i) => b.values.has(i)).map((i) => `${sheetOf(x)}: ${a.axis} ${i + 1} ${a.attr}`);
|
|
1585
|
-
return
|
|
1657
|
+
return ok2(built ? [built] : [], [], dropped);
|
|
1586
1658
|
}
|
|
1587
1659
|
if (fx === "name" && fy === "name") {
|
|
1588
|
-
if (side === "left") return
|
|
1589
|
-
return
|
|
1660
|
+
if (side === "left") return ok2([x], [`${sheetOf(x)}: name "${String(y.params.name)}"`]);
|
|
1661
|
+
return ok2([], [], [`${sheetOf(x)}: name "${String(x.params.name)}"`]);
|
|
1590
1662
|
}
|
|
1591
|
-
return
|
|
1663
|
+
return ok2([x]);
|
|
1592
1664
|
}
|
|
1593
1665
|
var blocked = (p) => !Array.isArray(p);
|
|
1594
1666
|
function tSeq(xs, ys, side, acc) {
|