@ai-matrx/office 0.8.20 → 0.8.22
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 +21 -2
- package/dist/collab-sheets-transform.cjs +471 -17
- package/dist/collab-sheets-transform.cjs.map +1 -1
- package/dist/collab-sheets-transform.d.cts +32 -4
- package/dist/collab-sheets-transform.d.ts +32 -4
- package/dist/collab-sheets-transform.js +471 -17
- package/dist/collab-sheets-transform.js.map +1 -1
- package/dist/univer-collab.cjs +536 -28
- package/dist/univer-collab.cjs.map +1 -1
- package/dist/univer-collab.d.cts +6 -1
- package/dist/univer-collab.d.ts +6 -1
- package/dist/univer-collab.js +536 -28
- package/dist/univer-collab.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.22
|
|
4
|
+
### C5c: Sheets co-editing — merge divergence fixed, number formats, counts and sheets travel
|
|
5
|
+
|
|
6
|
+
- **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).
|
|
7
|
+
- **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.
|
|
8
|
+
- **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.
|
|
9
|
+
- **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.
|
|
10
|
+
- **Sort travels** (`reorder-range`, family `sort`): it is a row permutation inside its range, so concurrent edits follow their rows (a cell op inside the sorted columns lands where the sort moved its row; number formats likewise), a row insert keeps the new rows in place, column inserts/removes widen or clip the range. Barriers: two overlapping sorts, a row/column move through the sorted range, a row remove of rows the sort moved. The inverse is the inverted permutation.
|
|
11
|
+
- **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.
|
|
12
|
+
- **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.
|
|
13
|
+
- **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`.
|
|
14
|
+
- `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
|
+
- Consumer action: none.
|
|
16
|
+
|
|
3
17
|
## 0.8.20
|
|
4
18
|
|
|
5
|
-
|
|
6
|
-
|
|
19
|
+
SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
|
|
20
|
+
so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
|
|
21
|
+
Diff: `git diff 046a506e40 150104f478 -- apps/shared/office/src`
|
|
22
|
+
|
|
23
|
+
Changed shipped source files (2):
|
|
24
|
+
- `apps/shared/office/src/sheets/workbook-ot-room.ts`
|
|
25
|
+
- `apps/shared/office/src/univer/collab/WorkbookCollabSession.ts`
|
|
7
26
|
|
|
8
27
|
## 0.8.19
|
|
9
28
|
### C7a: the browser editor's plugin resources
|
|
@@ -28,19 +28,24 @@ __export(transform_exports, {
|
|
|
28
28
|
SIDE_CAR_PARAMS: () => SIDE_CAR_PARAMS,
|
|
29
29
|
allowParams: () => allowParams,
|
|
30
30
|
annotateFormulas: () => annotateFormulas,
|
|
31
|
+
annotateSheetOps: () => annotateSheetOps,
|
|
31
32
|
cellRef: () => cellRef,
|
|
32
33
|
completeCellContent: () => completeCellContent,
|
|
33
34
|
exactMergeRemovals: () => exactMergeRemovals,
|
|
34
35
|
forApply: () => forApply,
|
|
35
36
|
formulaBarrier: () => formulaBarrier,
|
|
37
|
+
inlineSheetStyles: () => inlineSheetStyles,
|
|
36
38
|
isBarrier: () => isBarrier,
|
|
37
39
|
isFormulaText: () => isFormulaText,
|
|
40
|
+
isStructuralMutation: () => isStructuralMutation,
|
|
38
41
|
mapIndex: () => mapIndex,
|
|
42
|
+
mergeFixupsOwed: () => mergeFixupsOwed,
|
|
39
43
|
mergeThrough: () => mergeThrough,
|
|
40
44
|
refSheetsAfter: () => refSheetsAfter,
|
|
41
45
|
refSheetsFor: () => refSheetsFor,
|
|
42
46
|
rewriteFormula: () => rewriteFormula,
|
|
43
47
|
sheetTransformer: () => sheetTransformer,
|
|
48
|
+
spanThrough: () => spanThrough,
|
|
44
49
|
transformAxis: () => transformAxis,
|
|
45
50
|
transformGroup: () => transformGroup,
|
|
46
51
|
transformMutation: () => transformMutation
|
|
@@ -73,6 +78,15 @@ var SHEET_MUTATIONS = {
|
|
|
73
78
|
"sheet.mutation.set-worksheet-row-auto-height": covered("size", "rowsAutoHeightInfo"),
|
|
74
79
|
"sheet.mutation.set-worksheet-col-width": covered("size", "ranges", "colWidth"),
|
|
75
80
|
"sheet.mutation.set-worksheet-name": covered("name", "name"),
|
|
81
|
+
// ── covered (C5c) ────────────────────────────────────────────────────────────────────────────────
|
|
82
|
+
"sheet.mutation.set.numfmt": covered("numfmt", "values", "refMap"),
|
|
83
|
+
"sheet.mutation.remove.numfmt": covered("numfmt", "ranges"),
|
|
84
|
+
"sheet.mutation.set-worksheet-row-count": covered("count", "rowCount"),
|
|
85
|
+
"sheet.mutation.set-worksheet-column-count": covered("count", "columnCount"),
|
|
86
|
+
"sheet.mutation.insert-sheet": covered("sheet", "index", "sheet"),
|
|
87
|
+
"sheet.mutation.remove-sheet": covered("sheet", "sheetIndex", "sheetsLeft"),
|
|
88
|
+
"sheet.mutation.set-worksheet-order": covered("sheet", "fromOrder", "toOrder"),
|
|
89
|
+
"sheet.mutation.reorder-range": covered("sort", "range", "order"),
|
|
76
90
|
// ── never travels ────────────────────────────────────────────────────────────────────────────────
|
|
77
91
|
"formula.mutation.set-formula-calculation-start": never(FORMULA_RESULT),
|
|
78
92
|
"formula.mutation.set-trigger-formula-calculation-start": never(FORMULA_RESULT),
|
|
@@ -106,13 +120,7 @@ var SHEET_MUTATIONS = {
|
|
|
106
120
|
"thread-comment.mutation.resolve-comment": never(UNIVER_COMMENTS),
|
|
107
121
|
"thread-comment.mutation.delete-comment": never(UNIVER_COMMENTS),
|
|
108
122
|
// ── barriers: X (no transform yet, design §10) ───────────────────────────────────────────────────
|
|
109
|
-
"sheet.mutation.reorder-range": barrier("X", "sort: a permutation, not transformed yet"),
|
|
110
|
-
"sheet.mutation.set-worksheet-row-count": barrier("X", "absolute row count; needs delta semantics"),
|
|
111
|
-
"sheet.mutation.set-worksheet-column-count": barrier("X", "absolute column count; needs delta semantics"),
|
|
112
|
-
"sheet.mutation.set-worksheet-order": barrier("X", "absolute sheet order; needs move semantics"),
|
|
113
123
|
"sheet.mutation.move-range": barrier("X", "cut-paste carries the sender's cell matrices"),
|
|
114
|
-
"sheet.mutation.insert-sheet": barrier("X", "sheet add: not in C5a"),
|
|
115
|
-
"sheet.mutation.remove-sheet": barrier("X", "sheet remove: not in C5a"),
|
|
116
124
|
"sheet.mutation.set-worksheet-hidden": barrier("X", "sheet-level setting: not in C5a"),
|
|
117
125
|
"sheet.mutation.set-tab-color": barrier("X", "sheet-level setting: not in C5a"),
|
|
118
126
|
"sheet.mutation.set-workbook-name": barrier("X", "workbook-level setting: not in C5a"),
|
|
@@ -127,8 +135,6 @@ var SHEET_MUTATIONS = {
|
|
|
127
135
|
"sheet.mutation.set-row-visible": barrier("X", "row visibility: not in C5a"),
|
|
128
136
|
"sheet.mutation.set-col-hidden": barrier("X", "column visibility: not in C5a"),
|
|
129
137
|
"sheet.mutation.set-col-visible": barrier("X", "column visibility: not in C5a"),
|
|
130
|
-
"sheet.mutation.set.numfmt": barrier("X", "number formats by range: not in C5a"),
|
|
131
|
-
"sheet.mutation.remove.numfmt": barrier("X", "number formats by range: not in C5a"),
|
|
132
138
|
"sheet.mutation.add-range-protection": barrier("X", "permissions: not in C5a"),
|
|
133
139
|
"sheet.mutation.set-range-protection": barrier("X", "permissions: not in C5a"),
|
|
134
140
|
"sheet.mutation.delete-range-protection": barrier("X", "permissions: not in C5a"),
|
|
@@ -523,7 +529,7 @@ function refSheetsFor(texts, sheets) {
|
|
|
523
529
|
}
|
|
524
530
|
|
|
525
531
|
// src/collab/sheets/transform/transform.ts
|
|
526
|
-
var SHEET_TRANSFORMER_VERSION = "
|
|
532
|
+
var SHEET_TRANSFORMER_VERSION = "c5c-1";
|
|
527
533
|
var ID = {
|
|
528
534
|
values: "sheet.mutation.set-range-values",
|
|
529
535
|
insertRow: "sheet.mutation.insert-row",
|
|
@@ -540,7 +546,15 @@ var ID = {
|
|
|
540
546
|
colWidth: "sheet.mutation.set-worksheet-col-width",
|
|
541
547
|
name: "sheet.mutation.set-worksheet-name",
|
|
542
548
|
setName: "formula.mutation.set-defined-name",
|
|
543
|
-
removeName: "formula.mutation.remove-defined-name"
|
|
549
|
+
removeName: "formula.mutation.remove-defined-name",
|
|
550
|
+
setNumfmt: "sheet.mutation.set.numfmt",
|
|
551
|
+
removeNumfmt: "sheet.mutation.remove.numfmt",
|
|
552
|
+
rowCount: "sheet.mutation.set-worksheet-row-count",
|
|
553
|
+
colCount: "sheet.mutation.set-worksheet-column-count",
|
|
554
|
+
insertSheet: "sheet.mutation.insert-sheet",
|
|
555
|
+
removeSheet: "sheet.mutation.remove-sheet",
|
|
556
|
+
sheetOrder: "sheet.mutation.set-worksheet-order",
|
|
557
|
+
sort: "sheet.mutation.reorder-range"
|
|
544
558
|
};
|
|
545
559
|
var PAIR_BARRIERS = {};
|
|
546
560
|
var pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
|
|
@@ -652,6 +666,20 @@ function mergeThrough(s, m) {
|
|
|
652
666
|
}
|
|
653
667
|
return { range: m, fixup: false };
|
|
654
668
|
}
|
|
669
|
+
function mergeFixupsOwed(m, mergesBefore) {
|
|
670
|
+
const st = structuralOf(m);
|
|
671
|
+
if (!st) return null;
|
|
672
|
+
const remove = [];
|
|
673
|
+
const add = [];
|
|
674
|
+
for (const q of mergesBefore) {
|
|
675
|
+
const t = mergeThrough(st, q);
|
|
676
|
+
if (!t.fixup) continue;
|
|
677
|
+
remove.push({ startRow: q.startRow, endRow: q.endRow, startColumn: q.startColumn, endColumn: q.endColumn, ...q.rangeType !== void 0 ? { rangeType: q.rangeType } : {} });
|
|
678
|
+
if (t.range) add.push({ ...t.range });
|
|
679
|
+
}
|
|
680
|
+
return remove.length ? { remove, add } : null;
|
|
681
|
+
}
|
|
682
|
+
var isStructuralMutation = (m) => structuralOf(m) !== null;
|
|
655
683
|
function exactMergeRemovals(group, merges) {
|
|
656
684
|
const sim = new Map(Object.entries(merges).map(([k, v]) => [k, v.map((r) => ({ ...r }))]));
|
|
657
685
|
const out = [];
|
|
@@ -698,8 +726,24 @@ function cellsMutation(template, cells) {
|
|
|
698
726
|
function putCell(cells, r, c, v) {
|
|
699
727
|
(cells[r] ??= {})[c] = v;
|
|
700
728
|
}
|
|
729
|
+
function cellsOverlap(a, b) {
|
|
730
|
+
for (const r of Object.keys(a)) {
|
|
731
|
+
const br = b[r];
|
|
732
|
+
if (!br) continue;
|
|
733
|
+
for (const c of Object.keys(a[r] ?? {})) if (c in br) return true;
|
|
734
|
+
}
|
|
735
|
+
return false;
|
|
736
|
+
}
|
|
701
737
|
var isEmptyMatrix = (cells) => Object.values(cells).every((row) => Object.keys(row).length === 0);
|
|
738
|
+
var cellBarrierCache = /* @__PURE__ */ new WeakMap();
|
|
702
739
|
function cellBarrier(m) {
|
|
740
|
+
const hit = cellBarrierCache.get(m);
|
|
741
|
+
if (hit !== void 0) return hit;
|
|
742
|
+
const why = cellBarrierOf(m);
|
|
743
|
+
cellBarrierCache.set(m, why);
|
|
744
|
+
return why;
|
|
745
|
+
}
|
|
746
|
+
function cellBarrierOf(m) {
|
|
703
747
|
if (m.params.isOverrideStyle) return "set-range-values with isOverrideStyle (whole-style replace) is not covered";
|
|
704
748
|
let why = null;
|
|
705
749
|
forEachCell(cellsOf(m), (_r, _c, v) => {
|
|
@@ -781,6 +825,7 @@ function transformCell(X, Y, side) {
|
|
|
781
825
|
function transformCellsOverCells(x, y, side) {
|
|
782
826
|
if (sheetOf(x) !== sheetOf(y)) return ok([x]);
|
|
783
827
|
const ycells = cellsOf(y);
|
|
828
|
+
if (!cellsOverlap(cellsOf(x), ycells)) return ok([x]);
|
|
784
829
|
const xd = isDerived(x);
|
|
785
830
|
const yd = isDerived(y);
|
|
786
831
|
const eff = derivedSide(x, y, side);
|
|
@@ -808,7 +853,7 @@ function transformCellsOverCells(x, y, side) {
|
|
|
808
853
|
const op = passes.filter((p) => !isEmptyMatrix(p)).map((p) => cellsMutation(x, p));
|
|
809
854
|
return ok(op, lost, dropped);
|
|
810
855
|
}
|
|
811
|
-
var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived"];
|
|
856
|
+
var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived", "sheetIndex", "sheetsLeft"];
|
|
812
857
|
var isDerived = (m) => m.params.derived === true;
|
|
813
858
|
function derivedSide(x, y, side) {
|
|
814
859
|
const xd = isDerived(x);
|
|
@@ -830,7 +875,15 @@ function withRefSheets(params, rs) {
|
|
|
830
875
|
return out;
|
|
831
876
|
}
|
|
832
877
|
var hasFormulaText = (v) => !!v && (isFormulaText(v.f) || typeof v.ref === "string");
|
|
878
|
+
var hasFormulaCache = /* @__PURE__ */ new WeakMap();
|
|
833
879
|
function hasFormula(m) {
|
|
880
|
+
const hit = hasFormulaCache.get(m);
|
|
881
|
+
if (hit !== void 0) return hit;
|
|
882
|
+
const any = hasFormulaOf(m);
|
|
883
|
+
hasFormulaCache.set(m, any);
|
|
884
|
+
return any;
|
|
885
|
+
}
|
|
886
|
+
function hasFormulaOf(m) {
|
|
834
887
|
let any = false;
|
|
835
888
|
forEachCell(cellsOf(m), (_r, _c, v) => {
|
|
836
889
|
any ||= hasFormulaText(v);
|
|
@@ -869,15 +922,18 @@ function cellThrough(sheet, change, r, c) {
|
|
|
869
922
|
return nr === null || nc === null ? null : [nr, nc];
|
|
870
923
|
}
|
|
871
924
|
function cellsOverChange(x, change) {
|
|
925
|
+
if (!hasFormula(x) && (change.kind !== "axis" || change.sheet !== sheetOf(x))) return ok([x]);
|
|
872
926
|
const ctx = formulaCtx(x);
|
|
873
927
|
const out = {};
|
|
874
928
|
const dropped = [];
|
|
875
929
|
let why = null;
|
|
930
|
+
let changed = false;
|
|
876
931
|
forEachCell(cellsOf(x), (r, c, v) => {
|
|
877
932
|
if (why) return;
|
|
878
933
|
const at = cellThrough(sheetOf(x), change, r, c);
|
|
879
934
|
if (!at) {
|
|
880
935
|
dropped.push(cellRef(sheetOf(x), r, c));
|
|
936
|
+
changed = true;
|
|
881
937
|
return;
|
|
882
938
|
}
|
|
883
939
|
let e = v;
|
|
@@ -888,14 +944,19 @@ function cellsOverChange(x, change) {
|
|
|
888
944
|
return;
|
|
889
945
|
}
|
|
890
946
|
e = res.entry;
|
|
947
|
+
if (res.changed) changed = true;
|
|
891
948
|
}
|
|
949
|
+
if (at[0] !== r || at[1] !== c) changed = true;
|
|
892
950
|
putCell(out, at[0], at[1], e);
|
|
893
951
|
});
|
|
894
952
|
if (why) return barrier2(why);
|
|
895
|
-
const
|
|
953
|
+
const rsAfter = refSheetsAfter(ctx.refSheets, change);
|
|
954
|
+
if (!changed && JSON.stringify(rsAfter ?? null) === JSON.stringify(ctx.refSheets ?? null)) return ok([x]);
|
|
955
|
+
const params = { ...withRefSheets(x.params, rsAfter), cellValue: out };
|
|
896
956
|
return ok(isEmptyMatrix(out) ? [] : [{ id: x.id, params }], [], dropped);
|
|
897
957
|
}
|
|
898
958
|
function rewritesOf(y, change) {
|
|
959
|
+
if (!hasFormula(y)) return null;
|
|
899
960
|
const ctx = formulaCtx(y);
|
|
900
961
|
const out = {};
|
|
901
962
|
let why = null;
|
|
@@ -1041,6 +1102,368 @@ function buildSize(template, values) {
|
|
|
1041
1102
|
const value = uniform !== void 0 && [...values.values()].every((v) => v === uniform) ? uniform : Object.fromEntries(values);
|
|
1042
1103
|
return { id: template.id, params: { ...p, ranges, [field]: value } };
|
|
1043
1104
|
}
|
|
1105
|
+
var isNumfmt = (m) => m.id === ID.setNumfmt || m.id === ID.removeNumfmt;
|
|
1106
|
+
function nfParts(m) {
|
|
1107
|
+
if (m.id === ID.removeNumfmt) return [{ key: null, ranges: (m.params.ranges ?? []).map((r) => ({ ...r })) }];
|
|
1108
|
+
const values = m.params.values ?? {};
|
|
1109
|
+
return Object.keys(values).map((k) => ({ key: k, ranges: (values[k]?.ranges ?? []).map((r) => ({ ...r })) }));
|
|
1110
|
+
}
|
|
1111
|
+
function nfBuild(m, parts) {
|
|
1112
|
+
const keep = parts.filter((p) => p.ranges.length);
|
|
1113
|
+
if (!keep.length) return [];
|
|
1114
|
+
if (m.id === ID.removeNumfmt) return [{ id: m.id, params: { ...m.params, ranges: keep[0].ranges } }];
|
|
1115
|
+
const values = {};
|
|
1116
|
+
const refMap = {};
|
|
1117
|
+
const srcValues = m.params.values ?? {};
|
|
1118
|
+
const srcRef = m.params.refMap ?? {};
|
|
1119
|
+
for (const p of keep) {
|
|
1120
|
+
values[p.key] = { ...srcValues[p.key], ranges: p.ranges };
|
|
1121
|
+
refMap[p.key] = srcRef[p.key];
|
|
1122
|
+
}
|
|
1123
|
+
return [{ id: m.id, params: { ...m.params, values, refMap } }];
|
|
1124
|
+
}
|
|
1125
|
+
function nfValueAt(m, row, col) {
|
|
1126
|
+
for (const p of nfParts(m))
|
|
1127
|
+
if (p.ranges.some((r) => r.startRow <= row && row <= r.endRow && r.startColumn <= col && col <= r.endColumn)) {
|
|
1128
|
+
if (p.key === null) return null;
|
|
1129
|
+
const ref = (m.params.refMap ?? {})[p.key];
|
|
1130
|
+
return { pattern: ref?.pattern };
|
|
1131
|
+
}
|
|
1132
|
+
return void 0;
|
|
1133
|
+
}
|
|
1134
|
+
function spanThrough(op, lo, hi) {
|
|
1135
|
+
const pieces = [];
|
|
1136
|
+
if (op.kind === "insert") pieces.push([0, op.at - 1, 0], [op.at, Infinity, op.count]);
|
|
1137
|
+
else if (op.kind === "remove") pieces.push([0, op.start - 1, 0], [op.start + op.count, Infinity, -op.count]);
|
|
1138
|
+
else if (op.count <= 0 || op.to >= op.from && op.to <= op.from + op.count) pieces.push([0, Infinity, 0]);
|
|
1139
|
+
else if (op.to > op.from) {
|
|
1140
|
+
const n = op.count;
|
|
1141
|
+
pieces.push([0, op.from - 1, 0], [op.from, op.from + n - 1, op.to - n - op.from], [op.from + n, op.to - 1, -n], [op.to, Infinity, 0]);
|
|
1142
|
+
} else {
|
|
1143
|
+
const n = op.count;
|
|
1144
|
+
pieces.push([0, op.to - 1, 0], [op.to, op.from - 1, n], [op.from, op.from + n - 1, op.to - op.from], [op.from + n, Infinity, 0]);
|
|
1145
|
+
}
|
|
1146
|
+
const out = [];
|
|
1147
|
+
for (const [a, b, d] of pieces) {
|
|
1148
|
+
const x0 = Math.max(a, lo);
|
|
1149
|
+
const x1 = Math.min(b, hi);
|
|
1150
|
+
if (x0 <= x1) out.push([x0 + d, x1 + d]);
|
|
1151
|
+
}
|
|
1152
|
+
out.sort((p, q) => p[0] - q[0]);
|
|
1153
|
+
const merged = [];
|
|
1154
|
+
for (const p of out) {
|
|
1155
|
+
const last = merged[merged.length - 1];
|
|
1156
|
+
if (last && last[1] + 1 >= p[0]) last[1] = Math.max(last[1], p[1]);
|
|
1157
|
+
else merged.push([p[0], p[1]]);
|
|
1158
|
+
}
|
|
1159
|
+
return merged;
|
|
1160
|
+
}
|
|
1161
|
+
function rectsThrough(st, rects) {
|
|
1162
|
+
const [a0, a1] = S(st.axis);
|
|
1163
|
+
const out = [];
|
|
1164
|
+
for (const r of rects) for (const [lo, hi] of spanThrough(st.op, r[a0], r[a1])) out.push({ ...r, [a0]: lo, [a1]: hi });
|
|
1165
|
+
return out;
|
|
1166
|
+
}
|
|
1167
|
+
function rectMinus(a, b) {
|
|
1168
|
+
if (a.startRow > b.endRow || b.startRow > a.endRow || a.startColumn > b.endColumn || b.startColumn > a.endColumn) return [a];
|
|
1169
|
+
const out = [];
|
|
1170
|
+
if (a.startRow < b.startRow) out.push({ ...a, endRow: b.startRow - 1 });
|
|
1171
|
+
if (a.endRow > b.endRow) out.push({ ...a, startRow: b.endRow + 1 });
|
|
1172
|
+
const r0 = Math.max(a.startRow, b.startRow);
|
|
1173
|
+
const r1 = Math.min(a.endRow, b.endRow);
|
|
1174
|
+
if (a.startColumn < b.startColumn) out.push({ ...a, startRow: r0, endRow: r1, endColumn: b.startColumn - 1 });
|
|
1175
|
+
if (a.endColumn > b.endColumn) out.push({ ...a, startRow: r0, endRow: r1, startColumn: b.endColumn + 1 });
|
|
1176
|
+
return out;
|
|
1177
|
+
}
|
|
1178
|
+
function rectsMinus(as, bs) {
|
|
1179
|
+
let cur = as;
|
|
1180
|
+
for (const b of bs) cur = cur.flatMap((a) => rectMinus(a, b));
|
|
1181
|
+
return cur;
|
|
1182
|
+
}
|
|
1183
|
+
function nWrites(m) {
|
|
1184
|
+
const out = [];
|
|
1185
|
+
forEachCell(cellsOf(m), (r, c, v) => {
|
|
1186
|
+
if (v === null || v.s === null || v.s && typeof v.s === "object" && "n" in v.s)
|
|
1187
|
+
out.push({ startRow: r, endRow: r, startColumn: c, endColumn: c });
|
|
1188
|
+
});
|
|
1189
|
+
return out;
|
|
1190
|
+
}
|
|
1191
|
+
function numfmtPair(x, y, side, sx, sy) {
|
|
1192
|
+
const nx = isNumfmt(x);
|
|
1193
|
+
const ny = isNumfmt(y);
|
|
1194
|
+
if (!nx && !ny) return null;
|
|
1195
|
+
if (nx && sy) return ok(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsThrough(sy, p.ranges) }))));
|
|
1196
|
+
if (ny && sx) return ok([x]);
|
|
1197
|
+
if (nx && ny) {
|
|
1198
|
+
if (side === "left") return ok([x]);
|
|
1199
|
+
const ys = nfParts(y).flatMap((p) => p.ranges);
|
|
1200
|
+
return ok(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsMinus(p.ranges, ys) }))));
|
|
1201
|
+
}
|
|
1202
|
+
if (nx && y.id === ID.values) {
|
|
1203
|
+
if (side === "left") return ok([x]);
|
|
1204
|
+
const ys = nWrites(y);
|
|
1205
|
+
return ys.length ? ok(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsMinus(p.ranges, ys) })))) : ok([x]);
|
|
1206
|
+
}
|
|
1207
|
+
if (ny && x.id === ID.values) {
|
|
1208
|
+
if (side === "left") return ok([x]);
|
|
1209
|
+
const out = {};
|
|
1210
|
+
const tail = {};
|
|
1211
|
+
let touched = false;
|
|
1212
|
+
forEachCell(cellsOf(x), (r, c, v) => {
|
|
1213
|
+
const n = nfValueAt(y, r, c);
|
|
1214
|
+
if (n === void 0) return putCell(out, r, c, v);
|
|
1215
|
+
if (v === null || v.s === null) {
|
|
1216
|
+
putCell(out, r, c, v);
|
|
1217
|
+
if (n) {
|
|
1218
|
+
putCell(tail, r, c, { s: { n } });
|
|
1219
|
+
touched = true;
|
|
1220
|
+
}
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
if (v.s && typeof v.s === "object" && "n" in v.s) {
|
|
1224
|
+
const { n: _drop, ...rest } = v.s;
|
|
1225
|
+
const e = { ...v };
|
|
1226
|
+
if (Object.keys(rest).length) e.s = rest;
|
|
1227
|
+
else delete e.s;
|
|
1228
|
+
touched = true;
|
|
1229
|
+
if (Object.keys(e).length) putCell(out, r, c, e);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
putCell(out, r, c, v);
|
|
1233
|
+
});
|
|
1234
|
+
if (!touched) return ok([x]);
|
|
1235
|
+
const ops = [];
|
|
1236
|
+
if (!isEmptyMatrix(out)) ops.push(cellsMutation(x, out));
|
|
1237
|
+
if (!isEmptyMatrix(tail)) ops.push({ id: ID.values, params: { subUnitId: sheetOf(x), cellValue: tail } });
|
|
1238
|
+
return ok(ops);
|
|
1239
|
+
}
|
|
1240
|
+
return ok([x]);
|
|
1241
|
+
}
|
|
1242
|
+
var isCount = (m) => m.id === ID.rowCount || m.id === ID.colCount;
|
|
1243
|
+
var countAxis = (m) => m.id === ID.rowCount ? "row" : "col";
|
|
1244
|
+
var countKey = (m) => m.id === ID.rowCount ? "rowCount" : "columnCount";
|
|
1245
|
+
function countPair(x, y, sy) {
|
|
1246
|
+
if (!isCount(x)) return isCount(y) ? ok([x]) : null;
|
|
1247
|
+
const k = countKey(x);
|
|
1248
|
+
const v = Number(x.params[k]);
|
|
1249
|
+
if (sy) {
|
|
1250
|
+
if (sy.axis !== countAxis(x) || sy.op.kind === "move") return ok([x]);
|
|
1251
|
+
const d = sy.op.kind === "insert" ? sy.op.count : -sy.op.count;
|
|
1252
|
+
return ok([{ id: x.id, params: { ...x.params, [k]: Math.max(1, v + d) } }]);
|
|
1253
|
+
}
|
|
1254
|
+
if (isCount(y) && y.id === x.id) {
|
|
1255
|
+
const w = Number(y.params[k]);
|
|
1256
|
+
return ok([w > v ? { id: x.id, params: { ...x.params, [k]: w } } : x]);
|
|
1257
|
+
}
|
|
1258
|
+
return ok([x]);
|
|
1259
|
+
}
|
|
1260
|
+
var isSheetOp = (m) => m.id === ID.insertSheet || m.id === ID.removeSheet || m.id === ID.sheetOrder;
|
|
1261
|
+
var sheetIdOf = (m) => m.id === ID.insertSheet ? String(m.params.sheet?.id ?? "") : sheetOf(m);
|
|
1262
|
+
function sheetAxisOf(m) {
|
|
1263
|
+
const p = m.params;
|
|
1264
|
+
if (m.id === ID.insertSheet) return typeof p.index === "number" ? { kind: "insert", at: p.index, count: 1 } : null;
|
|
1265
|
+
if (m.id === ID.removeSheet) return typeof p.sheetIndex === "number" ? { kind: "remove", start: p.sheetIndex, count: 1 } : null;
|
|
1266
|
+
if (m.id === ID.sheetOrder) {
|
|
1267
|
+
const f = Number(p.fromOrder);
|
|
1268
|
+
const t = Number(p.toOrder);
|
|
1269
|
+
return { kind: "move", from: f, count: 1, to: t > f ? t + 1 : t };
|
|
1270
|
+
}
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
function buildSheetOp(template, op) {
|
|
1274
|
+
if (op.kind === "insert") return { id: template.id, params: { ...template.params, index: op.at } };
|
|
1275
|
+
if (op.kind === "remove") return { id: template.id, params: { ...template.params, sheetIndex: op.start } };
|
|
1276
|
+
return { id: template.id, params: { ...template.params, fromOrder: op.from, toOrder: op.to > op.from ? op.to - 1 : op.to } };
|
|
1277
|
+
}
|
|
1278
|
+
function namesSheet(m, id) {
|
|
1279
|
+
const rs = refSheetsOf(m);
|
|
1280
|
+
return !!rs && Object.values(rs).includes(id) && (m.id === ID.setName || hasFormula(m));
|
|
1281
|
+
}
|
|
1282
|
+
function sheetPair(x, y, side) {
|
|
1283
|
+
const xs = isSheetOp(x);
|
|
1284
|
+
const ys = isSheetOp(y);
|
|
1285
|
+
if (!xs && !ys) return null;
|
|
1286
|
+
if (xs && ys) {
|
|
1287
|
+
const a = sheetAxisOf(x);
|
|
1288
|
+
const b = sheetAxisOf(y);
|
|
1289
|
+
if (!a || !b) return barrier2("a sheet add or remove without its index (capture must call annotateSheetOps)");
|
|
1290
|
+
if (x.id === ID.insertSheet && y.id === ID.insertSheet && sheetIdOf(x) === sheetIdOf(y)) {
|
|
1291
|
+
return JSON.stringify(x.params) === JSON.stringify(y.params) ? ok([]) : barrier2("the same sheet id added twice with different content");
|
|
1292
|
+
}
|
|
1293
|
+
if (x.id === ID.removeSheet && typeof x.params.sheetsLeft === "number" && sheetIdOf(x) !== sheetIdOf(y) && (y.id === ID.removeSheet || y.id === ID.insertSheet)) {
|
|
1294
|
+
const left = x.params.sheetsLeft + (y.id === ID.insertSheet ? 1 : -1);
|
|
1295
|
+
if (left < 1) return barrier2("concurrent sheet removes would leave the workbook without a sheet");
|
|
1296
|
+
const out2 = transformAxis(a, b, side);
|
|
1297
|
+
if (!out2) return barrier2("sheet order: no transform");
|
|
1298
|
+
return ok(out2.map((o) => buildSheetOp({ id: x.id, params: { ...x.params, sheetsLeft: left } }, o)));
|
|
1299
|
+
}
|
|
1300
|
+
const out = transformAxis(a, b, side);
|
|
1301
|
+
if (!out) return barrier2("sheet order: no transform");
|
|
1302
|
+
return ok(out.map((o) => buildSheetOp(x, o)), [], out.length === 0 && x.id === ID.sheetOrder ? [`${sheetIdOf(x)}: sheet order`] : []);
|
|
1303
|
+
}
|
|
1304
|
+
if (ys) {
|
|
1305
|
+
if (y.id !== ID.removeSheet) return ok([x]);
|
|
1306
|
+
const gone2 = sheetOf(y);
|
|
1307
|
+
if (namesSheet(x, gone2)) return barrier2("a formula names a sheet removed at the same time");
|
|
1308
|
+
if (sheetOf(x) === gone2 && x.id !== ID.setName && x.id !== ID.removeName)
|
|
1309
|
+
return ok([], [], isDerived(x) ? [] : [`${gone2}: the sheet was removed`]);
|
|
1310
|
+
return ok([x]);
|
|
1311
|
+
}
|
|
1312
|
+
if (x.id !== ID.removeSheet) return ok([x]);
|
|
1313
|
+
const gone = sheetOf(x);
|
|
1314
|
+
if (namesSheet(y, gone)) return barrier2("a formula names a sheet removed at the same time");
|
|
1315
|
+
return ok([x], sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : []);
|
|
1316
|
+
}
|
|
1317
|
+
function annotateSheetOps(group, sheetOrder) {
|
|
1318
|
+
const order = [...sheetOrder];
|
|
1319
|
+
return group.map((m) => {
|
|
1320
|
+
if (m.id === ID.insertSheet) {
|
|
1321
|
+
const index = typeof m.params.index === "number" ? Math.min(m.params.index, order.length) : order.length;
|
|
1322
|
+
order.splice(index, 0, sheetIdOf(m));
|
|
1323
|
+
return index === m.params.index ? m : { id: m.id, params: { ...m.params, index } };
|
|
1324
|
+
}
|
|
1325
|
+
if (m.id === ID.removeSheet) {
|
|
1326
|
+
const i = order.indexOf(sheetOf(m));
|
|
1327
|
+
if (i >= 0) order.splice(i, 1);
|
|
1328
|
+
return i >= 0 ? { id: m.id, params: { ...m.params, sheetIndex: i, sheetsLeft: order.length } } : m;
|
|
1329
|
+
}
|
|
1330
|
+
if (m.id === ID.sheetOrder) {
|
|
1331
|
+
const f = Number(m.params.fromOrder);
|
|
1332
|
+
const [id] = order.splice(f, 1);
|
|
1333
|
+
order.splice(Number(m.params.toOrder), 0, id ?? sheetOf(m));
|
|
1334
|
+
}
|
|
1335
|
+
return m;
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
function inlineSheetStyles(sheet, styleOf) {
|
|
1339
|
+
const out = JSON.parse(JSON.stringify(sheet));
|
|
1340
|
+
const fix = (o) => {
|
|
1341
|
+
if (o && typeof o.s === "string") {
|
|
1342
|
+
const st = styleOf(o.s);
|
|
1343
|
+
if (st) o.s = JSON.parse(JSON.stringify(st));
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
for (const row of Object.values(out.cellData ?? {})) for (const cell of Object.values(row ?? {})) fix(cell);
|
|
1347
|
+
for (const key of ["rowData", "columnData"]) for (const v of Object.values(out[key] ?? {})) fix(v);
|
|
1348
|
+
if (typeof out.defaultStyle === "string") {
|
|
1349
|
+
const st = styleOf(out.defaultStyle);
|
|
1350
|
+
if (st) out.defaultStyle = JSON.parse(JSON.stringify(st));
|
|
1351
|
+
}
|
|
1352
|
+
return out;
|
|
1353
|
+
}
|
|
1354
|
+
var isSort = (m) => m.id === ID.sort;
|
|
1355
|
+
function sortOf(m) {
|
|
1356
|
+
const P = /* @__PURE__ */ new Map();
|
|
1357
|
+
for (const [t, src] of Object.entries(m.params.order ?? {})) P.set(Number(t), Number(src));
|
|
1358
|
+
return { range: { ...m.params.range }, P };
|
|
1359
|
+
}
|
|
1360
|
+
var sortQ = (o) => new Map([...o.P].map(([t, s0]) => [s0, t]));
|
|
1361
|
+
function buildSort(template, o) {
|
|
1362
|
+
const order = {};
|
|
1363
|
+
for (const [t, s0] of [...o.P].sort((a, b) => a[0] - b[0])) if (t !== s0) order[t] = s0;
|
|
1364
|
+
if (!Object.keys(order).length || o.range.startRow > o.range.endRow || o.range.startColumn > o.range.endColumn) return [];
|
|
1365
|
+
return [{ id: template.id, params: { ...template.params, range: o.range, order } }];
|
|
1366
|
+
}
|
|
1367
|
+
var inSortCols = (o, c) => c >= o.range.startColumn && c <= o.range.endColumn;
|
|
1368
|
+
function sortThrough(o, st) {
|
|
1369
|
+
const r = o.range;
|
|
1370
|
+
if (st.axis === "col") {
|
|
1371
|
+
if (st.op.kind === "move") {
|
|
1372
|
+
const { from, count, to } = st.op;
|
|
1373
|
+
const touches = !(from + count - 1 < r.startColumn || from > r.endColumn) || to > r.startColumn && to <= r.endColumn;
|
|
1374
|
+
if (touches) return null;
|
|
1375
|
+
const spans3 = spanThrough(st.op, r.startColumn, r.endColumn);
|
|
1376
|
+
return spans3.length === 1 ? { range: { ...r, startColumn: spans3[0][0], endColumn: spans3[0][1] }, P: o.P } : null;
|
|
1377
|
+
}
|
|
1378
|
+
const spans2 = spanThrough(st.op, r.startColumn, r.endColumn);
|
|
1379
|
+
if (!spans2.length) return { range: { ...r, startColumn: 1, endColumn: 0 }, P: /* @__PURE__ */ new Map() };
|
|
1380
|
+
return { range: { ...r, startColumn: spans2[0][0], endColumn: spans2[spans2.length - 1][1] }, P: o.P };
|
|
1381
|
+
}
|
|
1382
|
+
const op = st.op;
|
|
1383
|
+
if (op.kind === "move") {
|
|
1384
|
+
const touches = !(op.from + op.count - 1 < r.startRow || op.from > r.endRow) || op.to > r.startRow && op.to <= r.endRow;
|
|
1385
|
+
if (touches) return null;
|
|
1386
|
+
}
|
|
1387
|
+
if (op.kind === "insert" || op.kind === "move") {
|
|
1388
|
+
const P2 = /* @__PURE__ */ new Map();
|
|
1389
|
+
for (const [t, s0] of o.P) P2.set(mapIndex(op, t), mapIndex(op, s0));
|
|
1390
|
+
const spans2 = spanThrough(op, r.startRow, r.endRow);
|
|
1391
|
+
return { range: { ...r, startRow: spans2[0][0], endRow: spans2[spans2.length - 1][1] }, P: P2 };
|
|
1392
|
+
}
|
|
1393
|
+
const removed = (i) => i >= op.start && i < op.start + op.count;
|
|
1394
|
+
const Q = sortQ(o);
|
|
1395
|
+
const goneTargets = [];
|
|
1396
|
+
for (let i = op.start; i < op.start + op.count; i++) goneTargets.push(Q.get(i) ?? i);
|
|
1397
|
+
goneTargets.sort((a, b) => a - b);
|
|
1398
|
+
const compact = (t) => t - goneTargets.filter((g) => g < t).length;
|
|
1399
|
+
const P = /* @__PURE__ */ new Map();
|
|
1400
|
+
for (const [t, s0] of o.P) if (!removed(s0)) P.set(compact(t), mapIndex(op, s0));
|
|
1401
|
+
const spans = spanThrough(op, r.startRow, r.endRow);
|
|
1402
|
+
if (!spans.length) return { range: { ...r, startRow: 1, endRow: 0 }, P: /* @__PURE__ */ new Map() };
|
|
1403
|
+
return { range: { ...r, startRow: spans[0][0], endRow: spans[spans.length - 1][1] }, P };
|
|
1404
|
+
}
|
|
1405
|
+
function sortPair(x, y, sx, sy) {
|
|
1406
|
+
const xs = isSort(x);
|
|
1407
|
+
const ys = isSort(y);
|
|
1408
|
+
if (!xs && !ys) return null;
|
|
1409
|
+
if (xs && ys) {
|
|
1410
|
+
const a = sortOf(x).range;
|
|
1411
|
+
const b = sortOf(y).range;
|
|
1412
|
+
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") : ok([x]);
|
|
1414
|
+
}
|
|
1415
|
+
if (xs) {
|
|
1416
|
+
if (sy) {
|
|
1417
|
+
const t = sortThrough(sortOf(x), sy);
|
|
1418
|
+
return t ? ok(buildSort(x, t)) : barrier2("a row or column move through a sorted range");
|
|
1419
|
+
}
|
|
1420
|
+
return ok([x]);
|
|
1421
|
+
}
|
|
1422
|
+
const o = sortOf(y);
|
|
1423
|
+
const Q = sortQ(o);
|
|
1424
|
+
const moved = (r) => r >= o.range.startRow && r <= o.range.endRow && Q.has(r);
|
|
1425
|
+
if (sx) {
|
|
1426
|
+
if (sx.axis === "col") return sx.op.kind === "move" && !sortThrough(o, sx) ? barrier2("a column move through a sorted range") : ok([x]);
|
|
1427
|
+
if (sx.op.kind === "insert") return ok([x]);
|
|
1428
|
+
if (sx.op.kind === "move") return sortThrough(o, sx) ? ok([x]) : barrier2("a row move through a sorted range");
|
|
1429
|
+
const rows = [];
|
|
1430
|
+
let partial = false;
|
|
1431
|
+
for (let i = sx.op.start; i < sx.op.start + sx.op.count; i++) {
|
|
1432
|
+
if (moved(i)) partial = true;
|
|
1433
|
+
rows.push(Q.get(i) ?? i);
|
|
1434
|
+
}
|
|
1435
|
+
if (partial) return barrier2("a row remove against a concurrent sort of those rows");
|
|
1436
|
+
return ok([x]);
|
|
1437
|
+
}
|
|
1438
|
+
if (x.id === ID.values) {
|
|
1439
|
+
const out = {};
|
|
1440
|
+
let changed = false;
|
|
1441
|
+
forEachCell(cellsOf(x), (r, c, v) => {
|
|
1442
|
+
if (inSortCols(o, c) && moved(r)) {
|
|
1443
|
+
putCell(out, Q.get(r), c, v);
|
|
1444
|
+
changed = true;
|
|
1445
|
+
} else putCell(out, r, c, v);
|
|
1446
|
+
});
|
|
1447
|
+
return changed ? ok([cellsMutation(x, out)]) : ok([x]);
|
|
1448
|
+
}
|
|
1449
|
+
if (isNumfmt(x)) {
|
|
1450
|
+
const parts = nfParts(x).map((p) => {
|
|
1451
|
+
const ranges = [];
|
|
1452
|
+
for (const rc of p.ranges) {
|
|
1453
|
+
const inner = { ...rc, startRow: Math.max(rc.startRow, o.range.startRow), endRow: Math.min(rc.endRow, o.range.endRow), startColumn: Math.max(rc.startColumn, o.range.startColumn), endColumn: Math.min(rc.endColumn, o.range.endColumn) };
|
|
1454
|
+
if (inner.startRow > inner.endRow || inner.startColumn > inner.endColumn) {
|
|
1455
|
+
ranges.push(rc);
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
ranges.push(...rectMinus(rc, inner));
|
|
1459
|
+
for (let r = inner.startRow; r <= inner.endRow; r++) ranges.push({ ...inner, startRow: Q.get(r) ?? r, endRow: Q.get(r) ?? r });
|
|
1460
|
+
}
|
|
1461
|
+
return { ...p, ranges };
|
|
1462
|
+
});
|
|
1463
|
+
return ok(nfBuild(x, parts));
|
|
1464
|
+
}
|
|
1465
|
+
return ok([x]);
|
|
1466
|
+
}
|
|
1044
1467
|
function familyOf(id) {
|
|
1045
1468
|
const e = SHEET_MUTATIONS[id];
|
|
1046
1469
|
return e && e.kind === "covered" ? e.family : "";
|
|
@@ -1057,6 +1480,8 @@ function transformMutation(x, y, side) {
|
|
|
1057
1480
|
}
|
|
1058
1481
|
const pb = PAIR_BARRIERS[pairKey(x.id, y.id)];
|
|
1059
1482
|
if (pb) return barrier2(`${x.id} \xD7 ${y.id}: ${pb.reason}${pb.seed === null ? "" : ` (fuzz seed ${pb.seed})`}`);
|
|
1483
|
+
const sp = sheetPair(x, y, side);
|
|
1484
|
+
if (sp) return sp;
|
|
1060
1485
|
const fp = formulaPair(x, y, side);
|
|
1061
1486
|
if (fp) return fp;
|
|
1062
1487
|
if (sheetOf(x) !== sheetOf(y)) return ok([x]);
|
|
@@ -1064,6 +1489,12 @@ function transformMutation(x, y, side) {
|
|
|
1064
1489
|
const fy = familyOf(y.id);
|
|
1065
1490
|
const sy = structuralOf(y);
|
|
1066
1491
|
const sx = structuralOf(x);
|
|
1492
|
+
const sortp = sortPair(x, y, sx, sy);
|
|
1493
|
+
if (sortp) return sortp;
|
|
1494
|
+
const cp = countPair(x, y, sy);
|
|
1495
|
+
if (cp) return cp;
|
|
1496
|
+
const np = numfmtPair(x, y, side, sx, sy);
|
|
1497
|
+
if (np) return np;
|
|
1067
1498
|
if (sx && sy) {
|
|
1068
1499
|
if (sx.axis !== sy.axis) return ok([x]);
|
|
1069
1500
|
const out = transformAxis(sx.op, sy.op, side);
|
|
@@ -1171,22 +1602,45 @@ function tSeq(xs, ys, side, acc) {
|
|
|
1171
1602
|
acc.dropped.push(...a.dropped);
|
|
1172
1603
|
return [a.op, b.op];
|
|
1173
1604
|
}
|
|
1605
|
+
const outX = [];
|
|
1606
|
+
let curY = ys;
|
|
1607
|
+
for (const x of xs) {
|
|
1608
|
+
let curX = [x];
|
|
1609
|
+
const nextY = [];
|
|
1610
|
+
for (const y of curY) {
|
|
1611
|
+
if (curX.length === 0) {
|
|
1612
|
+
nextY.push(y);
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
const r = tSeq(curX, [y], side, acc);
|
|
1616
|
+
if (blocked(r)) return r;
|
|
1617
|
+
curX = r[0];
|
|
1618
|
+
for (const m of r[1]) nextY.push(m);
|
|
1619
|
+
}
|
|
1620
|
+
for (const m of curX) outX.push(m);
|
|
1621
|
+
curY = nextY;
|
|
1622
|
+
}
|
|
1623
|
+
return [outX, curY];
|
|
1624
|
+
}
|
|
1625
|
+
function tSeqOld(xs, ys, side, acc) {
|
|
1626
|
+
if (xs.length === 0 || ys.length === 0) return [xs, ys];
|
|
1627
|
+
if (xs.length === 1 && ys.length === 1) return tSeq(xs, ys, side, acc);
|
|
1174
1628
|
if (xs.length > 1) {
|
|
1175
|
-
const first2 =
|
|
1629
|
+
const first2 = tSeqOld([xs[0]], ys, side, acc);
|
|
1176
1630
|
if (blocked(first2)) return first2;
|
|
1177
|
-
const rest2 =
|
|
1631
|
+
const rest2 = tSeqOld(xs.slice(1), first2[1], side, acc);
|
|
1178
1632
|
if (blocked(rest2)) return rest2;
|
|
1179
1633
|
return [[...first2[0], ...rest2[0]], rest2[1]];
|
|
1180
1634
|
}
|
|
1181
|
-
const first =
|
|
1635
|
+
const first = tSeqOld(xs, [ys[0]], side, acc);
|
|
1182
1636
|
if (blocked(first)) return first;
|
|
1183
|
-
const rest =
|
|
1637
|
+
const rest = tSeqOld(first[0], ys.slice(1), side, acc);
|
|
1184
1638
|
if (blocked(rest)) return rest;
|
|
1185
1639
|
return [rest[0], [...first[1], ...rest[1]]];
|
|
1186
1640
|
}
|
|
1187
1641
|
function transformGroup(x, y, side) {
|
|
1188
1642
|
const acc = { lost: [], dropped: [] };
|
|
1189
|
-
const r = tSeq([...x], [...y], side, acc);
|
|
1643
|
+
const r = (process.env.OLD_TSEQ ? tSeqOld : tSeq)([...x], [...y], side, acc);
|
|
1190
1644
|
if (blocked(r)) return r;
|
|
1191
1645
|
return { op: r[0], lost: [...new Set(acc.lost)], dropped: [...new Set(acc.dropped)] };
|
|
1192
1646
|
}
|