codemirror-rails 4.3 → 4.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1d56f928591a2640684b3d67f64beaf69e7ccbe
4
- data.tar.gz: 129896fec638b52cae69719f29a89789670cbef9
3
+ metadata.gz: 8f231dd42a91b4d2a0ecc9440cad8f8e505f239d
4
+ data.tar.gz: 3482d82a48c052c4472c55a0db8a648fb87e0b87
5
5
  SHA512:
6
- metadata.gz: d1b37713a3c42d810cfd29b0db1aebca2a11df107ad2c90b7517b887d88ea24783c9d8810569fe225e0a0d10e8fa4b8de8f795e9767ecd1a898a7dfad245b32c
7
- data.tar.gz: 5afad0b33ba6c4711e2d38541b03425378de25b587bbdc6dfe7c347a17fa1cf9b1ad30ded9e8e87f23ea73a1db0b6bf6eff17d53bb8ad88affc74b2088cff15b
6
+ metadata.gz: 0989abb66c57e0f95cb9f20646f797c6eb296da0d1a87845379ea52db4488e24458bd40e61176d0125433abb0b7c6fc4dca586a0b4dcfe66273742ccb0a7973b
7
+ data.tar.gz: 74161b00c7c25f04c3468e58e304f989691cc6cc5c51c3017c7471f9f2112dc5a39a31779d960b9be3813ca28774b30a1ed9ca201e381f546c66bc2a1b571138
@@ -1,6 +1,6 @@
1
1
  module Codemirror
2
2
  module Rails
3
- VERSION = '4.3'
4
- CODEMIRROR_VERSION = '4.3'
3
+ VERSION = '4.4'
4
+ CODEMIRROR_VERSION = '4.4'
5
5
  end
6
6
  end
@@ -108,6 +108,7 @@
108
108
 
109
109
  for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
110
110
  optionHandlers[opt](cm, options[opt], Init);
111
+ maybeUpdateLineNumberWidth(cm);
111
112
  for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm);
112
113
  });
113
114
  }
@@ -467,18 +468,18 @@
467
468
  }
468
469
 
469
470
  // Compute the lines that are visible in a given viewport (defaults
470
- // the the current scroll position). viewPort may contain top,
471
+ // the the current scroll position). viewport may contain top,
471
472
  // height, and ensure (see op.scrollToPos) properties.
