@createiq/htmldiff 1.2.0-beta.8 → 1.2.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/HtmlDiff.cjs +45 -11
- package/dist/HtmlDiff.cjs.map +1 -1
- package/dist/HtmlDiff.mjs +45 -11
- package/dist/HtmlDiff.mjs.map +1 -1
- package/package.json +1 -1
- package/src/ThreeWayDiff.ts +53 -11
- package/test/HtmlDiff.threeWay.spec.ts +25 -2
package/dist/HtmlDiff.mjs
CHANGED
|
@@ -1333,13 +1333,31 @@ function collectInsertionsKeyedByEnd(d) {
|
|
|
1333
1333
|
return out;
|
|
1334
1334
|
}
|
|
1335
1335
|
/**
|
|
1336
|
-
* Emit any insertions at boundary `b`.
|
|
1337
|
-
* the same boundary AND the inserted token sequences are textually
|
|
1338
|
-
* identical, the insertion is treated as agreed and emitted unmarked.
|
|
1339
|
-
* Otherwise each side's insertion is emitted with author attribution.
|
|
1336
|
+
* Emit any insertions at boundary `b`. Three cases:
|
|
1340
1337
|
*
|
|
1341
|
-
*
|
|
1342
|
-
*
|
|
1338
|
+
* 1. One side inserted, the other didn't → emit that side's tokens
|
|
1339
|
+
* with author attribution.
|
|
1340
|
+
* 2. Both sides inserted the EXACT same sequence → settled, emit
|
|
1341
|
+
* unmarked.
|
|
1342
|
+
* 3. Both sides inserted overlapping but different sequences (the
|
|
1343
|
+
* common case: one author accepted the other's insertion and
|
|
1344
|
+
* edited it, so e.g. cp's "X Y Z" overlaps me's "X Y a Z" with
|
|
1345
|
+
* "a" being a one-author-only addition). Run an LCS sub-diff
|
|
1346
|
+
* between the two insertion sequences and emit:
|
|
1347
|
+
* - tokens in BOTH → settled (equal segment)
|
|
1348
|
+
* - tokens only in cp → ins-cp
|
|
1349
|
+
* - tokens only in me → ins-me
|
|
1350
|
+
* The order of emission preserves the natural reading flow of
|
|
1351
|
+
* the merged insertion — common tokens read where they appear,
|
|
1352
|
+
* with author-only deltas inserted in their LCS-determined
|
|
1353
|
+
* positions.
|
|
1354
|
+
*
|
|
1355
|
+
* Without this sub-alignment, real-world flows like "Me added 'add
|
|
1356
|
+
* more things here', CP accepted minus 'things'" would render as two
|
|
1357
|
+
* full redundant insertions (`<ins cp>add more here</ins><ins me>add
|
|
1358
|
+
* more things here</ins>`) rather than the obvious single shared
|
|
1359
|
+
* insertion with a me-only "things" word — confusing to read and a
|
|
1360
|
+
* regression vs Word's track-changes UX.
|
|
1343
1361
|
*/
|
|
1344
1362
|
function emitBoundary(b, cpInsAt, meInsAt, _cpDiffWords, _meDiffWords, segments) {
|
|
1345
1363
|
const cpIns = cpInsAt.get(b);
|
|
@@ -1347,18 +1365,34 @@ function emitBoundary(b, cpInsAt, meInsAt, _cpDiffWords, _meDiffWords, segments)
|
|
|
1347
1365
|
const hasCp = !!cpIns && cpIns.length > 0;
|
|
1348
1366
|
const hasMe = !!meIns && meIns.length > 0;
|
|
1349
1367
|
if (!hasCp && !hasMe) return;
|
|
1350
|
-
if (hasCp
|
|
1368
|
+
if (!hasCp) {
|
|
1369
|
+
appendSegment(segments, {
|
|
1370
|
+
kind: "ins",
|
|
1371
|
+
author: "me"
|
|
1372
|
+
}, meIns);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
if (!hasMe) {
|
|
1376
|
+
appendSegment(segments, {
|
|
1377
|
+
kind: "ins",
|
|
1378
|
+
author: "cp"
|
|
1379
|
+
}, cpIns);
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
if (tokenArraysEqual(cpIns, meIns)) {
|
|
1351
1383
|
appendSegment(segments, { kind: "equal" }, cpIns);
|
|
1352
1384
|
return;
|
|
1353
1385
|
}
|
|
1354
|
-
|
|
1386
|
+
const alignment = lcsAlign(cpIns, meIns);
|
|
1387
|
+
for (const a of alignment) if (a.oldIdx !== null && a.newIdx !== null) appendSegment(segments, { kind: "equal" }, [cpIns[a.oldIdx]]);
|
|
1388
|
+
else if (a.oldIdx !== null) appendSegment(segments, {
|
|
1355
1389
|
kind: "ins",
|
|
1356
1390
|
author: "cp"
|
|
1357
|
-
}, cpIns);
|
|
1358
|
-
if (
|
|
1391
|
+
}, [cpIns[a.oldIdx]]);
|
|
1392
|
+
else if (a.newIdx !== null) appendSegment(segments, {
|
|
1359
1393
|
kind: "ins",
|
|
1360
1394
|
author: "me"
|
|
1361
|
-
}, meIns);
|
|
1395
|
+
}, [meIns[a.newIdx]]);
|
|
1362
1396
|
}
|
|
1363
1397
|
function tokenArraysEqual(a, b) {
|
|
1364
1398
|
if (a.length !== b.length) return false;
|