codemirror-rails 4.12 → 4.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/lib/codemirror/rails/version.rb +2 -2
  3. data/vendor/assets/javascripts/codemirror.js +45 -22
  4. data/vendor/assets/javascripts/codemirror/addons/edit/closebrackets.js +6 -4
  5. data/vendor/assets/javascripts/codemirror/addons/edit/closetag.js +1 -1
  6. data/vendor/assets/javascripts/codemirror/addons/fold/foldgutter.js +12 -4
  7. data/vendor/assets/javascripts/codemirror/addons/hint/show-hint.js +14 -9
  8. data/vendor/assets/javascripts/codemirror/addons/hint/sql-hint.js +94 -51
  9. data/vendor/assets/javascripts/codemirror/addons/lint/lint.js +2 -1
  10. data/vendor/assets/javascripts/codemirror/addons/merge/merge.js +211 -123
  11. data/vendor/assets/javascripts/codemirror/addons/scroll/annotatescrollbar.js +36 -12
  12. data/vendor/assets/javascripts/codemirror/addons/search/matchesonscrollbar.js +9 -4
  13. data/vendor/assets/javascripts/codemirror/addons/selection/selection-pointer.js +3 -0
  14. data/vendor/assets/javascripts/codemirror/addons/tern/tern.js +31 -4
  15. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +46 -7
  16. data/vendor/assets/javascripts/codemirror/modes/clike.js +5 -1
  17. data/vendor/assets/javascripts/codemirror/modes/css.js +104 -55
  18. data/vendor/assets/javascripts/codemirror/modes/cypher.js +1 -1
  19. data/vendor/assets/javascripts/codemirror/modes/forth.js +180 -0
  20. data/vendor/assets/javascripts/codemirror/modes/go.js +1 -0
  21. data/vendor/assets/javascripts/codemirror/modes/idl.js +1 -1
  22. data/vendor/assets/javascripts/codemirror/modes/javascript.js +1 -1
  23. data/vendor/assets/javascripts/codemirror/modes/sql.js +1 -1
  24. data/vendor/assets/javascripts/codemirror/modes/stylus.js +444 -0
  25. data/vendor/assets/javascripts/codemirror/modes/verilog.js +192 -19
  26. data/vendor/assets/stylesheets/codemirror/themes/colorforth.css +33 -0
  27. metadata +4 -1
@@ -46,6 +46,7 @@
46
46
  }
47
47
  var poll = setInterval(function() {
48
48
  if (tooltip) for (var n = node;; n = n.parentNode) {
49
+ if (n && n.nodeType == 11) n = n.host;
49
50
  if (n == document.body) return;
50
51
  if (!n) { hide(); break; }
51
52
  }
@@ -119,7 +120,7 @@
119
120
  function startLinting(cm) {
120
121
  var state = cm.state.lint, options = state.options;
121
122
  var passOptions = options.options || options; // Support deprecated passing of `options` property in options
122
- if (options.async)
123
+ if (options.async || options.getAnnotations.async)
123
124
  options.getAnnotations(cm.getValue(), updateLinting, passOptions, cm);
124
125
  else
125
126
  updateLinting(cm, options.getAnnotations(cm.getValue(), passOptions, cm));
@@ -31,8 +31,6 @@
31
31
  insert: "CodeMirror-merge-r-inserted",
32
32
  del: "CodeMirror-merge-r-deleted",
33
33
  connect: "CodeMirror-merge-r-connect"};
34
- if (mv.options.connect == "align")
35
- this.aligners = [];
36
34
  }
37
35
 
38
36
  DiffView.prototype = {
@@ -42,7 +40,8 @@
42
40
  this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options)));
43
41
 
44
42
  this.diff = getDiff(asString(orig), asString(options.value));
45
- this.diffOutOfDate = false;
43
+ this.chunks = getChunks(this.diff);
44
+ this.diffOutOfDate = this.dealigned = false;
46
45
 
47
46
  this.showDifferences = options.showDifferences !== false;
48
47
  this.forceUpdate = registerUpdate(this);
@@ -61,16 +60,20 @@
61
60
  function ensureDiff(dv) {
62
61
  if (dv.diffOutOfDate) {
63
62
  dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue());
63
+ dv.chunks = getChunks(dv.diff);
64
64
  dv.diffOutOfDate = false;
65
65
  CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
66
66
  }
67
67
  }
68
68
 