472
- function visibleLines(display, doc, viewPort) {
473
- var top = viewPort && viewPort.top != null ? Math.max(0, viewPort.top) : display.scroller.scrollTop;
473
+ function visibleLines(display, doc, viewport) {
474
+ var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
474
475
  top = Math.floor(top - paddingTop(display));
475
- var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + display.wrapper.clientHeight;
476
+ var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
476
477
 
477
478
  var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
478
479
  // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
479
480
  // forces those lines into the viewport (if possible).
480
- if (viewPort && viewPort.ensure) {
481
- var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to.line;
481
+ if (viewport && viewport.ensure) {
482
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
482
483
  if (ensureFrom < from)
483
484
  return {from: ensureFrom,
484
485
  to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight)};
@@ -543,83 +544,46 @@
543
544
 
544
545
  // DISPLAY DRAWING
545
546
 
546
- // Updates the display, selection, and scrollbars, using the
547
- // information in display.view to find out which nodes are no longer
548
- // up-to-date. Tries to bail out early when no changes are needed,
549
- // unless forced is true.
550
- // Returns true if an actual update happened, false otherwise.
551
- function updateDisplay(cm, viewPort, forced) {
552
- var oldFrom = cm.display.viewFrom, oldTo = cm.display.viewTo, updated;
553
- var visible = visibleLines(cm.display, cm.doc, viewPort);
554
- for (var first = true;; first = false) {
555
- var oldWidth = cm.display.scroller.clientWidth;
556
- if (!updateDisplayInner(cm, visible, forced)) break;
557
- updated = true;
558
-
559
- // If the max line changed since it was last measured, measure it,
560
- // and ensure the document's width matches it.
561
- if (cm.display.maxLineChanged && !cm.options.lineWrapping)
562
- adjustContentWidth(cm);
563
-
564
- var barMeasure = measureForScrollbars(cm);
565
- updateSelection(cm);
566
- setDocumentHeight(cm, barMeasure);
567
- updateScrollbars(cm, barMeasure);
568
- if (webkit && cm.options.lineWrapping)
569
- checkForWebkitWidthBug(cm, barMeasure); // (Issue #2420)
570
- if (webkit && barMeasure.scrollWidth > barMeasure.clientWidth &&
571
- barMeasure.scrollWidth < barMeasure.clientWidth + 1 &&
572
- !hScrollbarTakesSpace(cm))
573
- updateScrollbars(cm); // (Issue #2562)
574
- if (first && cm.options.lineWrapping && oldWidth != cm.display.scroller.clientWidth) {
575
- forced = true;
576
- continue;
577
- }
578
- forced = false;
579
-
580
- // Clip forced viewport to actual scrollable area.
581
- if (viewPort && viewPort.top != null)
582
- viewPort = {top: Math.min(barMeasure.docHeight - scrollerCutOff - barMeasure.clientHeight, viewPort.top)};
583
- // Updated line heights might result in the drawn area not
584
- // actually covering the viewport. Keep looping until it does.
585
- visible = visibleLines(cm.display, cm.doc, viewPort);
586
- if (visible.from >= cm.display.viewFrom && visible.to <= cm.display.viewTo)
587
- break;
588
- }
547
+ function DisplayUpdate(cm, viewport, force) {
548
+ var display = cm.display;
589
549
 
590
- cm.display.updateLineNumbers = null;
591
- if (updated) {
592
- signalLater(cm, "update", cm);
593
- if (cm.display.viewFrom != oldFrom || cm.display.viewTo != oldTo)
594
- signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
595
- }
596
- return updated;
550
+ this.viewport = viewport;
551
+ // Store some values that we'll need later (but don't want to force a relayout for)
552
+ this.visible = visibleLines(display, cm.doc, viewport);
553
+ this.editorIsHidden = !display.wrapper.offsetWidth;
554
+ this.wrapperHeight = display.wrapper.clientHeight;
555
+ this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
556
+ this.oldScrollerWidth = display.scroller.clientWidth;
557
+ this.force = force;
558
+ this.dims = getDimensions(cm);
597
559
  }
598
560
 
599
561
  // Does the actual updating of the line display. Bails out
600
562
  // (returning false) when there is nothing to be done and forced is
601
563
  // false.
602
- function updateDisplayInner(cm, visible, forced) {
564
+ function updateDisplayIfNeeded(cm, update) {
603
565
  var display = cm.display, doc = cm.doc;
604
- if (!display.wrapper.offsetWidth) {
566
+ if (update.editorIsHidden) {
605
567
  resetView(cm);
606
- return;
568
+ return false;
607
569
  }
608
570
 
609
571
  // Bail out if the visible area is already rendered and nothing changed.
610
- if (!forced && visible.from >= display.viewFrom && visible.to <= display.viewTo &&
572
+ if (!update.force &&
573
+ update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
611
574
  (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
612
575
  countDirtyView(cm) == 0)
613
- return;
576
+ return false;
614
577
 
615
- if (maybeUpdateLineNumberWidth(cm))
578
+ if (maybeUpdateLineNumberWidth(cm)) {
616
579
  resetView(cm);
617
- var dims = getDimensions(cm);
580
+ update.dims = getDimensions(cm);
581
+ }
618
582
 
619
583
  // Compute a suitable new viewport (from & to)
620
584
  var end = doc.first + doc.size;
621
- var from = Math.max(visible.from - cm.options.viewportMargin, doc.first);
622
- var to = Math.min(end, visible.to + cm.options.viewportMargin);
585
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
586
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
623
587
  if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max(doc.first, display.viewFrom);
624
588
  if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, display.viewTo);
625
589
  if (sawCollapsedSpans) {
@@ -628,7 +592,7 @@
628
592
  }
629
593
 
630
594
  var different = from != display.viewFrom || to != display.viewTo ||
631
- display.lastSizeC != display.wrapper.clientHeight;
595
+ display.lastSizeC != update.wrapperHeight;
632
596
  adjustView(cm, from, to);
633
597
 
634
598
  display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
@@ -636,13 +600,15 @@
636
600
  cm.display.mover.style.top = display.viewOffset + "px";
637
601
 
638
602
  var toUpdate = countDirtyView(cm);
639
- if (!different && toUpdate == 0 && !forced) return;
603
+ if (!different && toUpdate == 0 && !update.force &&
604
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
605
+ return false;
640
606
 
641
607
  // For big changes, we hide the enclosing element during the
642
608
  // update, since that speeds up the operations on most browsers.
643
609
  var focused = activeElt();
644
610
  if (toUpdate > 4) display.lineDiv.style.display = "none";
645
- patchDisplay(cm, display.updateLineNumbers, dims);
611
+ patchDisplay(cm, display.updateLineNumbers, update.dims);
646
612
  if (toUpdate > 4) display.lineDiv.style.display = "";
647
613
  // There might have been a widget with a focused element that got
648
614
  // hidden or updated, if so re-focus it.
@@ -654,24 +620,54 @@
654
620
  removeChildren(display.selectionDiv);
655
621
 
656
622
  if (different) {
657
- display.lastSizeC = display.wrapper.clientHeight;
623
+ display.lastSizeC = update.wrapperHeight;
658
624
  startWorker(cm, 400);
659
625
  }
660
626
 
661
- updateHeightsInViewport(cm);
627
+ display.updateLineNumbers = null;
662
628
 
663
629
  return true;
664
630
  }
665
631
 
666
- function adjustContentWidth(cm) {
667
- var display = cm.display;
668
- var width = measureChar(cm, display.maxLine, display.maxLine.text.length).left;
669
- display.maxLineChanged = false;
670
- var minWidth = Math.max(0, width + 3);
671
- var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scrollerCutOff - display.scroller.clientWidth);
672
- display.sizer.style.minWidth = minWidth + "px";
673
- if (maxScrollLeft < cm.doc.scrollLeft)
674
- setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), true);
632
+ function postUpdateDisplay(cm, update) {
633
+ var force = update.force, viewport = update.viewport;
634
+ for (var first = true;; first = false) {
635
+ if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.display.scroller.clientWidth) {
636
+ force = true;
637
+ } else {
638
+ force = false;
639
+ // Clip forced viewport to actual scrollable area.
640
+ if (viewport && viewport.top != null)
641
+ viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - scrollerCutOff -
642
+ cm.display.scroller.clientHeight, viewport.top)};
643
+ // Updated line heights might result in the drawn area not
644
+ // actually covering the viewport. Keep looping until it does.
645
+ update.visible = visibleLines(cm.display, cm.doc, viewport);
646
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
647
+ break;
648
+ }
649
+ if (!updateDisplayIfNeeded(cm, update)) break;
650
+ updateHeightsInViewport(cm);
651
+ var barMeasure = measureForScrollbars(cm);
652
+ updateSelection(cm);
653
+ setDocumentHeight(cm, barMeasure);
654
+ updateScrollbars(cm, barMeasure);
655
+ }
656
+
657
+ signalLater(cm, "update", cm);
658
+ if (cm.display.viewFrom != update.oldViewFrom || cm.display.viewTo != update.oldViewTo)
659
+ signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
660
+ }
661
+
662
+ function updateDisplaySimple(cm, viewport) {
663
+ var update = new DisplayUpdate(cm, viewport);
664
+ if (updateDisplayIfNeeded(cm, update)) {
665
+ postUpdateDisplay(cm, update);
666
+ var barMeasure = measureForScrollbars(cm);
667
+ updateSelection(cm);
668
+ setDocumentHeight(cm, barMeasure);
669
+ updateScrollbars(cm, barMeasure);
670
+ }
675
671
  }
676
672
 
677
673
  function setDocumentHeight(cm, measure) {
@@ -1257,10 +1253,10 @@
1257
1253
  // SELECTION DRAWING
1258
1254
 
1259
1255
  // Redraw the selection and/or cursor
1260
- function updateSelection(cm) {
1261
- var display = cm.display, doc = cm.doc;
1262
- var curFragment = document.createDocumentFragment();
1263
- var selFragment = document.createDocumentFragment();
1256
+ function drawSelection(cm) {
1257
+ var display = cm.display, doc = cm.doc, result = {};
1258
+ var curFragment = result.cursors = document.createDocumentFragment();
1259
+ var selFragment = result.selection = document.createDocumentFragment();
1264
1260
 
1265
1261
  for (var i = 0; i < doc.sel.ranges.length; i++) {
1266
1262
  var range = doc.sel.ranges[i];
@@ -1275,16 +1271,23 @@
1275
1271
  if (cm.options.moveInputWithCursor) {
1276
1272
  var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1277
1273
  var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
1278
- var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1279
- headPos.top + lineOff.top - wrapOff.top));
1280
- var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1281
- headPos.left + lineOff.left - wrapOff.left));
1282
- display.inputDiv.style.top = top + "px";
1283
- display.inputDiv.style.left = left + "px";
1274
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1275
+ headPos.top + lineOff.top - wrapOff.top));
1276
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1277
+ headPos.left + lineOff.left - wrapOff.left));
1284
1278
  }
