Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: Source/devtools/front_end/cm/codemirror.js

Issue 475393002: DevTools: [CodeMirror] roll codemirror to v4.4.1 (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: fix creation of editor instance for test Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « LayoutTests/inspector/editor/editor-test.js ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE 2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3 3
4 // This is CodeMirror (http://codemirror.net), a code editor 4 // This is CodeMirror (http://codemirror.net), a code editor
5 // implemented in JavaScript on top of the browser's DOM. 5 // implemented in JavaScript on top of the browser's DOM.
6 // 6 //
7 // You can find some technical background for some of the code below 7 // You can find some technical background for some of the code below
8 // at http://marijnhaverbeke.nl/blog/#cm-internals . 8 // at http://marijnhaverbeke.nl/blog/#cm-internals .
9 9
10 (function(mod) { 10 (function(mod) {
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 cm.curOp.forceUpdate = true; 101 cm.curOp.forceUpdate = true;
102 attachDoc(cm, doc); 102 attachDoc(cm, doc);
103 103
104 if ((options.autofocus && !mobile) || activeElt() == display.input) 104 if ((options.autofocus && !mobile) || activeElt() == display.input)
105 setTimeout(bind(onFocus, cm), 20); 105 setTimeout(bind(onFocus, cm), 20);
106 else 106 else
107 onBlur(cm); 107 onBlur(cm);
108 108
109 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt)) 109 for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
110 optionHandlers[opt](cm, options[opt], Init); 110 optionHandlers[opt](cm, options[opt], Init);
111 maybeUpdateLineNumberWidth(cm);
111 for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm); 112 for (var i = 0; i < initHooks.length; ++i) initHooks[i](cm);
112 }); 113 });
113 } 114 }
114 115
115 // DISPLAY CONSTRUCTOR 116 // DISPLAY CONSTRUCTOR
116 117
117 // The display handles the DOM integration, both for input reading 118 // The display handles the DOM integration, both for input reading
118 // and content drawing. It holds references to DOM nodes and 119 // and content drawing. It holds references to DOM nodes and
119 // display-related state. 120 // display-related state.
120 121
(...skipping 339 matching lines...) Expand 10 before | Expand all | Expand 10 after
460 operation(cm, onMouseDown)(e); 461 operation(cm, onMouseDown)(e);
461 }; 462 };
462 on(d.scrollbarV, "mousedown", barMouseDown); 463 on(d.scrollbarV, "mousedown", barMouseDown);
463 on(d.scrollbarH, "mousedown", barMouseDown); 464 on(d.scrollbarH, "mousedown", barMouseDown);
464 } 465 }
465 cm.state.checkedOverlayScrollbar = true; 466 cm.state.checkedOverlayScrollbar = true;
466 } 467 }
467 } 468 }
468 469
469 // Compute the lines that are visible in a given viewport (defaults 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 // height, and ensure (see op.scrollToPos) properties. 472 // height, and ensure (see op.scrollToPos) properties.
472 function visibleLines(display, doc, viewPort) { 473 function visibleLines(display, doc, viewport) {
473 var top = viewPort && viewPort.top != null ? Math.max(0, viewPort.top) : dis play.scroller.scrollTop; 474 var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : dis play.scroller.scrollTop;
474 top = Math.floor(top - paddingTop(display)); 475 top = Math.floor(top - paddingTop(display));
475 var bottom = viewPort && viewPort.bottom != null ? viewPort.bottom : top + d isplay.wrapper.clientHeight; 476 var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + d isplay.wrapper.clientHeight;
476 477
477 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); 478 var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
478 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and 479 // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
479 // forces those lines into the viewport (if possible). 480 // forces those lines into the viewport (if possible).
480 if (viewPort && viewPort.ensure) { 481 if (viewport && viewport.ensure) {
481 var ensureFrom = viewPort.ensure.from.line, ensureTo = viewPort.ensure.to. line; 482 var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to. line;
482 if (ensureFrom < from) 483 if (ensureFrom < from)
483 return {from: ensureFrom, 484 return {from: ensureFrom,
484 to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + d isplay.wrapper.clientHeight)}; 485 to: lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + d isplay.wrapper.clientHeight)};
485 if (Math.min(ensureTo, doc.lastLine()) >= to) 486 if (Math.min(ensureTo, doc.lastLine()) >= to)
486 return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - d isplay.wrapper.clientHeight), 487 return {from: lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - d isplay.wrapper.clientHeight),
487 to: ensureTo}; 488 to: ensureTo};
488 } 489 }
489 return {from: from, to: Math.max(to, from + 1)}; 490 return {from: from, to: Math.max(to, from + 1)};
490 } 491 }
491 492
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
536 537
537 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, 538 // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
538 // but using getBoundingClientRect to get a sub-pixel-accurate 539 // but using getBoundingClientRect to get a sub-pixel-accurate
539 // result. 540 // result.
540 function compensateForHScroll(display) { 541 function compensateForHScroll(display) {
541 return display.scroller.getBoundingClientRect().left - display.sizer.getBoun dingClientRect().left; 542 return display.scroller.getBoundingClientRect().left - display.sizer.getBoun dingClientRect().left;
542 } 543 }
543 544
544 // DISPLAY DRAWING 545 // DISPLAY DRAWING
545 546
546 // Updates the display, selection, and scrollbars, using the 547 function DisplayUpdate(cm, viewport, force) {
547 // information in display.view to find out which nodes are no longer 548 var display = cm.display;
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 549
559 // If the max line changed since it was last measured, measure it, 550 this.viewport = viewport;
560 // and ensure the document's width matches it. 551 // Store some values that we'll need later (but don't want to force a relayo ut for)
561 if (cm.display.maxLineChanged && !cm.options.lineWrapping) 552 this.visible = visibleLines(display, cm.doc, viewport);
562 adjustContentWidth(cm); 553 this.editorIsHidden = !display.wrapper.offsetWidth;
563 554 this.wrapperHeight = display.wrapper.clientHeight;
564 var barMeasure = measureForScrollbars(cm); 555 this.oldViewFrom = display.viewFrom; this.oldViewTo = display.viewTo;
565 updateSelection(cm); 556 this.oldScrollerWidth = display.scroller.clientWidth;
566 setDocumentHeight(cm, barMeasure); 557 this.force = force;
567 updateScrollbars(cm, barMeasure); 558 this.dims = getDimensions(cm);
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.cl ientWidth) {
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 - barMea sure.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 }
589
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.vi ewTo);
595 }
596 return updated;
597 } 559 }
598 560
599 // Does the actual updating of the line display. Bails out 561 // Does the actual updating of the line display. Bails out
600 // (returning false) when there is nothing to be done and forced is 562 // (returning false) when there is nothing to be done and forced is
601 // false. 563 // false.
602 function updateDisplayInner(cm, visible, forced) { 564 function updateDisplayIfNeeded(cm, update) {
603 var display = cm.display, doc = cm.doc; 565 var display = cm.display, doc = cm.doc;
604 if (!display.wrapper.offsetWidth) { 566 if (update.editorIsHidden) {
605 resetView(cm); 567 resetView(cm);
606 return; 568 return false;
607 } 569 }
608 570
609 // Bail out if the visible area is already rendered and nothing changed. 571 // Bail out if the visible area is already rendered and nothing changed.
610 if (!forced && visible.from >= display.viewFrom && visible.to <= display.vie wTo && 572 if (!update.force &&
573 update.visible.from >= display.viewFrom && update.visible.to <= display. viewTo &&
611 (display.updateLineNumbers == null || display.updateLineNumbers >= displ ay.viewTo) && 574 (display.updateLineNumbers == null || display.updateLineNumbers >= displ ay.viewTo) &&
612 countDirtyView(cm) == 0) 575 countDirtyView(cm) == 0)
613 return; 576 return false;
614 577
615 if (maybeUpdateLineNumberWidth(cm)) 578 if (maybeUpdateLineNumberWidth(cm)) {
616 resetView(cm); 579 resetView(cm);
617 var dims = getDimensions(cm); 580 update.dims = getDimensions(cm);
581 }
618 582
619 // Compute a suitable new viewport (from & to) 583 // Compute a suitable new viewport (from & to)
620 var end = doc.first + doc.size; 584 var end = doc.first + doc.size;
621 var from = Math.max(visible.from - cm.options.viewportMargin, doc.first); 585 var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.fir st);
622 var to = Math.min(end, visible.to + cm.options.viewportMargin); 586 var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
623 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max (doc.first, display.viewFrom); 587 if (display.viewFrom < from && from - display.viewFrom < 20) from = Math.max (doc.first, display.viewFrom);
624 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, disp lay.viewTo); 588 if (display.viewTo > to && display.viewTo - to < 20) to = Math.min(end, disp lay.viewTo);
625 if (sawCollapsedSpans) { 589 if (sawCollapsedSpans) {
626 from = visualLineNo(cm.doc, from); 590 from = visualLineNo(cm.doc, from);
627 to = visualLineEndNo(cm.doc, to); 591 to = visualLineEndNo(cm.doc, to);
628 } 592 }
629 593
630 var different = from != display.viewFrom || to != display.viewTo || 594 var different = from != display.viewFrom || to != display.viewTo ||
631 display.lastSizeC != display.wrapper.clientHeight; 595 display.lastSizeC != update.wrapperHeight;
632 adjustView(cm, from, to); 596 adjustView(cm, from, to);
633 597
634 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); 598 display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
635 // Position the mover div to align with the current scroll position 599 // Position the mover div to align with the current scroll position
636 cm.display.mover.style.top = display.viewOffset + "px"; 600 cm.display.mover.style.top = display.viewOffset + "px";
637 601
638 var toUpdate = countDirtyView(cm); 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 >= displ ay.viewTo))
605 return false;
640 606
641 // For big changes, we hide the enclosing element during the 607 // For big changes, we hide the enclosing element during the
642 // update, since that speeds up the operations on most browsers. 608 // update, since that speeds up the operations on most browsers.
643 var focused = activeElt(); 609 var focused = activeElt();
644 if (toUpdate > 4) display.lineDiv.style.display = "none"; 610 if (toUpdate > 4) display.lineDiv.style.display = "none";
645 patchDisplay(cm, display.updateLineNumbers, dims); 611 patchDisplay(cm, display.updateLineNumbers, update.dims);
646 if (toUpdate > 4) display.lineDiv.style.display = ""; 612 if (toUpdate > 4) display.lineDiv.style.display = "";
647 // There might have been a widget with a focused element that got 613 // There might have been a widget with a focused element that got
648 // hidden or updated, if so re-focus it. 614 // hidden or updated, if so re-focus it.
649 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus (); 615 if (focused && activeElt() != focused && focused.offsetHeight) focused.focus ();
650 616
651 // Prevent selection and cursors from interfering with the scroll 617 // Prevent selection and cursors from interfering with the scroll
652 // width. 618 // width.
653 removeChildren(display.cursorDiv); 619 removeChildren(display.cursorDiv);
654 removeChildren(display.selectionDiv); 620 removeChildren(display.selectionDiv);
655 621
656 if (different) { 622 if (different) {
657 display.lastSizeC = display.wrapper.clientHeight; 623 display.lastSizeC = update.wrapperHeight;
658 startWorker(cm, 400); 624 startWorker(cm, 400);
659 } 625 }
660 626
661 updateHeightsInViewport(cm); 627 display.updateLineNumbers = null;
662 628
663 return true; 629 return true;
664 } 630 }
665 631
666 function adjustContentWidth(cm) { 632 function postUpdateDisplay(cm, update) {
667 var display = cm.display; 633 var force = update.force, viewport = update.viewport;
668 var width = measureChar(cm, display.maxLine, display.maxLine.text.length).le ft; 634 for (var first = true;; first = false) {
669 display.maxLineChanged = false; 635 if (first && cm.options.lineWrapping && update.oldScrollerWidth != cm.disp lay.scroller.clientWidth) {
670 var minWidth = Math.max(0, width + 3); 636 force = true;
671 var maxScrollLeft = Math.max(0, display.sizer.offsetLeft + minWidth + scroll erCutOff - display.scroller.clientWidth); 637 } else {
672 display.sizer.style.minWidth = minWidth + "px"; 638 force = false;
673 if (maxScrollLeft < cm.doc.scrollLeft) 639 // Clip forced viewport to actual scrollable area.
674 setScrollLeft(cm, Math.min(display.scroller.scrollLeft, maxScrollLeft), tr ue); 640 if (viewport && viewport.top != null)
641 viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - sc rollerCutOff -
642 cm.display.scroller.clientHeight, viewport.t op)};
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 <= c m.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.view To);
660 }
661
662 function updateDisplaySimple(cm, viewport) {
663 var update = new DisplayUpdate(cm, viewport);
664 if (updateDisplayIfNeeded(cm, update)) {
665 updateHeightsInViewport(cm);
666 postUpdateDisplay(cm, update);
667 var barMeasure = measureForScrollbars(cm);
668 updateSelection(cm);
669 setDocumentHeight(cm, barMeasure);
670 updateScrollbars(cm, barMeasure);
671 }
675 } 672 }
676 673
677 function setDocumentHeight(cm, measure) { 674 function setDocumentHeight(cm, measure) {
678 cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measu re.docHeight + "px"; 675 cm.display.sizer.style.minHeight = cm.display.heightForcer.style.top = measu re.docHeight + "px";
679 cm.display.gutters.style.height = Math.max(measure.docHeight, measure.client Height - scrollerCutOff) + "px"; 676 cm.display.gutters.style.height = Math.max(measure.docHeight, measure.client Height - scrollerCutOff) + "px";
680 } 677 }
681 678
682 function checkForWebkitWidthBug(cm, measure) { 679 function checkForWebkitWidthBug(cm, measure) {
683 // Work around Webkit bug where it sometimes reserves space for a 680 // Work around Webkit bug where it sometimes reserves space for a
684 // non-existing phantom scrollbar in the scroller (Issue #2420) 681 // non-existing phantom scrollbar in the scroller (Issue #2420)
(...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after
1250 } 1247 }
1251 } 1248 }
1252 } 1249 }
1253 return curPos; 1250 return curPos;
1254 } 1251 }
1255 } 1252 }
1256 1253
1257 // SELECTION DRAWING 1254 // SELECTION DRAWING
1258 1255
1259 // Redraw the selection and/or cursor 1256 // Redraw the selection and/or cursor
1260 function updateSelection(cm) { 1257 function drawSelection(cm) {
1261 var display = cm.display, doc = cm.doc; 1258 var display = cm.display, doc = cm.doc, result = {};
1262 var curFragment = document.createDocumentFragment(); 1259 var curFragment = result.cursors = document.createDocumentFragment();
1263 var selFragment = document.createDocumentFragment(); 1260 var selFragment = result.selection = document.createDocumentFragment();
1264 1261
1265 for (var i = 0; i < doc.sel.ranges.length; i++) { 1262 for (var i = 0; i < doc.sel.ranges.length; i++) {
1266 var range = doc.sel.ranges[i]; 1263 var range = doc.sel.ranges[i];
1267 var collapsed = range.empty(); 1264 var collapsed = range.empty();
1268 if (collapsed || cm.options.showCursorWhenSelecting) 1265 if (collapsed || cm.options.showCursorWhenSelecting)
1269 drawSelectionCursor(cm, range, curFragment); 1266 drawSelectionCursor(cm, range, curFragment);
1270 if (!collapsed) 1267 if (!collapsed)
1271 drawSelectionRange(cm, range, selFragment); 1268 drawSelectionRange(cm, range, selFragment);
1272 } 1269 }
1273 1270
1274 // Move the hidden textarea near the cursor to prevent scrolling artifacts 1271 // Move the hidden textarea near the cursor to prevent scrolling artifacts
1275 if (cm.options.moveInputWithCursor) { 1272 if (cm.options.moveInputWithCursor) {
1276 var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); 1273 var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
1277 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.l ineDiv.getBoundingClientRect(); 1274 var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.l ineDiv.getBoundingClientRect();
1278 var top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, 1275 result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
1279 headPos.top + lineOff.top - wrapOff.top)); 1276 headPos.top + lineOff.top - wrapOff.to p));
1280 var left = Math.max(0, Math.min(display.wrapper.clientWidth - 10, 1277 result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
1281 headPos.left + lineOff.left - wrapOff.left )); 1278 headPos.left + lineOff.left - wrapOff .left));
1282 display.inputDiv.style.top = top + "px";
1283 display.inputDiv.style.left = left + "px";
1284 } 1279 }
1285 1280
1286 removeChildrenAndAdd(display.cursorDiv, curFragment); 1281 return result;
1287 removeChildrenAndAdd(display.selectionDiv, selFragment); 1282 }
1283
1284 function showSelection(cm, drawn) {
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 }
1291 }
1292
1293 function updateSelection(cm) {
1294 showSelection(cm, drawSelection(cm));
1288 } 1295 }
1289 1296
1290 // Draws a cursor for the given range 1297 // Draws a cursor for the given range
1291 function drawSelectionCursor(cm, range, output) { 1298 function drawSelectionCursor(cm, range, output) {
1292 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.single CursorHeightPerLine); 1299 var pos = cursorCoords(cm, range.head, "div", null, null, !cm.options.single CursorHeightPerLine);
1293 1300
1294 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); 1301 var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
1295 cursor.style.left = pos.left + "px"; 1302 cursor.style.left = pos.left + "px";
1296 cursor.style.top = pos.top + "px"; 1303 cursor.style.top = pos.top + "px";
1297 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH eight + "px"; 1304 cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorH eight + "px";
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
1401 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo) 1408 if (cm.doc.mode.startState && cm.doc.frontier < cm.display.viewTo)
1402 cm.state.highlight.set(time, bind(highlightWorker, cm)); 1409 cm.state.highlight.set(time, bind(highlightWorker, cm));
1403 } 1410 }
1404 1411
1405 function highlightWorker(cm) { 1412 function highlightWorker(cm) {
1406 var doc = cm.doc; 1413 var doc = cm.doc;
1407 if (doc.frontier < doc.first) doc.frontier = doc.first; 1414 if (doc.frontier < doc.first) doc.frontier = doc.first;
1408 if (doc.frontier >= cm.display.viewTo) return; 1415 if (doc.frontier >= cm.display.viewTo) return;
1409 var end = +new Date + cm.options.workTime; 1416 var end = +new Date + cm.options.workTime;
1410 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier)); 1417 var state = copyState(doc.mode, getStateBefore(cm, doc.frontier));
1418 var changedLines = [];
1411 1419
1412 runInOp(cm, function() {
1413 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50 0), function(line) { 1420 doc.iter(doc.frontier, Math.min(doc.first + doc.size, cm.display.viewTo + 50 0), function(line) {
1414 if (doc.frontier >= cm.display.viewFrom) { // Visible 1421 if (doc.frontier >= cm.display.viewFrom) { // Visible
1415 var oldStyles = line.styles; 1422 var oldStyles = line.styles;
1416 var highlighted = highlightLine(cm, line, state, true); 1423 var highlighted = highlightLine(cm, line, state, true);
1417 line.styles = highlighted.styles; 1424 line.styles = highlighted.styles;
1418 var oldCls = line.styleClasses, newCls = highlighted.classes; 1425 var oldCls = line.styleClasses, newCls = highlighted.classes;
1419 if (newCls) line.styleClasses = newCls; 1426 if (newCls) line.styleClasses = newCls;
1420 else if (oldCls) line.styleClasses = null; 1427 else if (oldCls) line.styleClasses = null;
1421 var ischange = !oldStyles || oldStyles.length != line.styles.length || 1428 var ischange = !oldStyles || oldStyles.length != line.styles.length ||
1422 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg Class || oldCls.textClass != newCls.textClass); 1429 oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bg Class || oldCls.textClass != newCls.textClass);
1423 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt yles[i] != line.styles[i]; 1430 for (var i = 0; !ischange && i < oldStyles.length; ++i) ischange = oldSt yles[i] != line.styles[i];
1424 if (ischange) regLineChange(cm, doc.frontier, "text"); 1431 if (ischange) changedLines.push(doc.frontier);
1425 line.stateAfter = copyState(doc.mode, state); 1432 line.stateAfter = copyState(doc.mode, state);
1426 } else { 1433 } else {
1427 processLine(cm, line.text, state); 1434 processLine(cm, line.text, state);
1428 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n ull; 1435 line.stateAfter = doc.frontier % 5 == 0 ? copyState(doc.mode, state) : n ull;
1429 } 1436 }
1430 ++doc.frontier; 1437 ++doc.frontier;
1431 if (+new Date > end) { 1438 if (+new Date > end) {
1432 startWorker(cm, cm.options.workDelay); 1439 startWorker(cm, cm.options.workDelay);
1433 return true; 1440 return true;
1434 } 1441 }
1435 }); 1442 });
1443 if (changedLines.length) runInOp(cm, function() {
1444 for (var i = 0; i < changedLines.length; i++)
1445 regLineChange(cm, changedLines[i], "text");
1436 }); 1446 });
1437 } 1447 }
1438 1448
1439 // Finds the line to start with when starting a parse. Tries to 1449 // Finds the line to start with when starting a parse. Tries to
1440 // find a line with a stateAfter, so that it can start with a 1450 // find a line with a stateAfter, so that it can start with a
1441 // valid state. If that fails, it returns the line with the 1451 // valid state. If that fails, it returns the line with the
1442 // smallest indentation, which tends to need the least context to 1452 // smallest indentation, which tends to need the least context to
1443 // parse correctly. 1453 // parse correctly.
1444 function findStartLine(cm, n, precise) { 1454 function findStartLine(cm, n, precise) {
1445 var minindent, minline, doc = cm.doc; 1455 var minindent, minline, doc = cm.doc;
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1658 rect = node.getBoundingClientRect(); 1668 rect = node.getBoundingClientRect();
1659 } 1669 }
1660 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { 1670 if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
1661 var rSpan = node.parentNode.getClientRects()[0]; 1671 var rSpan = node.parentNode.getClientRects()[0];
1662 if (rSpan) 1672 if (rSpan)
1663 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top : rSpan.top, bottom: rSpan.bottom}; 1673 rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top : rSpan.top, bottom: rSpan.bottom};
1664 else 1674 else
1665 rect = nullRect; 1675 rect = nullRect;
1666 } 1676 }
1667 1677
1678 if (ie && ie_version < 11) rect = maybeUpdateRectForZooming(cm.display.measu re, rect);
1679
1668 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect. top; 1680 var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect. top;
1669 var mid = (rtop + rbot) / 2; 1681 var mid = (rtop + rbot) / 2;
1670 var heights = prepared.view.measure.heights; 1682 var heights = prepared.view.measure.heights;
1671 for (var i = 0; i < heights.length - 1; i++) 1683 for (var i = 0; i < heights.length - 1; i++)
1672 if (mid < heights[i]) break; 1684 if (mid < heights[i]) break;
1673 var top = i ? heights[i - 1] : 0, bot = heights[i]; 1685 var top = i ? heights[i - 1] : 0, bot = heights[i];
1674 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepare d.rect.left, 1686 var result = {left: (collapse == "right" ? rect.right : rect.left) - prepare d.rect.left,
1675 right: (collapse == "left" ? rect.left : rect.right) - prepare d.rect.left, 1687 right: (collapse == "left" ? rect.left : rect.right) - prepare d.rect.left,
1676 top: top, bottom: bot}; 1688 top: top, bottom: bot};
1677 if (!rect.left && !rect.right) result.bogus = true; 1689 if (!rect.left && !rect.right) result.bogus = true;
1678 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbot tom = rbot; } 1690 if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbot tom = rbot; }
1691
1679 return result; 1692 return result;
1680 } 1693 }
1681 1694
1695 // Work around problem with bounding client rects on ranges being
1696 // returned incorrectly when zoomed on IE10 and below.
1697 function maybeUpdateRectForZooming(measure, rect) {
1698 if (!window.screen || screen.logicalXDPI == null ||
1699 screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
1700 return rect;
1701 var scaleX = screen.logicalXDPI / screen.deviceXDPI;
1702 var scaleY = screen.logicalYDPI / screen.deviceYDPI;
1703 return {left: rect.left * scaleX, right: rect.right * scaleX,
1704 top: rect.top * scaleY, bottom: rect.bottom * scaleY};
1705 }
1706
1682 function clearLineMeasurementCacheFor(lineView) { 1707 function clearLineMeasurementCacheFor(lineView) {
1683 if (lineView.measure) { 1708 if (lineView.measure) {
1684 lineView.measure.cache = {}; 1709 lineView.measure.cache = {};
1685 lineView.measure.heights = null; 1710 lineView.measure.heights = null;
1686 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++) 1711 if (lineView.rest) for (var i = 0; i < lineView.rest.length; i++)
1687 lineView.measure.caches[i] = {}; 1712 lineView.measure.caches[i] = {};
1688 } 1713 }
1689 } 1714 }
1690 1715
1691 function clearLineMeasurementCache(cm) { 1716 function clearLineMeasurementCache(cm) {
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1904 } 1929 }
1905 1930
1906 // OPERATIONS 1931 // OPERATIONS
1907 1932
1908 // Operations are used to wrap a series of changes to the editor 1933 // Operations are used to wrap a series of changes to the editor
1909 // state in such a way that each change won't have to update the 1934 // state in such a way that each change won't have to update the
1910 // cursor and display (which would be awkward, slow, and 1935 // cursor and display (which would be awkward, slow, and
1911 // error-prone). Instead, display updates are batched and then all 1936 // error-prone). Instead, display updates are batched and then all
1912 // combined and executed at once. 1937 // combined and executed at once.
1913 1938
1939 var operationGroup = null;
1940
1914 var nextOpId = 0; 1941 var nextOpId = 0;
1915 // Start a new operation. 1942 // Start a new operation.
1916 function startOperation(cm) { 1943 function startOperation(cm) {
1917 cm.curOp = { 1944 cm.curOp = {
1945 cm: cm,
1918 viewChanged: false, // Flag that indicates that lines might need to b e redrawn 1946 viewChanged: false, // Flag that indicates that lines might need to b e redrawn
1919 startHeight: cm.doc.height, // Used to detect need to update scrollbar 1947 startHeight: cm.doc.height, // Used to detect need to update scrollbar
1920 forceUpdate: false, // Used to force a redraw 1948 forceUpdate: false, // Used to force a redraw
1921 updateInput: null, // Whether to reset the input textarea 1949 updateInput: null, // Whether to reset the input textarea
1922 typing: false, // Whether this reset should be careful to leave existing text (for compositing) 1950 typing: false, // Whether this reset should be careful to leave existing text (for compositing)
1923 changeObjs: null, // Accumulated changes, for firing change events 1951 changeObjs: null, // Accumulated changes, for firing change events
1924 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on 1952 cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
1953 cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
1925 selectionChanged: false, // Whether the selection needs to be redrawn 1954 selectionChanged: false, // Whether the selection needs to be redrawn
1926 updateMaxLine: false, // Set when the widest line needs to be determine d anew 1955 updateMaxLine: false, // Set when the widest line needs to be determine d anew
1927 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu shed to DOM yet 1956 scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pu shed to DOM yet
1928 scrollToPos: null, // Used to scroll to a specific position 1957 scrollToPos: null, // Used to scroll to a specific position
1929 id: ++nextOpId // Unique ID 1958 id: ++nextOpId // Unique ID
1930 }; 1959 };
1931 if (!delayedCallbackDepth++) delayedCallbacks = []; 1960 if (operationGroup) {
1961 operationGroup.ops.push(cm.curOp);
1962 } else {
1963 cm.curOp.ownsGroup = operationGroup = {
1964 ops: [cm.curOp],
1965 delayedCallbacks: []
1966 };
1967 }
1968 }
1969
1970 function fireCallbacksForOps(group) {
1971 // Calls delayed callbacks and cursorActivity handlers until no
1972 // new ones appear
1973 var callbacks = group.delayedCallbacks, i = 0;
1974 do {
1975 for (; i < callbacks.length; i++)
1976 callbacks[i]();
1977 for (var j = 0; j < group.ops.length; j++) {
1978 var op = group.ops[j];
1979 if (op.cursorActivityHandlers)
1980 while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
1981 op.cursorActivityHandlers[op.cursorActivityCalled++](op.cm);
1982 }
1983 } while (i < callbacks.length);
1932 } 1984 }
1933 1985
1934 // Finish an operation, updating the display and signalling delayed events 1986 // Finish an operation, updating the display and signalling delayed events
1935 function endOperation(cm) { 1987 function endOperation(cm) {
1936 var op = cm.curOp, doc = cm.doc, display = cm.display; 1988 var op = cm.curOp, group = op.ownsGroup;
1937 cm.curOp = null; 1989 if (!group) return;
1938 1990
1991 try { fireCallbacksForOps(group); }
1992 finally {
1993 operationGroup = null;
1994 for (var i = 0; i < group.ops.length; i++)
1995 group.ops[i].cm.curOp = null;
1996 endOperations(group);
1997 }
1998 }
1999
2000 // The DOM updates done when an operation finishes are batched so
2001 // that the minimum number of relayouts are required.
2002 function endOperations(group) {
2003 var ops = group.ops;
2004 for (var i = 0; i < ops.length; i++) // Read DOM
2005 endOperation_R1(ops[i]);
2006 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
2007 endOperation_W1(ops[i]);
2008 for (var i = 0; i < ops.length; i++) // Read DOM
2009 endOperation_R2(ops[i]);
2010 for (var i = 0; i < ops.length; i++) // Write DOM (maybe)
2011 endOperation_W2(ops[i]);
2012 for (var i = 0; i < ops.length; i++) // Read DOM
2013 endOperation_finish(ops[i]);
2014 }
2015
2016 function endOperation_R1(op) {
2017 var cm = op.cm, display = cm.display;
1939 if (op.updateMaxLine) findMaxLine(cm); 2018 if (op.updateMaxLine) findMaxLine(cm);
1940 2019
1941 // If it looks like an update might be needed, call updateDisplay 2020 op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
1942 if (op.viewChanged || op.forceUpdate || op.scrollTop != null || 2021 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
1943 op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || 2022 op.scrollToPos.to.line >= display.viewTo) ||
1944 op.scrollToPos.to.line >= display.viewTo) || 2023 display.maxLineChanged && cm.options.lineWrapping;
1945 display.maxLineChanged && cm.options.lineWrapping) { 2024 op.update = op.mustUpdate &&
1946 var updated = updateDisplay(cm, {top: op.scrollTop, ensure: op.scrollToPos }, op.forceUpdate); 2025 new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scro llToPos}, op.forceUpdate);
1947 if (cm.display.scroller.offsetHeight) cm.doc.scrollTop = cm.display.scroll er.scrollTop; 2026 }
2027
2028 function endOperation_W1(op) {
2029 op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update) ;
2030 }
2031
2032 function endOperation_R2(op) {
2033 var cm = op.cm, display = cm.display;
2034 if (op.updatedDisplay) updateHeightsInViewport(cm);
2035
2036 op.barMeasure = measureForScrollbars(cm);
2037
2038 // If the max line changed since it was last measured, measure it,
2039 // and ensure the document's width matches it.
2040 // updateDisplay_W2 will use these properties to do the actual resizing
2041 if (display.maxLineChanged && !cm.options.lineWrapping) {
2042 op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.l ength).left + 3;
2043 op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo +
2044 scrollerCutOff - display.scroller.clientWidth) ;
1948 } 2045 }
1949 // If no update was run, but the selection changed, redraw that. 2046
1950 if (!updated && op.selectionChanged) updateSelection(cm); 2047 if (op.updatedDisplay || op.selectionChanged)
1951 if (!updated && op.startHeight != cm.doc.height) updateScrollbars(cm); 2048 op.newSelectionNodes = drawSelection(cm);
2049 }
2050
2051 function endOperation_W2(op) {
2052 var cm = op.cm;
2053
2054 if (op.adjustWidthTo != null) {
2055 cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
2056 if (op.maxScrollLeft < cm.doc.scrollLeft)
2057 setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollL eft), true);
2058 cm.display.maxLineChanged = false;
2059 }
2060
2061 if (op.newSelectionNodes)
2062 showSelection(cm, op.newSelectionNodes);
2063 if (op.updatedDisplay)
2064 setDocumentHeight(cm, op.barMeasure);
2065 if (op.updatedDisplay || op.startHeight != cm.doc.height)
2066 updateScrollbars(cm, op.barMeasure);
2067
2068 if (op.selectionChanged) restartBlink(cm);
2069
2070 if (cm.state.focused && op.updateInput)
2071 resetInput(cm, op.typing);
2072 }
2073
2074 function endOperation_finish(op) {
2075 var cm = op.cm, display = cm.display, doc = cm.doc;
2076
2077 if (op.adjustWidthTo != null && Math.abs(op.barMeasure.scrollWidth - cm.disp lay.scroller.scrollWidth) > 1)
2078 updateScrollbars(cm);
2079
2080 if (op.updatedDisplay) postUpdateDisplay(cm, op.update);
1952 2081
1953 // Abort mouse wheel delta measurement, when scrolling explicitly 2082 // Abort mouse wheel delta measurement, when scrolling explicitly
1954 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) 2083 if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
1955 display.wheelStartX = display.wheelStartY = null; 2084 display.wheelStartX = display.wheelStartY = null;
1956 2085
1957 // Propagate the scroll position to the actual DOM scroller 2086 // Propagate the scroll position to the actual DOM scroller
1958 if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) { 2087 if (op.scrollTop != null && display.scroller.scrollTop != op.scrollTop) {
1959 var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scr oller.clientHeight, op.scrollTop)); 2088 var top = Math.max(0, Math.min(display.scroller.scrollHeight - display.scr oller.clientHeight, op.scrollTop));
1960 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top; 2089 display.scroller.scrollTop = display.scrollbarV.scrollTop = doc.scrollTop = top;
1961 } 2090 }
1962 if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) { 2091 if (op.scrollLeft != null && display.scroller.scrollLeft != op.scrollLeft) {
1963 var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scr oller.clientWidth, op.scrollLeft)); 2092 var left = Math.max(0, Math.min(display.scroller.scrollWidth - display.scr oller.clientWidth, op.scrollLeft));
1964 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLe ft = left; 2093 display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLe ft = left;
1965 alignHorizontally(cm); 2094 alignHorizontally(cm);
1966 } 2095 }
1967 // If we need to scroll a specific position into view, do so. 2096 // If we need to scroll a specific position into view, do so.
1968 if (op.scrollToPos) { 2097 if (op.scrollToPos) {
1969 var coords = scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from), 2098 var coords = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
1970 clipPos(cm.doc, op.scrollToPos.to), op.scro llToPos.margin); 2099 clipPos(doc, op.scrollToPos.to), op.scrollT oPos.margin);
1971 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo rds); 2100 if (op.scrollToPos.isCursor && cm.state.focused) maybeScrollWindow(cm, coo rds);
1972 } 2101 }
1973 2102
1974 if (op.selectionChanged) restartBlink(cm);
1975
1976 if (cm.state.focused && op.updateInput)
1977 resetInput(cm, op.typing);
1978
1979 // Fire events for markers that are hidden/unidden by editing or 2103 // Fire events for markers that are hidden/unidden by editing or
1980 // undoing 2104 // undoing
1981 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; 2105 var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
1982 if (hidden) for (var i = 0; i < hidden.length; ++i) 2106 if (hidden) for (var i = 0; i < hidden.length; ++i)
1983 if (!hidden[i].lines.length) signal(hidden[i], "hide"); 2107 if (!hidden[i].lines.length) signal(hidden[i], "hide");
1984 if (unhidden) for (var i = 0; i < unhidden.length; ++i) 2108 if (unhidden) for (var i = 0; i < unhidden.length; ++i)
1985 if (unhidden[i].lines.length) signal(unhidden[i], "unhide"); 2109 if (unhidden[i].lines.length) signal(unhidden[i], "unhide");
1986 2110
1987 var delayed; 2111 if (display.wrapper.offsetHeight)
1988 if (!--delayedCallbackDepth) { 2112 doc.scrollTop = cm.display.scroller.scrollTop;
1989 delayed = delayedCallbacks; 2113
1990 delayedCallbacks = null; 2114 // Apply workaround for two webkit bugs
2115 if (op.updatedDisplay && webkit) {
2116 if (cm.options.lineWrapping)
2117 checkForWebkitWidthBug(cm, op.barMeasure); // (Issue #2420)
2118 if (op.barMeasure.scrollWidth > op.barMeasure.clientWidth &&
2119 op.barMeasure.scrollWidth < op.barMeasure.clientWidth + 1 &&
2120 !hScrollbarTakesSpace(cm))
2121 updateScrollbars(cm); // (Issue #2562)
1991 } 2122 }
2123
1992 // Fire change events, and delayed event handlers 2124 // Fire change events, and delayed event handlers
1993 if (op.changeObjs) 2125 if (op.changeObjs)
1994 signal(cm, "changes", cm, op.changeObjs); 2126 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 } 2127 }
2000 2128
2001 // Run the given function in an operation 2129 // Run the given function in an operation
2002 function runInOp(cm, f) { 2130 function runInOp(cm, f) {
2003 if (cm.curOp) return f(); 2131 if (cm.curOp) return f();
2004 startOperation(cm); 2132 startOperation(cm);
2005 try { return f(); } 2133 try { return f(); }
2006 finally { endOperation(cm); } 2134 finally { endOperation(cm); }
2007 } 2135 }
2008 // Wraps a function in an operation. Returns the wrapped function. 2136 // Wraps a function in an operation. Returns the wrapped function.
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
2240 var missed = false; 2368 var missed = false;
2241 cm.display.pollingFast = true; 2369 cm.display.pollingFast = true;
2242 function p() { 2370 function p() {
2243 var changed = readInput(cm); 2371 var changed = readInput(cm);
2244 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);} 2372 if (!changed && !missed) {missed = true; cm.display.poll.set(60, p);}
2245 else {cm.display.pollingFast = false; slowPoll(cm);} 2373 else {cm.display.pollingFast = false; slowPoll(cm);}
2246 } 2374 }
2247 cm.display.poll.set(20, p); 2375 cm.display.poll.set(20, p);
2248 } 2376 }
2249 2377
2378 // This will be set to an array of strings when copying, so that,
2379 // when pasting, we know what kind of selections the copied text
2380 // was made out of.
2381 var lastCopied = null;
2382
2250 // Read input from the textarea, and update the document to match. 2383 // Read input from the textarea, and update the document to match.
2251 // When something is selected, it is present in the textarea, and 2384 // When something is selected, it is present in the textarea, and
2252 // selected (unless it is huge, in which case a placeholder is 2385 // selected (unless it is huge, in which case a placeholder is
2253 // used). When nothing is selected, the cursor sits after previously 2386 // used). When nothing is selected, the cursor sits after previously
2254 // seen text (can be empty), which is stored in prevInput (we must 2387 // seen text (can be empty), which is stored in prevInput (we must
2255 // not reset the textarea when typing, because that breaks IME). 2388 // not reset the textarea when typing, because that breaks IME).
2256 function readInput(cm) { 2389 function readInput(cm) {
2257 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc ; 2390 var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc ;
2258 // Since this is called a *lot*, try to bail out as cheaply as 2391 // Since this is called a *lot*, try to bail out as cheaply as
2259 // possible when it is clear that nothing happened. hasSelection 2392 // possible when it is clear that nothing happened. hasSelection
2260 // will be the case when there is a lot of text in the textarea, 2393 // will be the case when there is a lot of text in the textarea,
2261 // in which case reading its value would be expensive. 2394 // in which case reading its value would be expensive.
2262 if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(c m) || cm.options.disableInput) 2395 if (!cm.state.focused || (hasSelection(input) && !prevInput) || isReadOnly(c m) || cm.options.disableInput)
2263 return false; 2396 return false;
2264 // See paste handler for more on the fakedLastChar kludge 2397 // See paste handler for more on the fakedLastChar kludge
2265 if (cm.state.pasteIncoming && cm.state.fakedLastChar) { 2398 if (cm.state.pasteIncoming && cm.state.fakedLastChar) {
2266 input.value = input.value.substring(0, input.value.length - 1); 2399 input.value = input.value.substring(0, input.value.length - 1);
2267 cm.state.fakedLastChar = false; 2400 cm.state.fakedLastChar = false;
2268 } 2401 }
2269 var text = input.value; 2402 var text = input.value;
2270 // If nothing changed, bail. 2403 // If nothing changed, bail.
2271 if (text == prevInput && !cm.somethingSelected()) return false; 2404 if (text == prevInput && !cm.somethingSelected()) return false;
2272 // Work around nonsensical selection resetting in IE9/10 2405 // Work around nonsensical selection resetting in IE9/10, and
2273 if (ie && ie_version >= 9 && cm.display.inputHasSelection === text) { 2406 // inexplicable appearance of private area unicode characters on
2407 // some key combos in Mac (#2689).
2408 if (ie && ie_version >= 9 && cm.display.inputHasSelection === text ||
2409 mac && /[\uf700-\uf7ff]/.test(text)) {
2274 resetInput(cm); 2410 resetInput(cm);
2275 return false; 2411 return false;
2276 } 2412 }
2277 2413
2278 var withOp = !cm.curOp; 2414 var withOp = !cm.curOp;
2279 if (withOp) startOperation(cm); 2415 if (withOp) startOperation(cm);
2280 cm.display.shift = false; 2416 cm.display.shift = false;
2281 2417
2282 if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput) 2418 if (text.charCodeAt(0) == 0x200b && doc.sel == cm.display.selForContextMenu && !prevInput)
2283 prevInput = "\u200b"; 2419 prevInput = "\u200b";
2284 // Find the part of the input that is actually new 2420 // Find the part of the input that is actually new
2285 var same = 0, l = Math.min(prevInput.length, text.length); 2421 var same = 0, l = Math.min(prevInput.length, text.length);
2286 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++sa me; 2422 while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++sa me;
2287 var inserted = text.slice(same), textLines = splitLines(inserted); 2423 var inserted = text.slice(same), textLines = splitLines(inserted);
2288 2424
2289 // When pasing N lines into N selections, insert one line per selection 2425 // When pasing N lines into N selections, insert one line per selection
2290 var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.r anges.length == textLines.length; 2426 var multiPaste = null;
2427 if (cm.state.pasteIncoming && doc.sel.ranges.length > 1) {
2428 if (lastCopied && lastCopied.join("\n") == inserted)
2429 multiPaste = doc.sel.ranges.length % lastCopied.length == 0 && map(lastC opied, splitLines);
2430 else if (textLines.length == doc.sel.ranges.length)
2431 multiPaste = map(textLines, function(l) { return [l]; });
2432 }
2291 2433
2292 // Normal behavior is to insert the new text into every selection 2434 // Normal behavior is to insert the new text into every selection
2293 for (var i = doc.sel.ranges.length - 1; i >= 0; i--) { 2435 for (var i = doc.sel.ranges.length - 1; i >= 0; i--) {
2294 var range = doc.sel.ranges[i]; 2436 var range = doc.sel.ranges[i];
2295 var from = range.from(), to = range.to(); 2437 var from = range.from(), to = range.to();
2296 // Handle deletion 2438 // Handle deletion
2297 if (same < prevInput.length) 2439 if (same < prevInput.length)
2298 from = Pos(from.line, from.ch - (prevInput.length - same)); 2440 from = Pos(from.line, from.ch - (prevInput.length - same));
2299 // Handle overwrite 2441 // Handle overwrite
2300 else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming) 2442 else if (cm.state.overwrite && range.empty() && !cm.state.pasteIncoming)
2301 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + ls t(textLines).length)); 2443 to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + ls t(textLines).length));
2302 var updateInput = cm.curOp.updateInput; 2444 var updateInput = cm.curOp.updateInput;
2303 var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines, 2445 var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i % m ultiPaste.length] : textLines,
2304 origin: cm.state.pasteIncoming ? "paste" : cm.state.cut Incoming ? "cut" : "+input"}; 2446 origin: cm.state.pasteIncoming ? "paste" : cm.state.cut Incoming ? "cut" : "+input"};
2305 makeChange(cm.doc, changeEvent); 2447 makeChange(cm.doc, changeEvent);
2306 signalLater(cm, "inputRead", cm, changeEvent); 2448 signalLater(cm, "inputRead", cm, changeEvent);
2307 // When an 'electric' character is inserted, immediately trigger a reinden t 2449 // When an 'electric' character is inserted, immediately trigger a reinden t
2308 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && 2450 if (inserted && !cm.state.pasteIncoming && cm.options.electricChars &&
2309 cm.options.smartIndent && range.head.ch < 100 && 2451 cm.options.smartIndent && range.head.ch < 100 &&
2310 (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) { 2452 (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) {
2311 var mode = cm.getModeAt(range.head); 2453 var mode = cm.getModeAt(range.head);
2312 if (mode.electricChars) { 2454 if (mode.electricChars) {
2313 for (var j = 0; j < mode.electricChars.length; j++) 2455 for (var j = 0; j < mode.electricChars.length; j++)
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
2414 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);}); 2556 on(d.scroller, "mousewheel", function(e){onScrollWheel(cm, e);});
2415 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);}); 2557 on(d.scroller, "DOMMouseScroll", function(e){onScrollWheel(cm, e);});
2416 2558
2417 // Prevent clicks in the scrollbars from killing focus 2559 // Prevent clicks in the scrollbars from killing focus
2418 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); } 2560 function reFocus() { if (cm.state.focused) setTimeout(bind(focusInput, cm), 0); }
2419 on(d.scrollbarH, "mousedown", reFocus); 2561 on(d.scrollbarH, "mousedown", reFocus);
2420 on(d.scrollbarV, "mousedown", reFocus); 2562 on(d.scrollbarV, "mousedown", reFocus);
2421 // Prevent wrapper from ever scrolling 2563 // Prevent wrapper from ever scrolling
2422 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL eft = 0; }); 2564 on(d.wrapper, "scroll", function() { d.wrapper.scrollTop = d.wrapper.scrollL eft = 0; });
2423 2565
2424 on(d.input, "keyup", operation(cm, onKeyUp)); 2566 on(d.input, "keyup", function(e) { onKeyUp.call(cm, e); });
2425 on(d.input, "input", function() { 2567 on(d.input, "input", function() {
2426 if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inpu tHasSelection = null; 2568 if (ie && ie_version >= 9 && cm.display.inputHasSelection) cm.display.inpu tHasSelection = null;
2427 fastPoll(cm); 2569 fastPoll(cm);
2428 }); 2570 });
2429 on(d.input, "keydown", operation(cm, onKeyDown)); 2571 on(d.input, "keydown", operation(cm, onKeyDown));
2430 on(d.input, "keypress", operation(cm, onKeyPress)); 2572 on(d.input, "keypress", operation(cm, onKeyPress));
2431 on(d.input, "focus", bind(onFocus, cm)); 2573 on(d.input, "focus", bind(onFocus, cm));
2432 on(d.input, "blur", bind(onBlur, cm)); 2574 on(d.input, "blur", bind(onBlur, cm));
2433 2575
2434 function drag_(e) { 2576 function drag_(e) {
(...skipping 25 matching lines...) Expand all
2460 d.input.selectionEnd = end; 2602 d.input.selectionEnd = end;
2461 d.input.selectionStart = start; 2603 d.input.selectionStart = start;
2462 cm.state.fakedLastChar = true; 2604 cm.state.fakedLastChar = true;
2463 } 2605 }
2464 cm.state.pasteIncoming = true; 2606 cm.state.pasteIncoming = true;
2465 fastPoll(cm); 2607 fastPoll(cm);
2466 }); 2608 });
2467 2609
2468 function prepareCopyCut(e) { 2610 function prepareCopyCut(e) {
2469 if (cm.somethingSelected()) { 2611 if (cm.somethingSelected()) {
2612 lastCopied = cm.getSelections();
2470 if (d.inaccurateSelection) { 2613 if (d.inaccurateSelection) {
2471 d.prevInput = ""; 2614 d.prevInput = "";
2472 d.inaccurateSelection = false; 2615 d.inaccurateSelection = false;
2473 d.input.value = cm.getSelection(); 2616 d.input.value = lastCopied.join("\n");
2474 selectInput(d.input); 2617 selectInput(d.input);
2475 } 2618 }
2476 } else { 2619 } else {
2477 var text = "", ranges = []; 2620 var text = [], ranges = [];
2478 for (var i = 0; i < cm.doc.sel.ranges.length; i++) { 2621 for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
2479 var line = cm.doc.sel.ranges[i].head.line; 2622 var line = cm.doc.sel.ranges[i].head.line;
2480 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; 2623 var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
2481 ranges.push(lineRange); 2624 ranges.push(lineRange);
2482 text += cm.getRange(lineRange.anchor, lineRange.head); 2625 text.push(cm.getRange(lineRange.anchor, lineRange.head));
2483 } 2626 }
2484 if (e.type == "cut") { 2627 if (e.type == "cut") {
2485 cm.setSelections(ranges, null, sel_dontScroll); 2628 cm.setSelections(ranges, null, sel_dontScroll);
2486 } else { 2629 } else {
2487 d.prevInput = ""; 2630 d.prevInput = "";
2488 d.input.value = text; 2631 d.input.value = text.join("\n");
2489 selectInput(d.input); 2632 selectInput(d.input);
2490 } 2633 }
2634 lastCopied = text;
2491 } 2635 }
2492 if (e.type == "cut") cm.state.cutIncoming = true; 2636 if (e.type == "cut") cm.state.cutIncoming = true;
2493 } 2637 }
2494 on(d.input, "cut", prepareCopyCut); 2638 on(d.input, "cut", prepareCopyCut);
2495 on(d.input, "copy", prepareCopyCut); 2639 on(d.input, "copy", prepareCopyCut);
2496 2640
2497 // Needed to handle Tab key in KHTML 2641 // Needed to handle Tab key in KHTML
2498 if (khtml) on(d.sizer, "mouseup", function() { 2642 if (khtml) on(d.sizer, "mouseup", function() {
2499 if (activeElt() == d.input) d.input.blur(); 2643 if (activeElt() == d.input) d.input.blur();
2500 focusInput(cm); 2644 focusInput(cm);
(...skipping 377 matching lines...) Expand 10 before | Expand all | Expand 10 after
2878 } 3022 }
2879 } 3023 }
2880 3024
2881 // SCROLL EVENTS 3025 // SCROLL EVENTS
2882 3026
2883 // Sync the scrollable area and scrollbars, ensure the viewport 3027 // Sync the scrollable area and scrollbars, ensure the viewport
2884 // covers the visible area. 3028 // covers the visible area.
2885 function setScrollTop(cm, val) { 3029 function setScrollTop(cm, val) {
2886 if (Math.abs(cm.doc.scrollTop - val) < 2) return; 3030 if (Math.abs(cm.doc.scrollTop - val) < 2) return;
2887 cm.doc.scrollTop = val; 3031 cm.doc.scrollTop = val;
2888 if (!gecko) updateDisplay(cm, {top: val}); 3032 if (!gecko) updateDisplaySimple(cm, {top: val});
2889 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va l; 3033 if (cm.display.scroller.scrollTop != val) cm.display.scroller.scrollTop = va l;
2890 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val; 3034 if (cm.display.scrollbarV.scrollTop != val) cm.display.scrollbarV.scrollTop = val;
2891 if (gecko) updateDisplay(cm); 3035 if (gecko) updateDisplaySimple(cm);
2892 startWorker(cm, 100); 3036 startWorker(cm, 100);
2893 } 3037 }
2894 // Sync scroller and scrollbar, ensure the gutter elements are 3038 // Sync scroller and scrollbar, ensure the gutter elements are
2895 // aligned. 3039 // aligned.
2896 function setScrollLeft(cm, val, isScroller) { 3040 function setScrollLeft(cm, val, isScroller) {
2897 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val ) < 2) return; 3041 if (isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val ) < 2) return;
2898 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.cl ientWidth); 3042 val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.cl ientWidth);
2899 cm.doc.scrollLeft = val; 3043 cm.doc.scrollLeft = val;
2900 alignHorizontally(cm); 3044 alignHorizontally(cm);
2901 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val; 3045 if (cm.display.scroller.scrollLeft != val) cm.display.scroller.scrollLeft = val;
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
2964 return; 3108 return;
2965 } 3109 }
2966 3110
2967 // 'Project' the visible viewport to cover the area that is being 3111 // 'Project' the visible viewport to cover the area that is being
2968 // scrolled into view (if we know enough to estimate it). 3112 // scrolled into view (if we know enough to estimate it).
2969 if (dy && wheelPixelsPerUnit != null) { 3113 if (dy && wheelPixelsPerUnit != null) {
2970 var pixels = dy * wheelPixelsPerUnit; 3114 var pixels = dy * wheelPixelsPerUnit;
2971 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; 3115 var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
2972 if (pixels < 0) top = Math.max(0, top + pixels - 50); 3116 if (pixels < 0) top = Math.max(0, top + pixels - 50);
2973 else bot = Math.min(cm.doc.height, bot + pixels + 50); 3117 else bot = Math.min(cm.doc.height, bot + pixels + 50);
2974 updateDisplay(cm, {top: top, bottom: bot}); 3118 updateDisplaySimple(cm, {top: top, bottom: bot});
2975 } 3119 }
2976 3120
2977 if (wheelSamples < 20) { 3121 if (wheelSamples < 20) {
2978 if (display.wheelStartX == null) { 3122 if (display.wheelStartX == null) {
2979 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.sc rollTop; 3123 display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.sc rollTop;
2980 display.wheelDX = dx; display.wheelDY = dy; 3124 display.wheelDX = dx; display.wheelDY = dy;
2981 setTimeout(function() { 3125 setTimeout(function() {
2982 if (display.wheelStartX == null) return; 3126 if (display.wheelStartX == null) return;
2983 var movedX = scroll.scrollLeft - display.wheelStartX; 3127 var movedX = scroll.scrollLeft - display.wheelStartX;
2984 var movedY = scroll.scrollTop - display.wheelStartY; 3128 var movedY = scroll.scrollTop - display.wheelStartY;
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
3107 rmClass(lineDiv, "CodeMirror-crosshair"); 3251 rmClass(lineDiv, "CodeMirror-crosshair");
3108 off(document, "keyup", up); 3252 off(document, "keyup", up);
3109 off(document, "mouseover", up); 3253 off(document, "mouseover", up);
3110 } 3254 }
3111 } 3255 }
3112 on(document, "keyup", up); 3256 on(document, "keyup", up);
3113 on(document, "mouseover", up); 3257 on(document, "mouseover", up);
3114 } 3258 }
3115 3259
3116 function onKeyUp(e) { 3260 function onKeyUp(e) {
3117 if (signalDOMEvent(this, e)) return;
3118 if (e.keyCode == 16) this.doc.sel.shift = false; 3261 if (e.keyCode == 16) this.doc.sel.shift = false;
3262 signalDOMEvent(this, e);
3119 } 3263 }
3120 3264
3121 function onKeyPress(e) { 3265 function onKeyPress(e) {
3122 var cm = this; 3266 var cm = this;
3123 if (signalDOMEvent(cm, e) || e.ctrlKey || mac && e.metaKey) return; 3267 if (signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) ret urn;
3124 var keyCode = e.keyCode, charCode = e.charCode; 3268 var keyCode = e.keyCode, charCode = e.charCode;
3125 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe fault(e); return;} 3269 if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDe fault(e); return;}
3126 if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm , e)) return; 3270 if (((presto && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm , e)) return;
3127 var ch = String.fromCharCode(charCode == null ? keyCode : charCode); 3271 var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
3128 if (handleCharBinding(cm, e, ch)) return; 3272 if (handleCharBinding(cm, e, ch)) return;
3129 if (ie && ie_version >= 9) cm.display.inputHasSelection = null; 3273 if (ie && ie_version >= 9) cm.display.inputHasSelection = null;
3130 fastPoll(cm); 3274 fastPoll(cm);
3131 } 3275 }
3132 3276
3133 // FOCUS/BLUR EVENTS 3277 // FOCUS/BLUR EVENTS
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
3177 var reset = cm.options.resetSelectionOnContextMenu; 3321 var reset = cm.options.resetSelectionOnContextMenu;
3178 if (reset && cm.doc.sel.contains(pos) == -1) 3322 if (reset && cm.doc.sel.contains(pos) == -1)
3179 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); 3323 operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll);
3180 3324
3181 var oldCSS = display.input.style.cssText; 3325 var oldCSS = display.input.style.cssText;
3182 display.inputDiv.style.position = "absolute"; 3326 display.inputDiv.style.position = "absolute";
3183 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t op: " + (e.clientY - 5) + 3327 display.input.style.cssText = "position: fixed; width: 30px; height: 30px; t op: " + (e.clientY - 5) +
3184 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " + 3328 "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: " +
3185 (ie ? "rgba(255, 255, 255, .05)" : "transparent") + 3329 (ie ? "rgba(255, 255, 255, .05)" : "transparent") +
3186 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacit y: .05; filter: alpha(opacity=5);"; 3330 "; outline: none; border-width: 0; outline: none; overflow: hidden; opacit y: .05; filter: alpha(opacity=5);";
3331 if (webkit) var oldScrollY = window.scrollY; // Work around Chrome issue (#2 712)
3187 focusInput(cm); 3332 focusInput(cm);
3333 if (webkit) window.scrollTo(null, oldScrollY);
3188 resetInput(cm); 3334 resetInput(cm);
3189 // Adds "Select all" to context menu in FF 3335 // Adds "Select all" to context menu in FF
3190 if (!cm.somethingSelected()) display.input.value = display.prevInput = " "; 3336 if (!cm.somethingSelected()) display.input.value = display.prevInput = " ";
3191 display.selForContextMenu = cm.doc.sel; 3337 display.selForContextMenu = cm.doc.sel;
3192 clearTimeout(display.detectingSelectAll); 3338 clearTimeout(display.detectingSelectAll);
3193 3339
3194 // Select-all will be greyed out if there's nothing to select, so 3340 // Select-all will be greyed out if there's nothing to select, so
3195 // this adds a zero-width space so that we can later check whether 3341 // this adds a zero-width space so that we can later check whether
3196 // it got selected. 3342 // it got selected.
3197 function prepareSelectAllHack() { 3343 function prepareSelectAllHack() {
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after
3407 for (var i = event.changes.length - 1; i >= 0; --i) { 3553 for (var i = event.changes.length - 1; i >= 0; --i) {
3408 var change = event.changes[i]; 3554 var change = event.changes[i];
3409 change.origin = type; 3555 change.origin = type;
3410 if (filter && !filterChange(doc, change, false)) { 3556 if (filter && !filterChange(doc, change, false)) {
3411 source.length = 0; 3557 source.length = 0;
3412 return; 3558 return;
3413 } 3559 }
3414 3560
3415 antiChanges.push(historyChangeFromChange(doc, change)); 3561 antiChanges.push(historyChangeFromChange(doc, change));
3416 3562
3417 var after = i ? computeSelAfterChange(doc, change, null) : lst(source); 3563 var after = i ? computeSelAfterChange(doc, change) : lst(source);
3418 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); 3564 makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
3419 if (!i && doc.cm) doc.cm.scrollIntoView(change); 3565 if (!i && doc.cm) doc.cm.scrollIntoView({from: change.from, to: changeEnd( change)});
3420 var rebased = []; 3566 var rebased = [];
3421 3567
3422 // Propagate to the linked documents 3568 // Propagate to the linked documents
3423 linkedDocs(doc, function(doc, sharedHist) { 3569 linkedDocs(doc, function(doc, sharedHist) {
3424 if (!sharedHist && indexOf(rebased, doc.history) == -1) { 3570 if (!sharedHist && indexOf(rebased, doc.history) == -1) {
3425 rebaseHist(doc.history, change); 3571 rebaseHist(doc.history, change);
3426 rebased.push(doc.history); 3572 rebased.push(doc.history);
3427 } 3573 }
3428 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); 3574 makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
3429 }); 3575 });
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
3466 text: [lst(change.text)], origin: change.origin}; 3612 text: [lst(change.text)], origin: change.origin};
3467 } 3613 }
3468 var last = doc.lastLine(); 3614 var last = doc.lastLine();
3469 if (change.to.line > last) { 3615 if (change.to.line > last) {
3470 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length) , 3616 change = {from: change.from, to: Pos(last, getLine(doc, last).text.length) ,
3471 text: [change.text[0]], origin: change.origin}; 3617 text: [change.text[0]], origin: change.origin};
3472 } 3618 }
3473 3619
3474 change.removed = getBetween(doc, change.from, change.to); 3620 change.removed = getBetween(doc, change.from, change.to);
3475 3621
3476 if (!selAfter) selAfter = computeSelAfterChange(doc, change, null); 3622 if (!selAfter) selAfter = computeSelAfterChange(doc, change);
3477 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans); 3623 if (doc.cm) makeChangeSingleDocInEditor(doc.cm, change, spans);
3478 else updateDoc(doc, change, spans); 3624 else updateDoc(doc, change, spans);
3479 setSelectionNoUndo(doc, selAfter, sel_dontScroll); 3625 setSelectionNoUndo(doc, selAfter, sel_dontScroll);
3480 } 3626 }
3481 3627
3482 // Handle the interaction of a change to a document with the editor 3628 // Handle the interaction of a change to a document with the editor
3483 // that this document is part of. 3629 // that this document is part of.
3484 function makeChangeSingleDocInEditor(cm, change, spans) { 3630 function makeChangeSingleDocInEditor(cm, change, spans) {
3485 var doc = cm.doc, display = cm.display, from = change.from, to = change.to; 3631 var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
3486 3632
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
3598 3744
3599 // Calculate a new scroll position needed to scroll the given 3745 // Calculate a new scroll position needed to scroll the given
3600 // rectangle into view. Returns an object with scrollTop and 3746 // rectangle into view. Returns an object with scrollTop and
3601 // scrollLeft properties. When these are undefined, the 3747 // scrollLeft properties. When these are undefined, the
3602 // vertical/horizontal position does not need to be adjusted. 3748 // vertical/horizontal position does not need to be adjusted.
3603 function calculateScrollPos(cm, x1, y1, x2, y2) { 3749 function calculateScrollPos(cm, x1, y1, x2, y2) {
3604 var display = cm.display, snapMargin = textHeight(cm.display); 3750 var display = cm.display, snapMargin = textHeight(cm.display);
3605 if (y1 < 0) y1 = 0; 3751 if (y1 < 0) y1 = 0;
3606 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; 3752 var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
3607 var screen = display.scroller.clientHeight - scrollerCutOff, result = {}; 3753 var screen = display.scroller.clientHeight - scrollerCutOff, result = {};
3754 if (y2 - y1 > screen) y2 = y1 + screen;
3608 var docBottom = cm.doc.height + paddingVert(display); 3755 var docBottom = cm.doc.height + paddingVert(display);
3609 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin; 3756 var atTop = y1 < snapMargin, atBottom = y2 > docBottom - snapMargin;
3610 if (y1 < screentop) { 3757 if (y1 < screentop) {
3611 result.scrollTop = atTop ? 0 : y1; 3758 result.scrollTop = atTop ? 0 : y1;
3612 } else if (y2 > screentop + screen) { 3759 } else if (y2 > screentop + screen) {
3613 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen); 3760 var newTop = Math.min(y1, (atBottom ? docBottom : y2) - screen);
3614 if (newTop != screentop) result.scrollTop = newTop; 3761 if (newTop != screentop) result.scrollTop = newTop;
3615 } 3762 }
3616 3763
3617 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLe ft : display.scroller.scrollLeft; 3764 var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLe ft : display.scroller.scrollLeft;
3618 var screenw = display.scroller.clientWidth - scrollerCutOff; 3765 var screenw = display.scroller.clientWidth - scrollerCutOff - display.gutter s.offsetWidth;
3619 x1 += display.gutters.offsetWidth; x2 += display.gutters.offsetWidth; 3766 var tooWide = x2 - x1 > screenw;
3620 var gutterw = display.gutters.offsetWidth; 3767 if (tooWide) x2 = y1 + screen;
3621 var atLeft = x1 < gutterw + 10; 3768 if (x1 < 10)
3622 if (x1 < screenleft + gutterw || atLeft) { 3769 result.scrollLeft = 0;
3623 if (atLeft) x1 = 0; 3770 else if (x1 < screenleft)
3624 result.scrollLeft = Math.max(0, x1 - 10 - gutterw); 3771 result.scrollLeft = Math.max(0, x1 - (tooWide ? 0 : 10));
3625 } else if (x2 > screenw + screenleft - 3) { 3772 else if (x2 > screenw + screenleft - 3)
3626 result.scrollLeft = x2 + 10 - screenw; 3773 result.scrollLeft = x2 + (tooWide ? 0 : 10) - screenw;
3627 } 3774
3628 return result; 3775 return result;
3629 } 3776 }
3630 3777
3631 // Store a relative adjustment to the scroll position in the current 3778 // Store a relative adjustment to the scroll position in the current
3632 // operation (to be applied when the operation finishes). 3779 // operation (to be applied when the operation finishes).
3633 function addToScrollPos(cm, left, top) { 3780 function addToScrollPos(cm, left, top) {
3634 if (left != null || top != null) resolveScrollToPos(cm); 3781 if (left != null || top != null) resolveScrollToPos(cm);
3635 if (left != null) 3782 if (left != null)
3636 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : c m.curOp.scrollLeft) + left; 3783 cm.curOp.scrollLeft = (cm.curOp.scrollLeft == null ? cm.doc.scrollLeft : c m.curOp.scrollLeft) + left;
3637 if (top != null) 3784 if (top != null)
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
3673 // "add"/null, "subtract", or "prev". When aggressive is false 3820 // "add"/null, "subtract", or "prev". When aggressive is false
3674 // (typically set to true for forced single-line indents), empty 3821 // (typically set to true for forced single-line indents), empty
3675 // lines are not indented, and places where the mode returns Pass 3822 // lines are not indented, and places where the mode returns Pass
3676 // are left alone. 3823 // are left alone.
3677 function indentLine(cm, n, how, aggressive) { 3824 function indentLine(cm, n, how, aggressive) {
3678 var doc = cm.doc, state; 3825 var doc = cm.doc, state;
3679 if (how == null) how = "add"; 3826 if (how == null) how = "add";
3680 if (how == "smart") { 3827 if (how == "smart") {
3681 // Fall back to "prev" when the mode doesn't have an indentation 3828 // Fall back to "prev" when the mode doesn't have an indentation
3682 // method. 3829 // method.
3683 if (!cm.doc.mode.indent) how = "prev"; 3830 if (!doc.mode.indent) how = "prev";
3684 else state = getStateBefore(cm, n); 3831 else state = getStateBefore(cm, n);
3685 } 3832 }
3686 3833
3687 var tabSize = cm.options.tabSize; 3834 var tabSize = cm.options.tabSize;
3688 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) ; 3835 var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize) ;
3689 if (line.stateAfter) line.stateAfter = null; 3836 if (line.stateAfter) line.stateAfter = null;
3690 var curSpaceString = line.text.match(/^\s*/)[0], indentation; 3837 var curSpaceString = line.text.match(/^\s*/)[0], indentation;
3691 if (!aggressive && !/\S/.test(line.text)) { 3838 if (!aggressive && !/\S/.test(line.text)) {
3692 indentation = 0; 3839 indentation = 0;
3693 how = "not"; 3840 how = "not";
3694 } else if (how == "smart") { 3841 } else if (how == "smart") {
3695 indentation = cm.doc.mode.indent(state, line.text.slice(curSpaceString.len gth), line.text); 3842 indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length ), line.text);
3696 if (indentation == Pass) { 3843 if (indentation == Pass || indentation > 150) {
3697 if (!aggressive) return; 3844 if (!aggressive) return;
3698 how = "prev"; 3845 how = "prev";
3699 } 3846 }
3700 } 3847 }
3701 if (how == "prev") { 3848 if (how == "prev") {
3702 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize); 3849 if (n > doc.first) indentation = countColumn(getLine(doc, n-1).text, null, tabSize);
3703 else indentation = 0; 3850 else indentation = 0;
3704 } else if (how == "add") { 3851 } else if (how == "add") {
3705 indentation = curSpace + cm.options.indentUnit; 3852 indentation = curSpace + cm.options.indentUnit;
3706 } else if (how == "subtract") { 3853 } else if (how == "subtract") {
3707 indentation = curSpace - cm.options.indentUnit; 3854 indentation = curSpace - cm.options.indentUnit;
3708 } else if (typeof how == "number") { 3855 } else if (typeof how == "number") {
3709 indentation = curSpace + how; 3856 indentation = curSpace + how;
3710 } 3857 }
3711 indentation = Math.max(0, indentation); 3858 indentation = Math.max(0, indentation);
3712 3859
3713 var indentString = "", pos = 0; 3860 var indentString = "", pos = 0;
3714 if (cm.options.indentWithTabs) 3861 if (cm.options.indentWithTabs)
3715 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i ndentString += "\t";} 3862 for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; i ndentString += "\t";}
3716 if (pos < indentation) indentString += spaceStr(indentation - pos); 3863 if (pos < indentation) indentString += spaceStr(indentation - pos);
3717 3864
3718 if (indentString != curSpaceString) { 3865 if (indentString != curSpaceString) {
3719 replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length ), "+input"); 3866 replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
3720 } else { 3867 } else {
3721 // Ensure that, if the cursor was in the whitespace at the start 3868 // Ensure that, if the cursor was in the whitespace at the start
3722 // of the line, it is moved to the end of that space. 3869 // of the line, it is moved to the end of that space.
3723 for (var i = 0; i < doc.sel.ranges.length; i++) { 3870 for (var i = 0; i < doc.sel.ranges.length; i++) {
3724 var range = doc.sel.ranges[i]; 3871 var range = doc.sel.ranges[i];
3725 if (range.head.line == n && range.head.ch < curSpaceString.length) { 3872 if (range.head.line == n && range.head.ch < curSpaceString.length) {
3726 var pos = Pos(n, curSpaceString.length); 3873 var pos = Pos(n, curSpaceString.length);
3727 replaceOneSelection(doc, i, new Range(pos, pos)); 3874 replaceOneSelection(doc, i, new Range(pos, pos));
3728 break; 3875 break;
3729 } 3876 }
(...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after
3926 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev"; 4073 if (dir == null) dir = this.options.smartIndent ? "smart" : "prev";
3927 else dir = dir ? "add" : "subtract"; 4074 else dir = dir ? "add" : "subtract";
3928 } 4075 }
3929 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); 4076 if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive);
3930 }), 4077 }),
3931 indentSelection: methodOp(function(how) { 4078 indentSelection: methodOp(function(how) {
3932 var ranges = this.doc.sel.ranges, end = -1; 4079 var ranges = this.doc.sel.ranges, end = -1;
3933 for (var i = 0; i < ranges.length; i++) { 4080 for (var i = 0; i < ranges.length; i++) {
3934 var range = ranges[i]; 4081 var range = ranges[i];
3935 if (!range.empty()) { 4082 if (!range.empty()) {
3936 var start = Math.max(end, range.from().line); 4083 var from = range.from(), to = range.to();
3937 var to = range.to(); 4084 var start = Math.max(end, from.line);
3938 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; 4085 end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
3939 for (var j = start; j < end; ++j) 4086 for (var j = start; j < end; ++j)
3940 indentLine(this, j, how); 4087 indentLine(this, j, how);
4088 var newRanges = this.doc.sel.ranges;
4089 if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i]. from().ch > 0)
4090 replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to())) ;
3941 } else if (range.head.line > end) { 4091 } else if (range.head.line > end) {
3942 indentLine(this, range.head.line, how, true); 4092 indentLine(this, range.head.line, how, true);
3943 end = range.head.line; 4093 end = range.head.line;
3944 if (i == this.doc.sel.primIndex) ensureCursorVisible(this); 4094 if (i == this.doc.sel.primIndex) ensureCursorVisible(this);
3945 } 4095 }
3946 } 4096 }
3947 }), 4097 }),
3948 4098
3949 // Fetch the parser token for a given character. Useful for hacks 4099 // Fetch the parser token for a given character. Useful for hacks
3950 // that want to inspect the mode state (say, for completion). 4100 // that want to inspect the mode state (say, for completion).
(...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after
4126 if (horiz == "left") left = 0; 4276 if (horiz == "left") left = 0;
4127 else if (horiz == "middle") left = (display.sizer.clientWidth - node.off setWidth) / 2; 4277 else if (horiz == "middle") left = (display.sizer.clientWidth - node.off setWidth) / 2;
4128 node.style.left = left + "px"; 4278 node.style.left = left + "px";
4129 } 4279 }
4130 if (scroll) 4280 if (scroll)
4131 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs etHeight); 4281 scrollIntoView(this, left, top, left + node.offsetWidth, top + node.offs etHeight);
4132 }, 4282 },
4133 4283
4134 triggerOnKeyDown: methodOp(onKeyDown), 4284 triggerOnKeyDown: methodOp(onKeyDown),
4135 triggerOnKeyPress: methodOp(onKeyPress), 4285 triggerOnKeyPress: methodOp(onKeyPress),
4136 triggerOnKeyUp: methodOp(onKeyUp), 4286 triggerOnKeyUp: onKeyUp,
4137 4287
4138 execCommand: function(cmd) { 4288 execCommand: function(cmd) {
4139 if (commands.hasOwnProperty(cmd)) 4289 if (commands.hasOwnProperty(cmd))
4140 return commands[cmd](this); 4290 return commands[cmd](this);
4141 }, 4291 },
4142 4292
4143 findPosH: function(from, amount, unit, visually) { 4293 findPosH: function(from, amount, unit, visually) {
4144 var dir = 1; 4294 var dir = 1;
4145 if (amount < 0) { dir = -1; amount = -amount; } 4295 if (amount < 0) { dir = -1; amount = -amount; }
4146 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) { 4296 for (var i = 0, cur = clipPos(this.doc, from); i < amount; ++i) {
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
4326 }, true); 4476 }, true);
4327 4477
4328 option("indentUnit", 2, loadMode, true); 4478 option("indentUnit", 2, loadMode, true);
4329 option("indentWithTabs", false); 4479 option("indentWithTabs", false);
4330 option("smartIndent", true); 4480 option("smartIndent", true);
4331 option("tabSize", 4, function(cm) { 4481 option("tabSize", 4, function(cm) {
4332 resetModeState(cm); 4482 resetModeState(cm);
4333 clearCaches(cm); 4483 clearCaches(cm);
4334 regChange(cm); 4484 regChange(cm);
4335 }, true); 4485 }, true);
4336 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b\u2028\u2029\ufeff]/g, fun ction(cm, val) { 4486 option("specialChars", /[\t\u0000-\u0019\u00ad\u200b-\u200f\u2028\u2029\ufeff] /g, function(cm, val) {
4337 cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\ t"), "g"); 4487 cm.options.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\ t"), "g");
4338 cm.refresh(); 4488 cm.refresh();
4339 }, true); 4489 }, true);
4340 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) { cm.refresh();}, true); 4490 option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function(cm) { cm.refresh();}, true);
4341 option("electricChars", true); 4491 option("electricChars", true);
4342 option("rtlMoveVisually", !windows); 4492 option("rtlMoveVisually", !windows);
4343 option("wholeLineUpdateBefore", true); 4493 option("wholeLineUpdateBefore", true);
4344 4494
4345 option("theme", "default", function(cm) { 4495 option("theme", "default", function(cm) {
4346 themeChanged(cm); 4496 themeChanged(cm);
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
4561 deleteNearSelection(cm, function(range) { 4711 deleteNearSelection(cm, function(range) {
4562 return {from: Pos(range.from().line, 0), 4712 return {from: Pos(range.from().line, 0),
4563 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; 4713 to: clipPos(cm.doc, Pos(range.to().line + 1, 0))};
4564 }); 4714 });
4565 }, 4715 },
4566 delLineLeft: function(cm) { 4716 delLineLeft: function(cm) {
4567 deleteNearSelection(cm, function(range) { 4717 deleteNearSelection(cm, function(range) {
4568 return {from: Pos(range.from().line, 0), to: range.from()}; 4718 return {from: Pos(range.from().line, 0), to: range.from()};
4569 }); 4719 });
4570 }, 4720 },
4721 delWrappedLineLeft: function(cm) {
4722 deleteNearSelection(cm, function(range) {
4723 var top = cm.charCoords(range.head, "div").top + 5;
4724 var leftPos = cm.coordsChar({left: 0, top: top}, "div");
4725 return {from: leftPos, to: range.from()};
4726 });
4727 },
4728 delWrappedLineRight: function(cm) {
4729 deleteNearSelection(cm, function(range) {
4730 var top = cm.charCoords(range.head, "div").top + 5;
4731 var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100 , top: top}, "div");
4732 return {from: range.from(), to: rightPos };
4733 });
4734 },
4571 undo: function(cm) {cm.undo();}, 4735 undo: function(cm) {cm.undo();},
4572 redo: function(cm) {cm.redo();}, 4736 redo: function(cm) {cm.redo();},
4573 undoSelection: function(cm) {cm.undoSelection();}, 4737 undoSelection: function(cm) {cm.undoSelection();},
4574 redoSelection: function(cm) {cm.redoSelection();}, 4738 redoSelection: function(cm) {cm.redoSelection();},
4575 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, 4739 goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));},
4576 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, 4740 goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));},
4577 goLineStart: function(cm) { 4741 goLineStart: function(cm) {
4578 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.li ne); }, 4742 cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.li ne); },
4579 {origin: "+move", bias: 1}); 4743 {origin: "+move", bias: 1});
4580 }, 4744 },
4581 goLineStartSmart: function(cm) { 4745 goLineStartSmart: function(cm) {
4582 cm.extendSelectionsBy(function(range) { 4746 cm.extendSelectionsBy(function(range) {
4583 var start = lineStart(cm, range.head.line); 4747 return lineStartSmart(cm, range.head);
4584 var line = cm.getLineHandle(start.line);
4585 var order = getOrder(line);
4586 if (!order || order[0].level == 0) {
4587 var firstNonWS = Math.max(0, line.text.search(/\S/));
4588 var inWS = range.head.line == start.line && range.head.ch <= firstNonW S && range.head.ch;
4589 return Pos(start.line, inWS ? 0 : firstNonWS);
4590 }
4591 return start;
4592 }, {origin: "+move", bias: 1}); 4748 }, {origin: "+move", bias: 1});
4593 }, 4749 },
4594 goLineEnd: function(cm) { 4750 goLineEnd: function(cm) {
4595 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line ); }, 4751 cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line ); },
4596 {origin: "+move", bias: -1}); 4752 {origin: "+move", bias: -1});
4597 }, 4753 },
4598 goLineRight: function(cm) { 4754 goLineRight: function(cm) {
4599 cm.extendSelectionsBy(function(range) { 4755 cm.extendSelectionsBy(function(range) {
4600 var top = cm.charCoords(range.head, "div").top + 5; 4756 var top = cm.charCoords(range.head, "div").top + 5;
4601 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: t op}, "div"); 4757 return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: t op}, "div");
4602 }, sel_move); 4758 }, sel_move);
4603 }, 4759 },
4604 goLineLeft: function(cm) { 4760 goLineLeft: function(cm) {
4605 cm.extendSelectionsBy(function(range) { 4761 cm.extendSelectionsBy(function(range) {
4606 var top = cm.charCoords(range.head, "div").top + 5; 4762 var top = cm.charCoords(range.head, "div").top + 5;
4607 return cm.coordsChar({left: 0, top: top}, "div"); 4763 return cm.coordsChar({left: 0, top: top}, "div");
4608 }, sel_move); 4764 }, sel_move);
4609 }, 4765 },
4766 goLineLeftSmart: function(cm) {
4767 cm.extendSelectionsBy(function(range) {
4768 var top = cm.charCoords(range.head, "div").top + 5;
4769 var pos = cm.coordsChar({left: 0, top: top}, "div");
4770 if (pos.ch < cm.getLine(pos.line).search(/\S/)) return lineStartSmart(cm , range.head);
4771 return pos;
4772 }, sel_move);
4773 },
4610 goLineUp: function(cm) {cm.moveV(-1, "line");}, 4774 goLineUp: function(cm) {cm.moveV(-1, "line");},
4611 goLineDown: function(cm) {cm.moveV(1, "line");}, 4775 goLineDown: function(cm) {cm.moveV(1, "line");},
4612 goPageUp: function(cm) {cm.moveV(-1, "page");}, 4776 goPageUp: function(cm) {cm.moveV(-1, "page");},
4613 goPageDown: function(cm) {cm.moveV(1, "page");}, 4777 goPageDown: function(cm) {cm.moveV(1, "page");},
4614 goCharLeft: function(cm) {cm.moveH(-1, "char");}, 4778 goCharLeft: function(cm) {cm.moveH(-1, "char");},
4615 goCharRight: function(cm) {cm.moveH(1, "char");}, 4779 goCharRight: function(cm) {cm.moveH(1, "char");},
4616 goColumnLeft: function(cm) {cm.moveH(-1, "column");}, 4780 goColumnLeft: function(cm) {cm.moveH(-1, "column");},
4617 goColumnRight: function(cm) {cm.moveH(1, "column");}, 4781 goColumnRight: function(cm) {cm.moveH(1, "column");},
4618 goWordLeft: function(cm) {cm.moveH(-1, "word");}, 4782 goWordLeft: function(cm) {cm.moveH(-1, "word");},
4619 goGroupRight: function(cm) {cm.moveH(1, "group");}, 4783 goGroupRight: function(cm) {cm.moveH(1, "group");},
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
4698 "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", 4862 "Ctrl-Home": "goDocStart", "Ctrl-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd",
4699 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLin eStart", "Alt-Right": "goLineEnd", 4863 "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLin eStart", "Alt-Right": "goLineEnd",
4700 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S" : "save", "Ctrl-F": "find", 4864 "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S" : "save", "Ctrl-F": "find",
4701 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", 4865 "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
4702 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", 4866 "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
4703 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe lection", 4867 "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSe lection",
4704 fallthrough: "basic" 4868 fallthrough: "basic"
4705 }; 4869 };
4706 keyMap.macDefault = { 4870 keyMap.macDefault = {
4707 "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", 4871 "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", 4872 "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cm d-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
4709 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLine End", "Alt-Backspace": "delGroupBefore", 4873 "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineR ight", "Alt-Backspace": "delGroupBefore",
4710 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S ": "save", "Cmd-F": "find", 4874 "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S ": "save", "Cmd-F": "find",
4711 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi ft-Cmd-Alt-F": "replaceAll", 4875 "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shi ft-Cmd-Alt-F": "replaceAll",
4712 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delLineLeft" , 4876 "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLi neLeft", "Cmd-Delete": "delWrappedLineRight",
4713 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", 4877 "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection",
4714 fallthrough: ["basic", "emacsy"] 4878 fallthrough: ["basic", "emacsy"]
4715 }; 4879 };
4716 // Very basic readline/emacs-style bindings, which are standard on Mac. 4880 // Very basic readline/emacs-style bindings, which are standard on Mac.
4717 keyMap.emacsy = { 4881 keyMap.emacsy = {
4718 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl -N": "goLineDown", 4882 "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl -N": "goLineDown",
4719 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr l-E": "goLineEnd", 4883 "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctr l-E": "goLineEnd",
4720 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter" , "Ctrl-H": "delCharBefore", 4884 "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter" , "Ctrl-H": "delCharBefore",
4721 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi ne", "Ctrl-T": "transposeChars" 4885 "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLi ne", "Ctrl-T": "transposeChars"
4722 }; 4886 };
(...skipping 1614 matching lines...) Expand 10 before | Expand all | Expand 10 after
6337 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) + +undone; 6501 for (var i = 0; i < hist.undone.length; i++) if (!hist.undone[i].ranges) + +undone;
6338 return {undo: done, redo: undone}; 6502 return {undo: done, redo: undone};
6339 }, 6503 },
6340 clearHistory: function() {this.history = new History(this.history.maxGenerat ion);}, 6504 clearHistory: function() {this.history = new History(this.history.maxGenerat ion);},
6341 6505
6342 markClean: function() { 6506 markClean: function() {
6343 this.cleanGeneration = this.changeGeneration(true); 6507 this.cleanGeneration = this.changeGeneration(true);
6344 }, 6508 },
6345 changeGeneration: function(forceSplit) { 6509 changeGeneration: function(forceSplit) {
6346 if (forceSplit) 6510 if (forceSplit)
6347 this.history.lastOp = this.history.lastOrigin = null; 6511 this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null;
6348 return this.history.generation; 6512 return this.history.generation;
6349 }, 6513 },
6350 isClean: function (gen) { 6514 isClean: function (gen) {
6351 return this.history.generation == (gen || this.cleanGeneration); 6515 return this.history.generation == (gen || this.cleanGeneration);
6352 }, 6516 },
6353 6517
6354 getHistory: function() { 6518 getHistory: function() {
6355 return {done: copyHistoryArray(this.history.done), 6519 return {done: copyHistoryArray(this.history.done),
6356 undone: copyHistoryArray(this.history.undone)}; 6520 undone: copyHistoryArray(this.history.undone)};
6357 }, 6521 },
(...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after
6654 6818
6655 function History(startGen) { 6819 function History(startGen) {
6656 // Arrays of change events and selections. Doing something adds an 6820 // Arrays of change events and selections. Doing something adds an
6657 // event to done and clears undo. Undoing moves events from done 6821 // event to done and clears undo. Undoing moves events from done
6658 // to undone, redoing moves them in the other direction. 6822 // to undone, redoing moves them in the other direction.
6659 this.done = []; this.undone = []; 6823 this.done = []; this.undone = [];
6660 this.undoDepth = Infinity; 6824 this.undoDepth = Infinity;
6661 // Used to track when changes can be merged into a single undo 6825 // Used to track when changes can be merged into a single undo
6662 // event 6826 // event
6663 this.lastModTime = this.lastSelTime = 0; 6827 this.lastModTime = this.lastSelTime = 0;
6664 this.lastOp = null; 6828 this.lastOp = this.lastSelOp = null;
6665 this.lastOrigin = this.lastSelOrigin = null; 6829 this.lastOrigin = this.lastSelOrigin = null;
6666 // Used by the isClean() method 6830 // Used by the isClean() method
6667 this.generation = this.maxGeneration = startGen || 1; 6831 this.generation = this.maxGeneration = startGen || 1;
6668 } 6832 }
6669 6833
6670 // Create a history change event from an updateDoc-style change 6834 // Create a history change event from an updateDoc-style change
6671 // object. 6835 // object.
6672 function historyChangeFromChange(doc, change) { 6836 function historyChangeFromChange(doc, change) {
6673 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: g etBetween(doc, change.from, change.to)}; 6837 var histChange = {from: copyPos(change.from), to: changeEnd(change), text: g etBetween(doc, change.from, change.to)};
6674 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); 6838 attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
6732 generation: hist.generation}; 6896 generation: hist.generation};
6733 hist.done.push(cur); 6897 hist.done.push(cur);
6734 while (hist.done.length > hist.undoDepth) { 6898 while (hist.done.length > hist.undoDepth) {
6735 hist.done.shift(); 6899 hist.done.shift();
6736 if (!hist.done[0].ranges) hist.done.shift(); 6900 if (!hist.done[0].ranges) hist.done.shift();
6737 } 6901 }
6738 } 6902 }
6739 hist.done.push(selAfter); 6903 hist.done.push(selAfter);
6740 hist.generation = ++hist.maxGeneration; 6904 hist.generation = ++hist.maxGeneration;
6741 hist.lastModTime = hist.lastSelTime = time; 6905 hist.lastModTime = hist.lastSelTime = time;
6742 hist.lastOp = opId; 6906 hist.lastOp = hist.lastSelOp = opId;
6743 hist.lastOrigin = hist.lastSelOrigin = change.origin; 6907 hist.lastOrigin = hist.lastSelOrigin = change.origin;
6744 6908
6745 if (!last) signal(doc, "historyAdded"); 6909 if (!last) signal(doc, "historyAdded");
6746 } 6910 }
6747 6911
6748 function selectionEventCanBeMerged(doc, origin, prev, sel) { 6912 function selectionEventCanBeMerged(doc, origin, prev, sel) {
6749 var ch = origin.charAt(0); 6913 var ch = origin.charAt(0);
6750 return ch == "*" || 6914 return ch == "*" ||
6751 ch == "+" && 6915 ch == "+" &&
6752 prev.ranges.length == sel.ranges.length && 6916 prev.ranges.length == sel.ranges.length &&
6753 prev.somethingSelected() == sel.somethingSelected() && 6917 prev.somethingSelected() == sel.somethingSelected() &&
6754 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEven tDelay : 500); 6918 new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEven tDelay : 500);
6755 } 6919 }
6756 6920
6757 // Called whenever the selection changes, sets the new selection as 6921 // Called whenever the selection changes, sets the new selection as
6758 // the pending selection in the history, and pushes the old pending 6922 // the pending selection in the history, and pushes the old pending
6759 // selection into the 'done' array when it was significantly 6923 // selection into the 'done' array when it was significantly
6760 // different (in number of selected ranges, emptiness, or time). 6924 // different (in number of selected ranges, emptiness, or time).
6761 function addSelectionToHistory(doc, sel, opId, options) { 6925 function addSelectionToHistory(doc, sel, opId, options) {
6762 var hist = doc.history, origin = options && options.origin; 6926 var hist = doc.history, origin = options && options.origin;
6763 6927
6764 // A new event is started when the previous origin does not match 6928 // A new event is started when the previous origin does not match
6765 // the current, or the origins don't allow matching. Origins 6929 // the current, or the origins don't allow matching. Origins
6766 // starting with * are always merged, those starting with + are 6930 // starting with * are always merged, those starting with + are
6767 // merged when similar and close together in time. 6931 // merged when similar and close together in time.
6768 if (opId == hist.lastOp || 6932 if (opId == hist.lastSelOp ||
6769 (origin && hist.lastSelOrigin == origin && 6933 (origin && hist.lastSelOrigin == origin &&
6770 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || 6934 (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
6771 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) 6935 selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
6772 hist.done[hist.done.length - 1] = sel; 6936 hist.done[hist.done.length - 1] = sel;
6773 else 6937 else
6774 pushSelectionToHistory(sel, hist.done); 6938 pushSelectionToHistory(sel, hist.done);
6775 6939
6776 hist.lastSelTime = +new Date; 6940 hist.lastSelTime = +new Date;
6777 hist.lastSelOrigin = origin; 6941 hist.lastSelOrigin = origin;
6778 hist.lastOp = opId; 6942 hist.lastSelOp = opId;
6779 if (options && options.clearRedo !== false) 6943 if (options && options.clearRedo !== false)
6780 clearSelectionEvents(hist.undone); 6944 clearSelectionEvents(hist.undone);
6781 } 6945 }
6782 6946
6783 function pushSelectionToHistory(sel, dest) { 6947 function pushSelectionToHistory(sel, dest) {
6784 var top = lst(dest); 6948 var top = lst(dest);
6785 if (!(top && top.ranges && top.equals(sel))) 6949 if (!(top && top.ranges && top.equals(sel)))
6786 dest.push(sel); 6950 dest.push(sel);
6787 } 6951 }
6788 6952
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
6953 } 7117 }
6954 }; 7118 };
6955 7119
6956 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) { 7120 var signal = CodeMirror.signal = function(emitter, type /*, values...*/) {
6957 var arr = emitter._handlers && emitter._handlers[type]; 7121 var arr = emitter._handlers && emitter._handlers[type];
6958 if (!arr) return; 7122 if (!arr) return;
6959 var args = Array.prototype.slice.call(arguments, 2); 7123 var args = Array.prototype.slice.call(arguments, 2);
6960 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args); 7124 for (var i = 0; i < arr.length; ++i) arr[i].apply(null, args);
6961 }; 7125 };
6962 7126
7127 var orphanDelayedCallbacks = null;
7128
6963 // Often, we want to signal events at a point where we are in the 7129 // Often, we want to signal events at a point where we are in the
6964 // middle of some work, but don't want the handler to start calling 7130 // middle of some work, but don't want the handler to start calling
6965 // other methods on the editor, which might be in an inconsistent 7131 // other methods on the editor, which might be in an inconsistent
6966 // state or simply not expect any other events to happen. 7132 // state or simply not expect any other events to happen.
6967 // signalLater looks whether there are any handlers, and schedules 7133 // signalLater looks whether there are any handlers, and schedules
6968 // them to be executed when the last operation ends, or, if no 7134 // them to be executed when the last operation ends, or, if no
6969 // operation is active, when a timeout fires. 7135 // operation is active, when a timeout fires.
6970 var delayedCallbacks, delayedCallbackDepth = 0;
6971 function signalLater(emitter, type /*, values...*/) { 7136 function signalLater(emitter, type /*, values...*/) {
6972 var arr = emitter._handlers && emitter._handlers[type]; 7137 var arr = emitter._handlers && emitter._handlers[type];
6973 if (!arr) return; 7138 if (!arr) return;
6974 var args = Array.prototype.slice.call(arguments, 2); 7139 var args = Array.prototype.slice.call(arguments, 2), list;
6975 if (!delayedCallbacks) { 7140 if (operationGroup) {
6976 ++delayedCallbackDepth; 7141 list = operationGroup.delayedCallbacks;
6977 delayedCallbacks = []; 7142 } else if (orphanDelayedCallbacks) {
6978 setTimeout(fireDelayed, 0); 7143 list = orphanDelayedCallbacks;
7144 } else {
7145 list = orphanDelayedCallbacks = [];
7146 setTimeout(fireOrphanDelayed, 0);
6979 } 7147 }
6980 function bnd(f) {return function(){f.apply(null, args);};}; 7148 function bnd(f) {return function(){f.apply(null, args);};};
6981 for (var i = 0; i < arr.length; ++i) 7149 for (var i = 0; i < arr.length; ++i)
6982 delayedCallbacks.push(bnd(arr[i])); 7150 list.push(bnd(arr[i]));
6983 } 7151 }
6984 7152
6985 function fireDelayed() { 7153 function fireOrphanDelayed() {
6986 --delayedCallbackDepth; 7154 var delayed = orphanDelayedCallbacks;
6987 var delayed = delayedCallbacks; 7155 orphanDelayedCallbacks = null;
6988 delayedCallbacks = null;
6989 for (var i = 0; i < delayed.length; ++i) delayed[i](); 7156 for (var i = 0; i < delayed.length; ++i) delayed[i]();
6990 } 7157 }
6991 7158
6992 // The DOM events that CodeMirror handles can be overridden by 7159 // The DOM events that CodeMirror handles can be overridden by
6993 // registering a (non-DOM) handler on the editor for the event name, 7160 // registering a (non-DOM) handler on the editor for the event name,
6994 // and preventDefault-ing the event in that handler. 7161 // and preventDefault-ing the event in that handler.
6995 function signalDOMEvent(cm, e, override) { 7162 function signalDOMEvent(cm, e, override) {
6996 signal(cm, override || e.type, cm, e); 7163 signal(cm, override || e.type, cm, e);
6997 return e_defaultPrevented(e) || e.codemirrorIgnore; 7164 return e_defaultPrevented(e) || e.codemirrorIgnore;
6998 } 7165 }
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after
7324 return range.compareEndPoints("StartToEnd", range) != 0; 7491 return range.compareEndPoints("StartToEnd", range) != 0;
7325 }; 7492 };
7326 7493
7327 var hasCopyEvent = (function() { 7494 var hasCopyEvent = (function() {
7328 var e = elt("div"); 7495 var e = elt("div");
7329 if ("oncopy" in e) return true; 7496 if ("oncopy" in e) return true;
7330 e.setAttribute("oncopy", "return;"); 7497 e.setAttribute("oncopy", "return;");
7331 return typeof e.oncopy == "function"; 7498 return typeof e.oncopy == "function";
7332 })(); 7499 })();
7333 7500
7501 var badZoomedRects = null;
7502 function hasBadZoomedRects(measure) {
7503 if (badZoomedRects != null) return badZoomedRects;
7504 var node = removeChildrenAndAdd(measure, elt("span", "x"));
7505 var normal = node.getBoundingClientRect();
7506 var fromRange = range(node, 0, 1).getBoundingClientRect();
7507 return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1;
7508 }
7509
7334 // KEY NAMES 7510 // KEY NAMES
7335 7511
7336 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift" , 17: "Ctrl", 18: "Alt", 7512 var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift" , 17: "Ctrl", 18: "Alt",
7337 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "Page Up", 34: "PageDown", 35: "End", 7513 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "Page Up", 34: "PageDown", 35: "End",
7338 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 7514 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
7339 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod ", 107: "=", 109: "-", 127: "Delete", 7515 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod ", 107: "=", 109: "-", 127: "Delete",
7340 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 19 1: "/", 192: "`", 219: "[", 220: "\\", 7516 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 19 1: "/", 192: "`", 219: "[", 220: "\\",
7341 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 7517 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
7342 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown ", 63302: "Insert"}; 7518 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown ", 63302: "Insert"};
7343 CodeMirror.keyNames = keyNames; 7519 CodeMirror.keyNames = keyNames;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
7386 function lineEnd(cm, lineN) { 7562 function lineEnd(cm, lineN) {
7387 var merged, line = getLine(cm.doc, lineN); 7563 var merged, line = getLine(cm.doc, lineN);
7388 while (merged = collapsedSpanAtEnd(line)) { 7564 while (merged = collapsedSpanAtEnd(line)) {
7389 line = merged.find(1, true).line; 7565 line = merged.find(1, true).line;
7390 lineN = null; 7566 lineN = null;
7391 } 7567 }
7392 var order = getOrder(line); 7568 var order = getOrder(line);
7393 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l ineRight(line); 7569 var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : l ineRight(line);
7394 return Pos(lineN == null ? lineNo(line) : lineN, ch); 7570 return Pos(lineN == null ? lineNo(line) : lineN, ch);
7395 } 7571 }
7572 function lineStartSmart(cm, pos) {
7573 var start = lineStart(cm, pos.line);
7574 var line = getLine(cm.doc, start.line);
7575 var order = getOrder(line);
7576 if (!order || order[0].level == 0) {
7577 var firstNonWS = Math.max(0, line.text.search(/\S/));
7578 var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
7579 return Pos(start.line, inWS ? 0 : firstNonWS);
7580 }
7581 return start;
7582 }
7396 7583
7397 function compareBidiLevel(order, a, b) { 7584 function compareBidiLevel(order, a, b) {
7398 var linedir = order[0].level; 7585 var linedir = order[0].level;
7399 if (a == linedir) return true; 7586 if (a == linedir) return true;
7400 if (b == linedir) return false; 7587 if (b == linedir) return false;
7401 return a < b; 7588 return a < b;
7402 } 7589 }
7403 var bidiOther; 7590 var bidiOther;
7404 function getBidiPartAt(order, pos) { 7591 function getBidiPartAt(order, pos) {
7405 bidiOther = null; 7592 bidiOther = null;
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
7625 } 7812 }
7626 if (order[0].level != lst(order).level) 7813 if (order[0].level != lst(order).level)
7627 order.push(new BidiSpan(order[0].level, len, len)); 7814 order.push(new BidiSpan(order[0].level, len, len));
7628 7815
7629 return order; 7816 return order;
7630 }; 7817 };
7631 })(); 7818 })();
7632 7819
7633 // THE END 7820 // THE END
7634 7821
7635 CodeMirror.version = "4.3.1"; 7822 CodeMirror.version = "4.4.1";
7636 7823
7637 return CodeMirror; 7824 return CodeMirror;
7638 }); 7825 });
OLDNEW
« no previous file with comments | « LayoutTests/inspector/editor/editor-test.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698