69
+ var updating = false;
69
70
  function registerUpdate(dv) {
70
71
  var edit = {from: 0, to: 0, marked: []};
71
72
  var orig = {from: 0, to: 0, marked: []};
72
- var debounceChange;
73
+ var debounceChange, updatingFast = false;
73
74
  function update(mode) {
75
+ updating = true;
76
+ updatingFast = false;
74
77
  if (mode == "full") {
75
78
  if (dv.svg) clear(dv.svg);
76
79
  if (dv.copyButtons) clear(dv.copyButtons);
@@ -84,26 +87,38 @@
84
87
  updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
85
88
  }
86
89
  makeConnections(dv);
90
+
91
+ if (dv.mv.options.connect == "align")
92
+ alignChunks(dv);
93
+ updating = false;
87
94
  }
88
- function set(slow) {
95
+ function setDealign(fast) {
96
+ if (updating) return;
97
+ dv.dealigned = true;
98
+ set(fast);
99
+ }
100
+ function set(fast) {
101
+ if (updating || updatingFast) return;
89
102
  clearTimeout(debounceChange);
90
- debounceChange = setTimeout(update, slow == true ? 250 : 100);
103
+ if (fast === true) updatingFast = true;
104
+ debounceChange = setTimeout(update, fast === true ? 20 : 250);
91
105
  }
92
- function change() {
106
+ function change(_cm, change) {
93
107
  if (!dv.diffOutOfDate) {
94
108
  dv.diffOutOfDate = true;
95
109
  edit.from = edit.to = orig.from = orig.to = 0;
96
110
  }
97
- set(true);
111
+ // Update faster when a line was added/removed
112
+ setDealign(change.text.length - 1 != change.to.line - change.from.line);
98
113
  }
99
114
  dv.edit.on("change", change);
100
115
  dv.orig.on("change", change);
101
- dv.edit.on("markerAdded", set);
102
- dv.edit.on("markerCleared", set);
103
- dv.orig.on("markerAdded", set);
104
- dv.orig.on("markerCleared", set);
105
- dv.edit.on("viewportChange", set);
106
- dv.orig.on("viewportChange", set);
116
+ dv.edit.on("markerAdded", setDealign);
117
+ dv.edit.on("markerCleared", setDealign);
118
+ dv.orig.on("markerAdded", setDealign);
119
+ dv.orig.on("markerCleared", setDealign);
120
+ dv.edit.on("viewportChange", function() { set(false); });
121
+ dv.orig.on("viewportChange", function() { set(false); });
107
122
  update();
108
123
  return update;
109
124
  }
@@ -134,7 +149,7 @@
134
149
  } else {
135
150
  var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
136
151
  var mid = editor.lineAtHeight(midY, "local");
137
- var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
152
+ var around = chunkBoundariesAround(dv.chunks, mid, type == DIFF_INSERT);
138
153
  var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
139
154
  var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
140
155
  var ratio = (midY - off.top) / (off.bot - off.top);
@@ -259,19 +274,6 @@
259
274
  function makeConnections(dv) {
260
275
  if (!dv.showDifferences) return;
261
276
 
262
- var align = dv.mv.options.connect == "align", oldScrollEdit, oldScrollOrig;
263
- if (align) {
264
- if (!dv.orig.curOp) return dv.orig.operation(function() {
265
- makeConnections(dv);
266
- });
267
- oldScrollEdit = dv.edit.getScrollInfo().top;
268
- oldScrollOrig = dv.orig.getScrollInfo().top;
269
- for (var i = 0; i < dv.aligners.length; i++)
270
- dv.aligners[i].clear();
271
- dv.aligners.length = 0;
272
- var extraSpaceAbove = {edit: 0, orig: 0};
273
- }
274
-
275
277
  if (dv.svg) {
276
278
  clear(dv.svg);
277
279
  var w = dv.gap.offsetWidth;
@@ -281,34 +283,118 @@
281
283
 
282
284
  var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
283
285
  var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top;
284
- iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
285
- if (topEdit <= vpEdit.to && botEdit >= vpEdit.from &&
286
- topOrig <= vpOrig.to && botOrig >= vpOrig.from)
287
- drawConnectorsForChunk(dv, topOrig, botOrig, topEdit, botEdit, sTopOrig, sTopEdit, w);
288
- if (align && (topEdit <= vpEdit.to || topOrig <= vpOrig.to)) {
289
- var above = (botEdit < vpEdit.from && botOrig < vpOrig.from);
290
- alignChunks(dv, topOrig, botOrig, topEdit, botEdit, above && extraSpaceAbove);
286
+ for (var i = 0; i < dv.chunks.length; i++) {
287
+ var ch = dv.chunks[i];
288
+ if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from &&
289
+ ch.origFrom <= vpOrig.to && ch.origTo >= vpOrig.from)
290
+ drawConnectorsForChunk(dv, ch, sTopOrig, sTopEdit, w);
291
+ }
292
+ }
293
+
294
+ function getMatchingOrigLine(editLine, chunks) {
295
+ var editStart = 0, origStart = 0;
296
+ for (var i = 0; i < chunks.length; i++) {
297
+ var chunk = chunks[i];
298
+ if (chunk.editTo > editLine && chunk.editFrom <= editLine) return null;
299
+ if (chunk.editFrom > editLine) break;
300
+ editStart = chunk.editTo;
301
+ origStart = chunk.origTo;
302
+ }
303
+ return origStart + (editLine - editStart);
304
+ }
305
+
306
+ function findAlignedLines(dv, other) {
307
+ var linesToAlign = [];
308
+ for (var i = 0; i < dv.chunks.length; i++) {
309
+ var chunk = dv.chunks[i];
310
+ linesToAlign.push([chunk.origTo, chunk.editTo, other ? getMatchingOrigLine(chunk.editTo, other.chunks) : null]);
311
+ }
312
+ if (other) {
313
+ for (var i = 0; i < other.chunks.length; i++) {
314
+ var chunk = other.chunks[i];
315
+ for (var j = 0; j < linesToAlign.length; j++) {
316
+ var align = linesToAlign[j];
317
+ if (align[1] == chunk.editTo) {
318
+ j = -1;
319
+ break;
320
+ } else if (align[1] > chunk.editTo) {
321
+ break;
322
+ }
323
+ }
324
+ if (j > -1)
325
+ linesToAlign.splice(j - 1, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]);
291
326
  }
327
+ }
328
+ return linesToAlign;
329
+ }
330
+
331
+ function alignChunks(dv, force) {
332
+ if (!dv.dealigned && !force) return;
333
+ if (!dv.orig.curOp) return dv.orig.operation(function() {
334
+ alignChunks(dv, force);
292
335
  });
293
- if (align) {
294
- if (extraSpaceAbove.edit)
295
- dv.aligners.push(padBelow(dv.edit, 0, extraSpaceAbove.edit));
296
- if (extraSpaceAbove.orig)
297
- dv.aligners.push(padBelow(dv.orig, 0, extraSpaceAbove.orig));
298
- dv.edit.scrollTo(null, oldScrollEdit);
299
- dv.orig.scrollTo(null, oldScrollOrig);
336
+
337
+ dv.dealigned = false;
338
+ var other = dv.mv.left == dv ? dv.mv.right : dv.mv.left;
339
+ if (other) {
340
+ ensureDiff(other);
341
+ other.dealigned = false;
300
342
  }
343
+ var linesToAlign = findAlignedLines(dv, other);
344
+
345
+ // Clear old aligners
346
+ var aligners = dv.mv.aligners;
347
+ for (var i = 0; i < aligners.length; i++)
348
+ aligners[i].clear();
349
+ aligners.length = 0;
350
+
351
+ var cm = [dv.orig, dv.edit], scroll = [];
352
+ if (other) cm.push(other.orig);
353
+ for (var i = 0; i < cm.length; i++)
354
+ scroll.push(cm[i].getScrollInfo().top);
355
+
356
+ for (var ln = 0; ln < linesToAlign.length; ln++)
357
+ alignLines(cm, linesToAlign[ln], aligners);
358
+
359
+ for (var i = 0; i < cm.length; i++)
360
+ cm[i].scrollTo(null, scroll[i]);
301
361
  }
302
362
 
303
- function drawConnectorsForChunk(dv, topOrig, botOrig, topEdit, botEdit, sTopOrig, sTopEdit, w) {
363
+ function alignLines(cm, lines, aligners) {
364
+ var maxOffset = 0, offset = [];
365
+ for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
366
+ var off = cm[i].heightAtLine(lines[i], "local");
367
+ offset[i] = off;
368
+ maxOffset = Math.max(maxOffset, off);
369
+ }
370
+ for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
371
+ var diff = maxOffset - offset[i];
372
+ if (diff > 1)
373
+ aligners.push(padAbove(cm[i], lines[i], diff));
374
+ }
375
+ }
376
+
377
+ function padAbove(cm, line, size) {
378
+ var above = true;
379
+ if (line > cm.lastLine()) {
380
+ line--;
381
+ above = false;
382
+ }
383
+ var elt = document.createElement("div");
384
+ elt.className = "CodeMirror-merge-spacer";
385
+ elt.style.height = size + "px"; elt.style.minWidth = "1px";
386
+ return cm.addLineWidget(line, elt, {height: size, above: above});
387
+ }
388
+
389
+ function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) {
304
390
  var flip = dv.type == "left";
305
- var top = dv.orig.heightAtLine(topOrig, "local") - sTopOrig;
391
+ var top = dv.orig.heightAtLine(chunk.origFrom, "local") - sTopOrig;
306
392
  if (dv.svg) {
307
393
  var topLpx = top;
308
- var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
394
+ var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit;
309
395
  if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
310
- var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
311
- var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
396
+ var botLpx = dv.orig.heightAtLine(chunk.origTo, "local") - sTopOrig;
397
+ var botRpx = dv.edit.heightAtLine(chunk.editTo, "local") - sTopEdit;
312
398
  if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
313
399
  var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
314
400
  var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
@@ -321,48 +407,26 @@
321
407
  "CodeMirror-merge-copy"));
322
408
  var editOriginals = dv.mv.options.allowEditingOriginals;
323
409
  copy.title = editOriginals ? "Push to left" : "Revert chunk";
324
- copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
410
+ copy.chunk = chunk;
325
411
  copy.style.top = top + "px";
326
412
 
327
413
  if (editOriginals) {
328
- var topReverse = dv.orig.heightAtLine(topEdit, "local") - sTopEdit;
414
+ var topReverse = dv.orig.heightAtLine(chunk.editFrom, "local") - sTopEdit;
329
415
  var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc",
330
416
  "CodeMirror-merge-copy-reverse"));