1285
1279
 
1286
- removeChildrenAndAdd(display.cursorDiv, curFragment);
1287
- removeChildrenAndAdd(display.selectionDiv, selFragment);
1280
+ return result;
1281
+ }
1282
+
1283
+ function updateSelection(cm, drawn) {
1284
+ if (!drawn) drawn = drawSelection(cm);
1285
+ removeChildrenAndAdd(cm.display.cursorDiv, drawn.cursors);
1286
+ removeChildrenAndAdd(cm.display.selectionDiv, drawn.selection);
1287
+ if (drawn.teTop != null) {
1288
+ cm.display.inputDiv.style.top = drawn.teTop + "px";
1289
+ cm.display.inputDiv.style.left = drawn.teLeft + "px";
1290
+ }
1288
1291
  }
1289
1292
 
1290
1293
  // Draws a cursor for the given range
@@ -1408,8 +1411,8 @@
1408
1411
  if (doc.frontier >= cm.display.viewTo) return;
1409
1412
  var end = +new Date + cm.options.workTime;
1410
1413
  var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
1414
+ var changedLines = [];
1411
1415
 
1412
- runInOp(cm, function() {
1413
1416
  doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function(line) {
1414
1417
  if (doc.frontier >= cm.display.viewFrom) { // Visible
1415
1418
  var oldStyles = line.styles;
@@ -1421,7 +1424,7 @@
1421
1424
  var ischange = !oldStyles || oldStyles.length != line.styles.length ||
1422
1425
  oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
1423
1426
  for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldStyles[i] != line.styles[i];
1424
- if (ischange) regLineChange(cm, doc.frontier, "text");
1427
+ if (ischange) changedLines.push(doc.frontier);
1425
1428
  line.stateAfter = copyState(doc.mode, state);
1426
1429
  } else {
1427
1430
  processLine(cm, line.text, state);
@@ -1433,6 +1436,9 @@
1433
1436
  return true;
1434
1437
  }
1435
1438
  });
1439
+ if (changedLines.length) runInOp(cm, function() {
1440
+ for (var i = 0; i < changedLines.length; i++)
1441
+ regLineChange(cm, changedLines[i], "text");
1436
1442
  });
1437
1443
  }
