codemirror-rails 4.12 → 4.13

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.
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