331
417
  copyReverse.title = "Push to right";
332
- copyReverse.chunk = {topEdit: topOrig, botEdit: botOrig, topOrig: topEdit, botOrig: botEdit};
418
+ copyReverse.chunk = {editFrom: chunk.origFrom, editTo: chunk.origTo,
419
+ origFrom: chunk.editFrom, origTo: chunk.editTo};
333
420
  copyReverse.style.top = topReverse + "px";
334
421
  dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px";
335
422
  }
336
423
  }
337
424
  }
338
425
 
339
- function alignChunks(dv, topOrig, botOrig, topEdit, botEdit, aboveViewport) {
340
- var topOrigPx = dv.orig.heightAtLine(topOrig, "local");
341
- var botOrigPx = dv.orig.heightAtLine(botOrig, "local");
342
- var topEditPx = dv.edit.heightAtLine(topEdit, "local");
343
- var botEditPx = dv.edit.heightAtLine(botEdit, "local");
344
- var origH = botOrigPx -topOrigPx, editH = botEditPx - topEditPx;
345
- var diff = editH - origH;
346
- if (diff > 1) {
347
- if (aboveViewport) aboveViewport.orig += diff;
348
- else dv.aligners.push(padBelow(dv.orig, botOrig - 1, diff));
349
- } else if (diff < -1) {
350
- if (aboveViewport) aboveViewport.edit -= diff;
351
- else dv.aligners.push(padBelow(dv.edit, botEdit - 1, -diff));
352
- }
353
- return 0;
354
- }
355
-
356
- function padBelow(cm, line, size) {
357
- var elt = document.createElement("div");
358
- elt.style.height = size + "px"; elt.style.minWidth = "1px";
359
- return cm.addLineWidget(line, elt, {height: size});
360
- }
361
-
362
426
  function copyChunk(dv, to, from, chunk) {
363
427
  if (dv.diffOutOfDate) return;
364
- to.replaceRange(from.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
365
- Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0));
428
+ to.replaceRange(from.getRange(Pos(chunk.origFrom, 0), Pos(chunk.origTo, 0)),
429
+ Pos(chunk.editFrom, 0), Pos(chunk.editTo, 0));
366
430
  }