1438
1444
 
@@ -1665,6 +1671,8 @@
1665
1671
  rect = nullRect;
1666
1672
  }
1667
1673
 
1674
+ if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measure, rect);
1675
+
1668
1676
  var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
1669
1677
  var mid = (rtop + rbot) / 2;
1670
1678
  var heights = prepared.view.measure.heights;
@@ -1676,9 +1684,22 @@
1676
1684
  top: top, bottom: bot};
1677
1685
  if (!rect.left && !rect.right) result.bogus = true;
1678
1686
  if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
1687
+
1679
1688
  return result;
1680
1689
  }
1681
1690
 
1691
+ // Work around problem with bounding client rects on ranges being
1692
+ // returned incorrectly when zoomed on IE10 and below.
1693
+ function maybeUpdateRectForZooming(measure, rect) {
1694
+ if (!window.screen || screen.logicalXDPI == null ||
1695
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
1696
+ return rect;
1697
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI;
1698
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI;
1699
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
1700
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY};
1701
+ }
1702
+
1682
1703
  function clearLineMeasurementCacheFor(lineView) {
1683
1704
  if (lineView.measure) {
1684
1705
  lineView.measure.cache = {};
@@ -1911,10 +1932,13 @@
1911
1932
  // error-prone). Instead, display updates are batched and then all
1912
1933
  // combined and executed at once.
1913
1934
 
1935
+ var operationGroup = null;
1936
+
1914
1937
  var nextOpId = 0;
1915
1938
  // Start a new operation.
1916
1939
  function startOperation(cm) {
1917
1940
  cm.curOp = {
1941
+ cm: cm,
1918
1942
  viewChanged: false, // Flag that indicates that lines might need to be redrawn
1919
1943
  startHeight: cm.doc.height, // Used to detect need to update scrollbar
1920
1944
  forceUpdate: false, // Used to force a redraw
@@ -1922,33 +1946,129 @@
1922
1946
  typing: false, // Whether this reset should be careful to leave existing text (for compositing)
1923
1947
  changeObjs: null, // Accumulated changes, for firing change events
1924
1948
  cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
1949
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
1925
1950
  selectionChanged: false, // Whether the selection needs to be redrawn
1926
1951
  updateMaxLine: false, // Set when the widest line needs to be determined anew
1927
1952
  scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
1928
1953
  scrollToPos: null, // Used to scroll to a specific position
1929
1954
  id: ++nextOpId // Unique ID
1930
1955
  };
1931
- if (!delayedCallbackDepth++) delayedCallbacks = [];
1956
+ if (operationGroup) {
1957
+ operationGroup.ops.push(cm.curOp);
1958
+ } else {
1959
+ cm.curOp.ownsGroup = operationGroup = {
1960
+ ops: [cm.curOp],
1961
+ delayedCallbacks: []
1962
+ };
1963
+ }
1964
+ }
1965
+
1966
+ function fireCallbacksForOps(group) {
1967
+ // Calls delayed callbacks and cursorActivity handlers until no
1968
+ // new ones appear
1969
+ var callbacks = group.delayedCallbacks, i = 0;
1970
+ do {
1971
+ for (; i < callbacks.length; i++)
1972
+ callbacks[i]();
1973
+ for (var j = 0; j < group.ops.length; j++) {
1974
+ var op = group.ops[j];
1975
+ if (op.cursorActivityHandlers)
1976
+ while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
1977
+ op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
1978
+ }
1979
+ } while (i < callbacks.length);
1932
1980
  }
1933
1981
 
1934
1982
  // Finish an operation, updating the display and signalling delayed events
1935
1983
  function endOperation(cm) {
1936
- var op = cm.curOp, doc = cm.doc, display = cm.display;
1937
- cm.curOp = null;
1938
-
1984
+ var op = cm.curOp, group = op.ownsGroup;
1985
+ if (!group) return;
1986
+
1987
+ try { fireCallbacksForOps(group); }
1988
+ finally {
1989
+ operationGroup = null;
1990
+ for (var i = 0; i < group.ops.length; i++)
1991
+ group.ops[i].cm.curOp = null;
1992
+ endOperations(group);
1993
+ }
1994
+ }
1995
+
1996
+ // The DOM updates done when an operation finishes are batched so
1997
+ // that the minimum number of relayouts are required.
1998
+ function endOperations(group) {
1999
+ var ops = group.ops;
2000
+ for (var i = 0; i < ops.length; i++) // Read DOM
2001
+ endOperation_R1(ops[i]);
2002
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
2003
+ endOperation_W1(ops[i]);
2004
+ for (var i = 0; i < ops.length; i++) // Read DOM
2005
+ endOperation_R2(ops[i]);
2006
+ for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
2007
+ endOperation_W2(ops[i]);
2008
+ for (var i = 0; i < ops.length; i++) // Read DOM
2009
+ endOperation_finish(ops[i]);
2010
+ }
2011
+
2012
+ function endOperation_R1(op) {
2013
+ var cm = op.cm, display = cm.display;
1939
2014
  if (op.updateMaxLine) findMaxLine(cm);
1940
2015
 
1941
- // If it looks like an update might be needed, call updateDisplay
1942
- if (op.viewChanged || op.forceUpdate || op.scrollTop != null ||
1943
- op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
1944
- op.scrollToPos.to.line >= display.viewTo) ||
1945
- display.maxLineChanged && cm.options.lineWrapping) {
1946
- var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
1947
- if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroller.scrollTop;
2016
+ op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
2017
+ op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
2018
+ op.scrollToPos.to.line >= display.viewTo) ||
2019
+ display.maxLineChanged && cm.options.lineWrapping;
2020
+ op.update = op.mustUpdate &&
2021
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
2022
+ }
2023
+
2024
+ function endOperation_W1(op) {
2025
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
2026
+ }
2027
+
2028
+ function endOperation_R2(op) {
2029
+ var cm = op.cm, display = cm.display;
2030
+ if (op.updatedDisplay) updateHeightsInViewport(cm);
2031
+
2032
+ // If the max line changed since it was last measured, measure it,
2033
+ // and ensure the document's width matches it.
2034
+ // updateDisplayIfNeeded will use these properties to do the actual resizing
2035
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
2036
+ op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left;
2037
+ op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo +
2038
+ scrollerCutOff - display.scroller.clientWidth);
2039
+ }
2040
+
2041
+ op.barMeasure = measureForScrollbars(cm);
2042
+ if (op.updatedDisplay || op.selectionChanged)
2043
+ op.newSelectionNodes = drawSelection(cm);
2044
+ }
2045
+
2046
+ function endOperation_W2(op) {
2047
+ var cm = op.cm;
2048
+
2049
+ if (op.adjustWidthTo != null) {
2050
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
2051
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
2052
+ setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true);
1948
2053
  }
