Skip to content

Commit 4e46423

Browse files
committed
feat: line-based chunks
1 parent 3277be9 commit 4e46423

File tree

1 file changed

+63
-14
lines changed

1 file changed

+63
-14
lines changed

addon/merge/merge.js

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
this.classes.classLocation = classLocation
5252

5353
this.diff = getDiff(asString(orig), asString(options.value), this.mv.options.ignoreWhitespace);
54-
this.chunks = getChunks(this.diff);
54+
this.chunks = getChunks(this.diff, this.mv.options.chunkPerLine);
5555
this.diffOutOfDate = this.dealigned = false;
5656
this.needsScrollSync = null
5757

@@ -78,7 +78,7 @@
7878
function ensureDiff(dv) {
7979
if (dv.diffOutOfDate) {
8080
dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue(), dv.mv.options.ignoreWhitespace);
81-
dv.chunks = getChunks(dv.diff);
81+
dv.chunks = getChunks(dv.diff, dv.mv.options.chunkPerLine);
8282
dv.diffOutOfDate = false;
8383
CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
8484
}
@@ -101,8 +101,8 @@
101101
}
102102
ensureDiff(dv);
103103
if (dv.showDifferences) {
104-
updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
105-
updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
104+
updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes, dv.chunks, dv.mv.options.chunkPerLine);
105+
updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes, dv.chunks, dv.mv.options.chunkPerLine);
106106
}
107107

108108
if (dv.mv.options.connect == "align")
@@ -242,20 +242,20 @@
242242
}
243243

244244
// FIXME maybe add a margin around viewport to prevent too many updates
245-
function updateMarks(editor, diff, state, type, classes) {
245+
function updateMarks(editor, diff, state, type, classes, chunks, chunkPerLine) {
246246
var vp = editor.getViewport();
247247
editor.operation(function() {
248248
if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
249249
clearMarks(editor, state.marked, classes);
250-
markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes);
250+
markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes, chunks, chunkPerLine);
251251
state.from = vp.from; state.to = vp.to;
252252
} else {
253253
if (vp.from < state.from) {
254-
markChanges(editor, diff, type, state.marked, vp.from, state.from, classes);
254+
markChanges(editor, diff, type, state.marked, vp.from, state.from, classes, chunks, chunkPerLine);
255255
state.from = vp.from;
256256
}
257257
if (vp.to > state.to) {
258-
markChanges(editor, diff, type, state.marked, state.to, vp.to, classes);
258+
markChanges(editor, diff, type, state.marked, state.to, vp.to, classes, chunks, chunkPerLine);
259259
state.to = vp.to;
260260
}
261261
}
@@ -272,7 +272,7 @@
272272
return line;
273273
}
274274

275-
function markChanges(editor, diff, type, marks, from, to, classes) {
275+
function markChanges(editor, diff, type, marks, from, to, classes, chunks, chunkPerLine) {
276276
var pos = Pos(0, 0);
277277
var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));
278278
var cls = type == DIFF_DELETE ? classes.del : classes.insert;
@@ -289,6 +289,14 @@
289289
}
290290
}
291291

292+
if (chunkPerLine) {
293+
for (var i = 0; i < chunks.length; i++) {
294+
const chunk = chunks[i];
295+
if (type === DIFF_DELETE) markChunk(chunk.origFrom, chunk.origTo);
296+
else markChunk(chunk.editFrom, chunk.editTo);
297+
}
298+
}
299+
292300
var chunkStart = 0, pending = false;
293301
for (var i = 0; i < diff.length; ++i) {
294302
var part = diff[i], tp = part[0], str = part[1];
@@ -297,7 +305,10 @@
297305
moveOver(pos, str);
298306
var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);
299307
if (cleanTo > cleanFrom) {
300-
if (pending) { markChunk(chunkStart, cleanFrom); pending = false }
308+
if (pending) {
309+
if (!chunkPerLine) markChunk(chunkStart, cleanFrom);
310+
pending = false;
311+
}
301312
chunkStart = cleanTo;
302313
}
303314
} else {
@@ -424,7 +435,12 @@
424435
if (other)
425436
mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2)
426437

427-
return result
438+
if (!dv.mv.options.chunkPerLine) return result
439+
return result.map(function (r) {
440+
if (r[1] !== null) return r
441+
r[1] = r[0]
442+
return r
443+
})
428444
}
429445

430446
function alignChunks(dv, force) {
@@ -472,8 +488,11 @@
472488
}
473489
for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
474490
var diff = maxOffset - offset[i];
475-
if (diff > 1)
491+
if (diff > 1 && i === 0) {
476492
aligners.push(padAbove(cm[i], lines[i], diff));
493+
} else if (diff > 1) {
494+
aligners.push(padBelow(cm[i], lines[i] - 1, diff));
495+
}
477496
}
478497
}
479498

@@ -489,6 +508,13 @@
489508
return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true});
490509
}
491510

511+
function padBelow(cm, line, size) {
512+
var elt = document.createElement("div");
513+
elt.className = "CodeMirror-merge-spacer";
514+
elt.style.height = size + "px"; elt.style.minWidth = "1px";
515+
return cm.addLineWidget(line, elt, {height: size, above: false, mergeSpacer: true, handleMouseEvents: true});
516+
}
517+
492518
function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) {
493519
var flip = dv.type == "left";
494520
var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig;
@@ -679,7 +705,7 @@
679705
return diff;
680706
}
681707

682-
function getChunks(diff) {
708+
function getChunks(diff, chunkPerLine) {
683709
var chunks = [];
684710
if (!diff.length) return chunks;
685711
var startEdit = 0, startOrig = 0;
@@ -704,7 +730,30 @@
704730
if (startEdit <= edit.line || startOrig <= orig.line)
705731
chunks.push({origFrom: startOrig, origTo: orig.line + 1,
706732
editFrom: startEdit, editTo: edit.line + 1});
707-
return chunks;
733+
if (chunkPerLine) return separateChunks(chunks);
734+
return chunks
735+
}
736+
737+
// separate chunks if original and edited text are
738+
function separateChunks(chunks) {
739+
const newChunks = [];
740+
for (var i = 0; i < chunks.length; i++) {
741+
const origDiff = chunks[i].origTo - chunks[i].origFrom;
742+
const editDiff = chunks[i].editTo - chunks[i].editFrom;
743+
if (origDiff !== editDiff) {
744+
newChunks.push(chunks[i]);
745+
} else {
746+
for (var j = 0; j < origDiff; j++) {
747+
newChunks.push({
748+
origFrom: chunks[i].origFrom + j,
749+
origTo: chunks[i].origFrom + j + 1,
750+
editFrom: chunks[i].editFrom + j,
751+
editTo: chunks[i].editFrom + j + 1,
752+
});
753+
}
754+
}
755+
}
756+
return newChunks;
708757
}
709758

710759
function endOfLineClean(diff, i) {

0 commit comments

Comments
 (0)