367
431
 
368
432
  // Merge view, containing 0, 1, or 2 diff views.
@@ -372,16 +436,11 @@
372
436
 
373
437
  this.options = options;
374
438
  var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
375
- if (origLeft && origRight) {
376
- if (options.connect == "align")
377
- throw new Error("connect: \"align\" is not supported for three-way merge views");
378
- if (options.collapseIdentical)
379
- throw new Error("collapseIdentical option is not supported for three-way merge views");
380
- }
381
439
 
382
440
  var hasLeft = origLeft != null, hasRight = origRight != null;
383
441
  var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
384
442
  var wrap = [], left = this.left = null, right = this.right = null;
443
+ var self = this;
385
444
 
386
445
  if (hasLeft) {
387
446
  left = this.left = new DiffView(this, "left");
@@ -410,8 +469,17 @@
410
469
  if (left) left.init(leftPane, origLeft, options);
411
470
  if (right) right.init(rightPane, origRight, options);
412
471
 
413
- if (options.collapseIdentical)
414
- collapseIdenticalStretches(left || right, options.collapseIdentical);
472
+ if (options.collapseIdentical) {
473
+ updating = true;
474
+ this.editor().operation(function() {
475
+ collapseIdenticalStretches(self, options.collapseIdentical);
476
+ });
477
+ updating = false;
478
+ }
479
+ if (options.connect == "align") {
480
+ this.aligners = [];
481
+ alignChunks(this.left || this.right, true);
482
+ }
415
483
 
416
484
  var onResize = function() {
417
485
  if (left) makeConnections(left);
@@ -463,10 +531,10 @@
463
531
  if (this.left) this.left.setShowDifferences(val);
464
532
  },
465
533
  rightChunks: function() {
466
- return this.right && getChunks(this.right);
534
+ if (this.right) { ensureDiff(this.right); return this.right.chunks; }
467
535
  },
468
536
  leftChunks: function() {
469
- return this.left && getChunks(this.left);
537
+ if (this.left) { ensureDiff(this.left); return this.left.chunks; }
470
538
  }
471
539
  };