1949
- // If no update was run, but the selection changed, redraw that.
1950
- if (!updated && op.selectionChanged) updateSelection(cm);
1951
- if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm);
2054
+
2055
+ if (op.newSelectionNodes)
2056
+ updateSelection(cm, op.newSelectionNodes);
2057
+ if (op.updatedDisplay)
2058
+ setDocumentHeight(cm, op.barMeasure);
2059
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
2060
+ updateScrollbars(cm, op.barMeasure);
2061
+
2062
+ if (op.selectionChanged) restartBlink(cm);
2063
+
2064
+ if (cm.state.focused && op.updateInput)
2065
+ resetInput(cm, op.typing);
2066
+ }
2067
+
2068
+ function endOperation_finish(op) {
2069
+ var cm = op.cm, display = cm.display, doc = cm.doc;
2070
+
2071
+ if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
1952
2072
 
1953
2073
  // Abort mouse wheel delta measurement, when scrolling explicitly
1954
2074
  if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
@@ -1966,16 +2086,11 @@
1966
2086
  }
1967
2087
  // If we need to scroll a specific position into view, do so.
1968
2088
  if (op.scrollToPos) {
1969
- var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
1970
- clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
2089
+ var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
2090
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
1971
2091
  if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coords);
1972
2092
  }
1973
2093
 
1974
- if (op.selectionChanged) restartBlink(cm);
1975
-
1976
- if (cm.state.focused && op.updateInput)
1977
- resetInput(cm, op.typing);
1978
-
1979
2094
  // Fire events for markers that are hidden/unidden by editing or
1980
2095
  // undoing
1981
2096
  var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
@@ -1984,18 +2099,22 @@
1984
2099
  if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1985
2100
  if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1986
2101
 
1987
- var delayed;
1988
- if (!--delayedCallbackDepth) {
1989
- delayed = delayedCallbacks;
1990
- delayedCallbacks = null;
2102
+ if (display.wrapper.offsetHeight)
2103
+ doc.scrollTop = cm.display.scroller.scrollTop;
2104
+
2105
+ // Apply workaround for two webkit bugs
2106
+ if (op.updatedDisplay && webkit) {
2107
+ if (cm.options.lineWrapping)
2108
+ checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420)
2109
+ if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth &&
2110
+ op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 &&
2111
+ !hScrollbarTakesSpace(cm))
2112
+ updateScrollbars(cm); // (Issue #2562)
1991
2113
  }
