codemirror-rails 4.3 → 4.4

Sign up to get free protection for your applications and to get access to all the features.
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
  });