472
540
 
@@ -494,7 +562,8 @@
494
562
  return diff;
495
563
  }
496
564
 
497
- function iterateChunks(diff, f) {
565
+ function getChunks(diff) {
566
+ var chunks = [];
498
567
  var startEdit = 0, startOrig = 0;
499
568
  var edit = Pos(0, 0), orig = Pos(0, 0);
500
569
  for (var i = 0; i < diff.length; ++i) {
@@ -506,7 +575,8 @@
506
575
  var endOff = endOfLineClean(diff, i) ? 1 : 0;
507
576
  var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff;
508
577
  if (cleanToEdit > cleanFromEdit) {
509
- if (i) f(startOrig, cleanFromOrig, startEdit, cleanFromEdit);
578
+ if (i) chunks.push({origFrom: startOrig, origTo: cleanFromOrig,
579
+ editFrom: startEdit, editTo: cleanFromEdit});
510
580
  startEdit = cleanToEdit; startOrig = cleanToOrig;
511
581
  }
512
582
  } else {
@@ -514,17 +584,9 @@
514
584
  }
515
585
  }
516
586
  if (startEdit <= edit.line || startOrig <= orig.line)
517
- f(startOrig, orig.line + 1, startEdit, edit.line + 1);
518
- }
519
-
520
- function getChunks(dv) {
521
- ensureDiff(dv);
522
- var collect = [];
523
- iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
524
- collect.push({origFrom: topOrig, origTo: botOrig,
525
- editFrom: topEdit, editTo: botEdit});
526
- });
527
- return collect;
587
+ chunks.push({origFrom: startOrig, origTo: orig.line + 1,
588
+ editFrom: startEdit, editTo: edit.line + 1});
589
+ return chunks;
528
590
  }
529
591
 
530
592
  function endOfLineClean(diff, i) {
@@ -545,18 +607,19 @@
545
607
  return last.charCodeAt(last.length - 1) == 10;
546
608
  }
547
609
 