2114
+
1992
2115
  // Fire change events, and delayed event handlers
1993
2116
  if (op.changeObjs)
1994
2117
  signal(cm, "changes", cm, op.changeObjs);
1995
- if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i]();
1996
- if (op.cursorActivityHandlers)
1997
- for (var i = 0; i < op.cursorActivityHandlers.length; i++)
1998
- op.cursorActivityHandlers[i](cm);
1999
2118
  }
2000
2119
 
2001
2120
  // Run the given function in an operation
@@ -2247,6 +2366,11 @@
2247
2366
  cm.display.poll.set(20, p);
2248
2367
  }
2249
2368
 
2369
+ // This will be set to an array of strings when copying, so that,
2370
+ // when pasting, we know what kind of selections the copied text
2371
+ // was made out of.
2372
+ var lastCopied = null;
2373
+
2250
2374
  // Read input from the textarea, and update the document to match.
2251
2375
  // When something is selected, it is present in the textarea, and
2252
2376
  // selected (unless it is huge, in which case a placeholder is
@@ -2269,8 +2393,11 @@
2269
2393
  var text = input.value;
2270
2394
  // If nothing changed, bail.
2271
2395
  if (text == prevInput && !cm.somethingSelected()) return false;
2272
- // Work around nonsensical selection resetting in IE9/10
2273
- if (ie && ie_version >= 9 && cm.display.inputHasSelection === text) {
2396
+ // Work around nonsensical selection resetting in IE9/10, and
2397
+ // inexplicable appearance of private area unicode characters on
2398
+ // some key combos in Mac (#2689).
2399
+ if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
2400
+ mac && /[\uf700-\uf7ff]/.test(text)) {
2274
2401
  resetInput(cm);
2275
2402
  return false;
2276
2403
  }
@@ -2287,7 +2414,13 @@
2287
2414
  var inserted = text.slice(same), textLines = splitLines(inserted);
2288
2415
 
2289
2416
  // When pasing N lines into N selections, insert one line per selection
2290
- var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.ranges.length == textLines.length;
2417
+ var multiPaste = null;
2418
+ if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
2419
+ if (lastCopied && lastCopied.join("\n") == inserted)
2420
+ multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastCopied, splitLines);
2421
+ else if (textLines.length == doc.sel.ranges.length)
2422
+ multiPaste = map(textLines, function(l) { return [l]; });
2423
+ }
2291
2424
 
2292
2425
  // Normal behavior is to insert the new text into every selection
2293
2426
  for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
@@ -2300,7 +2433,7 @@
2300
2433
  else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
2301
2434
  to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length));
2302
2435
  var updateInput = cm.curOp.updateInput;
2303
- var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines,
2436
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % multiPaste.length] : textLines,
2304
2437
  origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"};
2305
2438
  makeChange(cm.doc, changeEvent);
2306
2439
  signalLater(cm, "inputRead", cm, changeEvent);
@@ -2421,7 +2554,7 @@
2421
2554
  // Prevent wrapper from ever scrolling
2422
2555
  on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
2423
2556
 
2424
- on(d.input, "keyup", operation(cm, onKeyUp));
2557
+ on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); });
2425
2558
  on(d.input, "input", function() {
2426
2559
  if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null;
2427
2560
  fastPoll(cm);
@@ -2467,27 +2600,29 @@
2467
2600
 
2468
2601
  function prepareCopyCut(e) {
2469
2602
  if (cm.somethingSelected()) {
2603
+ lastCopied = cm.getSelections();
2470
2604
  if (d.inaccurateSelection) {
2471
2605
  d.prevInput = "";
2472
2606
  d.inaccurateSelection = false;
2473
- d.input.value = cm.getSelection();
2607
+ d.input.value = lastCopied.join("\n");
2474
2608
  selectInput(d.input);
2475
2609
  }
2476
2610
  } else {
2477
- var text = "", ranges = [];
2611
+ var text = [], ranges = [];
2478
2612
  for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
2479
2613
  var line = cm.doc.sel.ranges[i].head.line;
2480
2614
  var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
2481
2615
  ranges.push(lineRange);
2482
- text += cm.getRange(lineRange.anchor, lineRange.head);
2616
+ text.push(cm.getRange(lineRange.anchor, lineRange.head));
2483
2617
  }
2484
2618
  if (e.type == "cut") {
2485
2619
  cm.setSelections(ranges, null, sel_dontScroll);
2486
2620
  } else {
2487
2621
  d.prevInput = "";
2488
- d.input.value = text;
2622
+ d.input.value = text.join("\n");
2489
2623
  selectInput(d.input);
2490
2624
  }
2625
+ lastCopied = text;
2491
2626
  }
2492
2627
  if (e.type == "cut") cm.state.cutIncoming = true;
2493
2628
  }
@@ -2885,10 +3020,10 @@
2885
3020
  function setScrollTop(cm, val) {
2886
3021
  if (Math.abs(cm.doc.scrollTop - val) < 2) return;
2887
3022
  cm.doc.scrollTop = val;
2888
- if (!gecko) updateDisplay(cm, {top: val});
3023
+ if (!gecko) updateDisplaySimple(cm, {top: val});
2889
3024
  if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = val;
2890
3025
  if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
2891
- if (gecko) updateDisplay(cm);
3026
+ if (gecko) updateDisplaySimple(cm);
2892
3027
  startWorker(cm, 100);
2893
3028
  }
2894
3029
  // Sync scroller and scrollbar, ensure the gutter elements are
@@ -2971,7 +3106,7 @@
2971
3106
  var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
2972
3107
  if (pixels < 0) top = Math.max(0, top + pixels - 50);
2973
3108
  else bot = Math.min(cm.doc.height, bot + pixels + 50);
2974
- updateDisplay(cm, {top: top, bottom: bot});
3109
+ updateDisplaySimple(cm, {top: top, bottom: bot});
2975
3110
  }
