|
51 | 51 | this.classes.classLocation = classLocation
|
52 | 52 |
|
53 | 53 | 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); |
55 | 55 | this.diffOutOfDate = this.dealigned = false;
|
56 | 56 | this.needsScrollSync = null
|
57 | 57 |
|
|
78 | 78 | function ensureDiff(dv) {
|
79 | 79 | if (dv.diffOutOfDate) {
|
80 | 80 | 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); |
82 | 82 | dv.diffOutOfDate = false;
|
83 | 83 | CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
|
84 | 84 | }
|
|
101 | 101 | }
|
102 | 102 | ensureDiff(dv);
|
103 | 103 | 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); |
106 | 106 | }
|
107 | 107 |
|
108 | 108 | if (dv.mv.options.connect == "align")
|
|
242 | 242 | }
|
243 | 243 |
|
244 | 244 | // 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) { |
246 | 246 | var vp = editor.getViewport();
|
247 | 247 | editor.operation(function() {
|
248 | 248 | if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
|
249 | 249 | 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); |
251 | 251 | state.from = vp.from; state.to = vp.to;
|
252 | 252 | } else {
|
253 | 253 | 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); |
255 | 255 | state.from = vp.from;
|
256 | 256 | }
|
257 | 257 | 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); |
259 | 259 | state.to = vp.to;
|
260 | 260 | }
|
261 | 261 | }
|
|
272 | 272 | return line;
|
273 | 273 | }
|
274 | 274 |
|
275 |
| - function markChanges(editor, diff, type, marks, from, to, classes) { |
| 275 | + function markChanges(editor, diff, type, marks, from, to, classes, chunks, chunkPerLine) { |
276 | 276 | var pos = Pos(0, 0);
|
277 | 277 | var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));
|
278 | 278 | var cls = type == DIFF_DELETE ? classes.del : classes.insert;
|
|
289 | 289 | }
|
290 | 290 | }
|
291 | 291 |
|
| 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 | + |
292 | 300 | var chunkStart = 0, pending = false;
|
293 | 301 | for (var i = 0; i < diff.length; ++i) {
|
294 | 302 | var part = diff[i], tp = part[0], str = part[1];
|
|
297 | 305 | moveOver(pos, str);
|
298 | 306 | var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);
|
299 | 307 | if (cleanTo > cleanFrom) {
|
300 |
| - if (pending) { markChunk(chunkStart, cleanFrom); pending = false } |
| 308 | + if (pending) { |
| 309 | + if (!chunkPerLine) markChunk(chunkStart, cleanFrom); |
| 310 | + pending = false; |
| 311 | + } |
301 | 312 | chunkStart = cleanTo;
|
302 | 313 | }
|
303 | 314 | } else {
|
|
424 | 435 | if (other)
|
425 | 436 | mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2)
|
426 | 437 |
|
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 | + }) |
428 | 444 | }
|
429 | 445 |
|
430 | 446 | function alignChunks(dv, force) {
|
|
472 | 488 | }
|
473 | 489 | for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
|
474 | 490 | var diff = maxOffset - offset[i];
|
475 |
| - if (diff > 1) |
| 491 | + if (diff > 1 && i === 0) { |
476 | 492 | aligners.push(padAbove(cm[i], lines[i], diff));
|
| 493 | + } else if (diff > 1) { |
| 494 | + aligners.push(padBelow(cm[i], lines[i] - 1, diff)); |
| 495 | + } |
477 | 496 | }
|
478 | 497 | }
|
479 | 498 |
|
|
489 | 508 | return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true});
|
490 | 509 | }
|
491 | 510 |
|
| 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 | + |
492 | 518 | function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) {
|
493 | 519 | var flip = dv.type == "left";
|
494 | 520 | var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig;
|
|
679 | 705 | return diff;
|
680 | 706 | }
|
681 | 707 |
|
682 |
| - function getChunks(diff) { |
| 708 | + function getChunks(diff, chunkPerLine) { |
683 | 709 | var chunks = [];
|
684 | 710 | if (!diff.length) return chunks;
|
685 | 711 | var startEdit = 0, startOrig = 0;
|
|
704 | 730 | if (startEdit <= edit.line || startOrig <= orig.line)
|
705 | 731 | chunks.push({origFrom: startOrig, origTo: orig.line + 1,
|
706 | 732 | 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; |
708 | 757 | }
|
709 | 758 |
|
710 | 759 | function endOfLineClean(diff, i) {
|
|
0 commit comments