548
- function chunkBoundariesAround(diff, n, nInEdit) {
610
+ function chunkBoundariesAround(chunks, n, nInEdit) {
549
611
  var beforeE, afterE, beforeO, afterO;
550
- iterateChunks(diff, function(fromOrig, toOrig, fromEdit, toEdit) {
551
- var fromLocal = nInEdit ? fromEdit : fromOrig;
552
- var toLocal = nInEdit ? toEdit : toOrig;
612
+ for (var i = 0; i < chunks.length; i++) {
613
+ var chunk = chunks[i];
614
+ var fromLocal = nInEdit ? chunk.editFrom : chunk.origFrom;
615
+ var toLocal = nInEdit ? chunk.editTo : chunk.origTo;
553
616
  if (afterE == null) {
554
- if (fromLocal > n) { afterE = fromEdit; afterO = fromOrig; }
555
- else if (toLocal > n) { afterE = toEdit; afterO = toOrig; }
617
+ if (fromLocal > n) { afterE = chunk.editFrom; afterO = chunk.origFrom; }
618
+ else if (toLocal > n) { afterE = chunk.editTo; afterO = chunk.origTo; }
556
619
  }
557
- if (toLocal <= n) { beforeE = toEdit; beforeO = toOrig; }
558
- else if (fromLocal <= n) { beforeE = fromEdit; beforeO = fromOrig; }
559
- });
620
+ if (toLocal <= n) { beforeE = chunk.editTo; beforeO = chunk.origTo; }
621
+ else if (fromLocal <= n) { beforeE = chunk.editFrom; beforeO = chunk.origFrom; }
622
+ }
560
623
  return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
561
624
  }
562
625
 
@@ -579,25 +642,50 @@
579
642
  return {mark: mark, clear: clear};
580
643
  }
581
644
 
582
- function collapseStretch(dv, origStart, editStart, size) {
583
- var mOrig = collapseSingle(dv.orig, origStart, origStart + size);
584
- var mEdit = collapseSingle(dv.edit, editStart, editStart + size);
585
- mOrig.mark.on("clear", function() { mEdit.clear(); });
586
- mEdit.mark.on("clear", function() { mOrig.clear(); });
645
+ function collapseStretch(size, editors) {
646
+ var marks = [];
647
+ function clear() {
648
+ for (var i = 0; i < marks.length; i++) marks[i].clear();
649
+ }
650
+ for (var i = 0; i < editors.length; i++) {
651
+ var editor = editors[i];
652
+ var mark = collapseSingle(editor.cm, editor.line, editor.line + size);
653
+ marks.push(mark);
654
+ mark.mark.on("clear", clear);
655
+ }
656
+ return marks[0].mark;
657
+ }
658
+
659
+ function unclearNearChunks(dv, margin, off, clear) {
660
+ for (var i = 0; i < dv.chunks.length; i++) {
661
+ var chunk = dv.chunks[i];
662
+ for (var l = chunk.editFrom - margin; l < chunk.editTo + margin; l++) {
663
+ var pos = l + off;
664
+ if (pos >= 0 && pos < clear.length) clear[pos] = false;
665
+ }
666
+ }
587
667
  }
588
668
 
589
- function collapseIdenticalStretches(dv, margin) {
669
+ function collapseIdenticalStretches(mv, margin) {
590
670
  if (typeof margin != "number") margin = 2;
591
- var lastOrig = dv.orig.firstLine(), lastEdit = dv.edit.firstLine();
592
- iterateChunks(dv.diff, function(topOrig, botOrig, _topEdit, botEdit) {
593
- var identicalSize = topOrig - margin - lastOrig;
594
- if (identicalSize > margin)
595
- collapseStretch(dv, lastOrig, lastEdit, identicalSize);
596
- lastOrig = botOrig + margin; lastEdit = botEdit + margin;
597
- });
598
- var bottomSize = dv.orig.lastLine() + 1 - lastOrig;
599
- if (bottomSize > margin)
600
- collapseStretch(dv, lastOrig, lastEdit, bottomSize);
671
+ var clear = [], edit = mv.editor(), off = edit.firstLine();
672
+ for (var l = off, e = edit.lastLine(); l <= e; l++) clear.push(true);
673
+ if (mv.left) unclearNearChunks(mv.left, margin, off, clear);
674
+ if (mv.right) unclearNearChunks(mv.right, margin, off, clear);
675
+
676
+ for (var i = 0; i < clear.length; i++) {
677
+ if (clear[i]) {
678
+ var line = i + off;
679
+ for (var size = 1; i < clear.length - 1 && clear[i + 1]; i++, size++) {}
680
+ if (size > margin) {
681
+ var editors = [{line: line, cm: edit}];
682
+ if (mv.left) editors.push({line: getMatchingOrigLine(line, mv.left.chunks), cm: mv.left.orig});
683
+ if (mv.right) editors.push({line: getMatchingOrigLine(line, mv.right.chunks), cm: mv.right.orig});
684
+ var mark = collapseStretch(size, editors);
685
+ if (mv.options.onCollapse) mv.options.onCollapse(mv, line, size, mark);
686
+ }
687
+ }
688
+ }
601
689
  }
602
690
 
603
691
  // General utilities