2976
3111
 
2977
3112
  if (wheelSamples < 20) {
@@ -3114,13 +3249,13 @@
3114
3249
  }
3115
3250
 
3116
3251
  function onKeyUp(e) {
3117
- if (signalDOMEvent(this, e)) return;
3118
3252
  if (e.keyCode == 16) this.doc.sel.shift = false;
3253
+ signalDOMEvent(this, e);
3119
3254
  }
3120
3255
 
3121
3256
  function onKeyPress(e) {
3122
3257
  var cm = this;
3123
- if (signalDOMEvent(cm, e) || e.ctrlKey || mac && e.metaKey) return;
3258
+ if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) return;
3124
3259
  var keyCode = e.keyCode, charCode = e.charCode;
3125
3260
  if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
3126
3261
  if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return;
@@ -3184,7 +3319,9 @@
3184
3319
  "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
3185
3320
  (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
3186
3321
  "; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
3322
+ if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2712)
3187
3323
  focusInput(cm);
3324
+ if (webkit) window.scrollTo(null, oldScrollY);
3188
3325
  resetInput(cm);
3189
3326
  // Adds "Select all" to context menu in FF
3190
3327
  if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
@@ -3414,7 +3551,7 @@
3414
3551
 
3415
3552
  antiChanges.push(historyChangeFromChange(doc, change));
3416
3553
 
3417
- var after = i ? computeSelAfterChange(doc, change, null) : lst(source);
3554
+ var after = i ? computeSelAfterChange(doc, change) : lst(source);
3418
3555
  makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
3419
3556
  if (!i && doc.cm) doc.cm.scrollIntoView(change);
3420
3557
  var rebased = [];
@@ -3473,7 +3610,7 @@
3473
3610
 
3474
3611
  change.removed = getBetween(doc, change.from, change.to);
3475
3612
 
3476
- if (!selAfter) selAfter = computeSelAfterChange(doc, change, null);
3613
+ if (!selAfter) selAfter = computeSelAfterChange(doc, change);
3477
3614
  if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
3478
3615
  else updateDoc(doc, change, spans);
3479
3616
  setSelectionNoUndo(doc, selAfter, sel_dontScroll);
@@ -3680,7 +3817,7 @@
3680
3817
  if (how == "smart") {
3681
3818
  // Fall back to "prev" when the mode doesn't have an indentation
3682
3819
  // method.
3683
- if (!cm.doc.mode.indent) how = "prev";
3820
+ if (!doc.mode.indent) how = "prev";
3684
3821
  else state = getStateBefore(cm, n);
3685
3822
  }
3686
3823
 
@@ -3692,8 +3829,8 @@
3692
3829
  indentation = 0;
3693
3830
  how = "not";
3694
3831
  } else if (how == "smart") {
3695
- indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
3696
- if (indentation == Pass) {
3832
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
3833
+ if (indentation == Pass || indentation > 150) {
3697
3834
  if (!aggressive) return;
3698
3835
  how = "prev";
3699
3836
  }
@@ -3716,7 +3853,7 @@
3716
3853
  if (pos < indentation) indentString += spaceStr(indentation - pos);
3717
3854
 
3718
3855
  if (indentString != curSpaceString) {
3719
- replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
3856
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
3720
3857
  } else {
3721
3858
  // Ensure that, if the cursor was in the whitespace at the start
3722
3859
  // of the line, it is moved to the end of that space.
@@ -4133,7 +4270,7 @@
4133
4270
 
4134
4271
  triggerOnKeyDown: methodOp(onKeyDown),
4135
4272
  triggerOnKeyPress: methodOp(onKeyPress),
4136
- triggerOnKeyUp: methodOp(onKeyUp),
4273
+ triggerOnKeyUp: onKeyUp,
4137
4274
 
4138
4275
  execCommand: function(cmd) {
4139
4276
  if (commands.hasOwnProperty(cmd))
@@ -4568,6 +4705,20 @@
4568
4705
  return {from: Pos(range.from().line, 0), to: range.from()};
4569
4706
  });
4570
4707
  },
4708
+ delWrappedLineLeft: function(cm) {
4709
+ deleteNearSelection(cm, function(range) {
4710
+ var top = cm.charCoords(range.head, "div").top + 5;
4711
+ var leftPos = cm.coordsChar({left: 0, top: top}, "div");
4712
+ return {from: leftPos, to: range.from()};
4713
+ });
4714
+ },
4715
+ delWrappedLineRight: function(cm) {
4716
+ deleteNearSelection(cm, function(range) {
4717
+ var top = cm.charCoords(range.head, "div").top + 5;
4718
+ var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
4719
+ return {from: range.from(), to: rightPos };
4720
+ });
4721
+ },
4571
4722
  undo: function(cm) {cm.undo();},
4572
4723
  redo: function(cm) {cm.redo();},
4573
4724
  undoSelection: function(cm) {cm.undoSelection();},
@@ -4705,11 +4856,11 @@
4705
4856
  };
4706
4857
  keyMap.macDefault = {
4707
4858
  "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
4708
- "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
4709
- "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delGroupBefore",
4859
+ "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
4860
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
4710
4861
  "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
4711
4862
  "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
4712
- "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft",
4863
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
4713
4864
  "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection",
4714
4865
  fallthrough: ["basic", "emacsy"]
4715
4866
  };
@@ -6960,6 +7111,8 @@
6960
7111
  for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
6961
7112
  };
6962
7113
 
7114
+ var orphanDelayedCallbacks = null;
7115
+
6963
7116
  // Often, we want to signal events at a point where we are in the
6964
7117
  // middle of some work, but don't want the handler to start calling
6965
7118
  // other methods on the editor, which might be in an inconsistent
@@ -6967,25 +7120,26 @@
6967
7120
  // signalLater looks whether there are any handlers, and schedules
6968
7121
  // them to be executed when the last operation ends, or, if no
6969
7122
  // operation is active, when a timeout fires.
6970
- var delayedCallbacks, delayedCallbackDepth = 0;
6971
7123
  function signalLater(emitter, type /*, values...*/) {
6972
7124
  var arr = emitter._handlers && emitter._handlers[type];
6973
7125
  if (!arr) return;
6974
- var args = Array.prototype.slice.call(arguments, 2);
6975
- if (!delayedCallbacks) {
6976
- ++delayedCallbackDepth;
6977
- delayedCallbacks = [];
6978
- setTimeout(fireDelayed, 0);
7126
+ var args = Array.prototype.slice.call(arguments, 2), list;
7127
+ if (operationGroup) {
7128
+ list = operationGroup.delayedCallbacks;
7129
+ } else if (orphanDelayedCallbacks) {
7130
+ list = orphanDelayedCallbacks;
7131
+ } else {
7132
+ list = orphanDelayedCallbacks = [];
7133
+ setTimeout(fireOrphanDelayed, 0);
6979
7134
  }
6980
7135
  function bnd(f) {return function(){f.apply(null, args);};};
6981
7136
  for (var i = 0; i < arr.length; ++i)
6982
- delayedCallbacks.push(bnd(arr[i]));
7137
+ list.push(bnd(arr[i]));
6983
7138
  }
6984
7139
 
6985
- function fireDelayed() {
6986
- --delayedCallbackDepth;
6987
- var delayed = delayedCallbacks;
6988
- delayedCallbacks = null;
7140
+ function fireOrphanDelayed() {
7141
+ var delayed = orphanDelayedCallbacks;
7142
+ orphanDelayedCallbacks = null;
6989
7143
  for (var i = 0; i < delayed.length; ++i) delayed[i]();
6990
7144
  }
6991
7145
 
@@ -7331,6 +7485,15 @@
7331
7485
  return typeof e.oncopy == "function";
7332
7486
  })();
7333
7487
 
7488
+ var badZoomedRects = null;
7489
+ function hasBadZoomedRects(measure) {
7490
+ if (badZoomedRects != null) return badZoomedRects;
7491
+ var node = removeChildrenAndAdd(measure, elt("span", "x"));
7492
+ var normal = node.getBoundingClientRect();
7493
+ var fromRange = range(node, 0, 1).getBoundingClientRect();
7494
+ return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
7495
+ }
7496
+
7334
7497
  // KEY NAMES
7335
7498
 
7336
7499
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
@@ -7632,7 +7795,7 @@
7632
7795
 
7633
7796
  // THE END
7634
7797
 
7635
- CodeMirror.version = "4.3.0";
7798
+ CodeMirror.version = "4.4.0";
7636
7799
 
7637
7800
  return CodeMirror;
7638
7801
  });