From 253977070b51076e66e2046dd27beeed1023b1bf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 12:06:58 +0100 Subject: [PATCH 001/195] Bump version number --- lib/codemirror.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 67e5291f67..ecd0355e3d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -6046,7 +6046,7 @@ window.CodeMirror = (function() { // THE END - CodeMirror.version = "3.21.1"; + CodeMirror.version = "4.0.0"; return CodeMirror; })(); diff --git a/package.json b/package.json index e8a7c18ac4..8edf5e8a3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"3.21.1", + "version":"4.0.0", "main": "lib/codemirror.js", "description": "In-browser code editing made bearable", "licenses": [{"type": "MIT", From 196d5ca23dcb9b67165ffb778b74f3a47c01c7f1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Nov 2013 11:42:50 +0100 Subject: [PATCH 002/195] Wrap all cursors in a div --- lib/codemirror.css | 14 ++++++++------ lib/codemirror.js | 44 +++++++++++++++++++++----------------------- test/test.js | 7 +++++-- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/lib/codemirror.css b/lib/codemirror.css index 23eaf74d44..c141f6d85f 100644 --- a/lib/codemirror.css +++ b/lib/codemirror.css @@ -42,7 +42,6 @@ .CodeMirror div.CodeMirror-cursor { border-left: 1px solid black; - z-index: 3; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { @@ -52,10 +51,9 @@ width: auto; border: 0; background: #7e7; - z-index: 1; } /* Can style cursor different in overwrite (non-insert) mode */ -.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} +div.CodeMirror-overwrite div.CodeMirror-cursor {} .cm-tab { display: inline-block; } @@ -236,11 +234,15 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror div.CodeMirror-cursor { position: absolute; - visibility: hidden; border-right: none; width: 0; } -.CodeMirror-focused div.CodeMirror-cursor { + +div.CodeMirror-cursors { + visibility: hidden; + z-index: 3; +} +.CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } @@ -257,7 +259,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} @media print { /* Hide the cursor when printing */ - .CodeMirror div.CodeMirror-cursor { + .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } diff --git a/lib/codemirror.js b/lib/codemirror.js index ecd0355e3d..107660eb03 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -116,15 +116,12 @@ window.CodeMirror = (function() { // DIVs containing the selection and the actual code d.lineDiv = elt("div", null, "CodeMirror-code"); d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); - // Blinky cursor, and element used to ensure cursor fits at the end of a line - d.cursor = elt("div", "\u00a0", "CodeMirror-cursor"); - // Secondary cursor, shown when on a 'jump' in bi-directional text - d.otherCursor = elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"); + d.cursorDiv = elt("div", null, "CodeMirror-cursors"); // Used to measure text size d.measure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system - d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursor, d.otherCursor], - null, "position: relative; outline: none"); + d.lineSpace = elt("div", [d.measure, d.selectionDiv, d.lineDiv, d.cursorDiv], + null, "position: relative; outline: none"); // Moved around its parent to cover visible view d.mover = elt("div", [elt("div", [d.lineSpace], "CodeMirror-lines")], null, "position: relative"); // Set to the height of the text, causes scrolling @@ -778,15 +775,13 @@ window.CodeMirror = (function() { function updateSelection(cm) { var display = cm.display; + removeChildren(display.cursorDiv); + removeChildren(display.selectionDiv); var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to); if (collapsed || cm.options.showCursorWhenSelecting) updateSelectionCursor(cm); - else - display.cursor.style.display = display.otherCursor.style.display = "none"; if (!collapsed) updateSelectionRange(cm); - else - display.selectionDiv.style.display = "none"; // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { @@ -802,17 +797,20 @@ window.CodeMirror = (function() { // No selection, plain cursor function updateSelectionCursor(cm) { var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div"); - display.cursor.style.left = pos.left + "px"; - display.cursor.style.top = pos.top + "px"; - display.cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; - display.cursor.style.display = ""; + + var cursor = display.cursorDiv.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); + cursor.style.left = pos.left + "px"; + cursor.style.top = pos.top + "px"; + cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; if (pos.other) { - display.otherCursor.style.display = ""; - display.otherCursor.style.left = pos.other.left + "px"; - display.otherCursor.style.top = pos.other.top + "px"; - display.otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; - } else { display.otherCursor.style.display = "none"; } + // Secondary cursor, shown when on a 'jump' in bi-directional text + var otherCursor = display.cursorDiv.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); + otherCursor.style.display = ""; + otherCursor.style.left = pos.other.left + "px"; + otherCursor.style.top = pos.other.top + "px"; + otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; + } } // Highlight selection @@ -893,10 +891,10 @@ window.CodeMirror = (function() { var display = cm.display; clearInterval(display.blinker); var on = true; - display.cursor.style.visibility = display.otherCursor.style.visibility = ""; + display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) display.blinker = setInterval(function() { - display.cursor.style.visibility = display.otherCursor.style.visibility = (on = !on) ? "" : "hidden"; + display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } @@ -3220,9 +3218,9 @@ window.CodeMirror = (function() { toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) return; if (this.state.overwrite = !this.state.overwrite) - this.display.cursor.className += " CodeMirror-overwrite"; + this.display.cursorDiv.className += " CodeMirror-overwrite"; else - this.display.cursor.className = this.display.cursor.className.replace(" CodeMirror-overwrite", ""); + this.display.cursorDiv.className = this.display.cursorDiv.className.replace(" CodeMirror-overwrite", ""); }, hasFocus: function() { return document.activeElement == this.display.input; }, diff --git a/test/test.js b/test/test.js index 1d7f80e355..6661645cd4 100644 --- a/test/test.js +++ b/test/test.js @@ -1210,18 +1210,21 @@ testCM("rtlMovement", function(cm) { ""], function(line) { var inv = line.charAt(0) == "خ"; cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart"); - var cursor = byClassName(cm.getWrapperElement(), "CodeMirror-cursor")[0]; + var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0]; + var cursor = cursors.firstChild; var prevX = cursor.offsetLeft, prevY = cursor.offsetTop; for (var i = 0; i <= line.length; ++i) { cm.execCommand("goCharRight"); + cursor = cursors.firstChild; if (i == line.length) is(cursor.offsetTop > prevY, "next line"); else is(cursor.offsetLeft > prevX, "moved right"); prevX = cursor.offsetLeft; prevY = cursor.offsetTop; } cm.setCursor(0, 0); cm.execCommand(inv ? "goLineStart" : "goLineEnd"); - prevX = cursor.offsetLeft; + prevX = cursors.firstChild.offsetLeft; for (var i = 0; i < line.length; ++i) { cm.execCommand("goCharLeft"); + cursor = cursors.firstChild; is(cursor.offsetLeft < prevX, "moved left"); prevX = cursor.offsetLeft; } From f55cf1888af6e1ccaf9fdf5cf659399bb36e18f4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Nov 2013 12:25:03 +0100 Subject: [PATCH 003/195] Move extend and shift flags out of selection objects --- lib/codemirror.js | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 107660eb03..def4991a26 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -185,6 +185,8 @@ window.CodeMirror = (function() { // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; + d.shift = false; + return d; } @@ -1500,7 +1502,7 @@ window.CodeMirror = (function() { var withOp = !cm.curOp; if (withOp) startOperation(cm); - sel.shift = false; + cm.display.shift = false; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; var from = sel.from, to = sel.to; @@ -1629,7 +1631,7 @@ window.CodeMirror = (function() { on(d.input, "keyup", operation(cm, function(e) { if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; - if (e.keyCode == 16) cm.doc.sel.shift = false; + if (e.keyCode == 16) cm.display.shift = false; })); on(d.input, "input", function() { if (ie && !ie_lt9 && cm.display.inputHasSelection) cm.display.inputHasSelection = null; @@ -1713,7 +1715,7 @@ window.CodeMirror = (function() { function onMouseDown(e) { if (signalDOMEvent(this, e)) return; var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; - sel.shift = e.shiftKey; + display.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { @@ -2072,13 +2074,13 @@ window.CodeMirror = (function() { // Ensure previous input has been read, so that the handler sees a // consistent view of the document if (cm.display.pollingFast && readInput(cm)) cm.display.pollingFast = false; - var doc = cm.doc, prevShift = doc.sel.shift, done = false; + var prevShift = cm.display.shift, done = false; try { if (isReadOnly(cm)) cm.state.suppressEdits = true; - if (dropShift) doc.sel.shift = false; + if (dropShift) cm.display.shift = false; done = bound(cm) != Pass; } finally { - doc.sel.shift = prevShift; + cm.display.shift = prevShift; cm.state.suppressEdits = false; } return done; @@ -2148,7 +2150,7 @@ window.CodeMirror = (function() { if (old_ie && e.keyCode == 27) e.returnValue = false; var code = e.keyCode; // IE does strange things with escape. - cm.doc.sel.shift = code == 16 || e.shiftKey; + cm.display.shift = code == 16 || e.shiftKey; // First give onKeyEvent option a chance to handle this. var handled = handleKeyBinding(cm, e); if (opera) { @@ -2193,7 +2195,7 @@ window.CodeMirror = (function() { cm.display.wrapper.className = cm.display.wrapper.className.replace(" CodeMirror-focused", ""); } clearInterval(cm.display.blinker); - setTimeout(function() {if (!cm.state.focused) cm.doc.sel.shift = false;}, 150); + setTimeout(function() {if (!cm.state.focused) cm.display.shift = false;}, 150); } var detectingSelectAll; @@ -2544,7 +2546,7 @@ window.CodeMirror = (function() { // If shift is held, this will move the selection anchor. Otherwise, // it'll set the whole selection. function extendSelection(doc, pos, other, bias) { - if (doc.sel.shift || doc.sel.extend) { + if (doc.cm && doc.cm.display.shift || doc.extend) { var anchor = doc.sel.anchor; if (other) { var posBefore = posLess(pos, anchor); @@ -3173,7 +3175,7 @@ window.CodeMirror = (function() { moveH: operation(null, function(dir, unit) { var sel = this.doc.sel, pos; - if (sel.shift || sel.extend || posEq(sel.from, sel.to)) + if (this.shift || this.doc.extend || posEq(sel.from, sel.to)) pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually); else pos = dir < 0 ? sel.from : sel.to; @@ -4901,7 +4903,7 @@ window.CodeMirror = (function() { this.cleanGeneration = 1; this.frontier = firstLine; var start = Pos(firstLine, 0); - this.sel = {from: start, to: start, head: start, anchor: start, shift: false, extend: false, goalColumn: null}; + this.sel = {from: start, to: start, head: start, anchor: start, goalColumn: null}; this.id = ++nextDocId; this.modeOption = mode; @@ -4998,7 +5000,7 @@ window.CodeMirror = (function() { undo: docOperation(function() {makeChangeFromHistory(this, "undo");}), redo: docOperation(function() {makeChangeFromHistory(this, "redo");}), - setExtending: function(val) {this.sel.extend = val;}, + setExtending: function(val) {this.extend = val;}, historySize: function() { var hist = this.history; @@ -5083,7 +5085,8 @@ window.CodeMirror = (function() { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor, - shift: this.sel.shift, extend: false, goalColumn: this.sel.goalColumn}; + goalColumn: this.sel.goalColumn}; + doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; doc.setHistory(this.getHistory()); From 71d992ba4a835fd8e56dfb76d2f5c291128039a3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 16:45:54 +0100 Subject: [PATCH 004/195] Rename confusing variable --- lib/codemirror.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index def4991a26..a18036e432 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -36,7 +36,7 @@ window.CodeMirror = (function() { if (opera_version && opera_version >= 15) { opera = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || opera && (opera_version == null || opera_version < 12.11)); - var captureMiddleClick = gecko || (old_ie && !ie_lt9); + var captureRightClick = gecko || (old_ie && !ie_lt9); // Optimize some code when these features are not used var sawReadOnlySpans = false, sawCollapsedSpans = false; @@ -1583,7 +1583,7 @@ window.CodeMirror = (function() { // Gecko browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for Gecko. - if (!captureMiddleClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); + if (!captureRightClick) on(d.scroller, "contextmenu", function(e) {onContextMenu(cm, e);}); on(d.scroller, "scroll", function() { if (d.scroller.clientHeight) { @@ -1729,7 +1729,7 @@ window.CodeMirror = (function() { switch (e_button(e)) { case 3: - if (captureMiddleClick) onContextMenu.call(cm, cm, e); + if (captureRightClick) onContextMenu.call(cm, cm, e); return; case 2: if (webkit) cm.state.lastMiddleDown = +new Date; @@ -2251,7 +2251,7 @@ window.CodeMirror = (function() { } if (old_ie && !ie_lt9) prepareSelectAllHack(); - if (captureMiddleClick) { + if (captureRightClick) { e_stop(e); var mouseup = function() { off(window, "mouseup", mouseup); From 324ca9bed60324f6b64a4b306f4b110131c6f45a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 5 Nov 2013 13:46:59 +0100 Subject: [PATCH 005/195] Implement multiple selections Issue #778 --- keymap/vim.js | 7 +- lib/codemirror.js | 694 +++++++++++++++++++++++++++++++++++------------------- test/test.js | 83 ++++++- 3 files changed, 535 insertions(+), 249 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 21c74c4cf4..9e5b257f4f 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -344,13 +344,14 @@ cm.state.vim = null; } }); - function beforeSelectionChange(cm, cur) { + function beforeSelectionChange(cm, obj) { var vim = cm.state.vim; if (vim.insertMode || vim.exMode) return; - var head = cur.head; + var head = obj.ranges[0].head; if (head.ch && head.ch == cm.doc.getLine(head.line).length) { - head.ch--; + obj.update([{anchor: obj.ranges[0].anchor, + head: {line: head.line, ch: head.ch - 1}}]); } } function getOnPasteFn(cm) { diff --git a/lib/codemirror.js b/lib/codemirror.js index a18036e432..d07f0007ec 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -775,19 +775,90 @@ window.CodeMirror = (function() { // SELECTION / CURSOR + function Selection(ranges, primIndex) { + this.ranges = ranges; + this.primIndex = primIndex; + } + + Selection.prototype = { + primary: function() { return this.ranges[this.primIndex]; }, + equals: function(other) { + if (other == this) return true; + if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) return false; + for (var i = 0; i < this.ranges.length; i++) { + var here = this.ranges[i], there = other.ranges[i]; + if (cmp(here.anchor, there.anchor) != 0 || cmp(here.head, there.head) != 0) return false; + } + return true; + }, + deepCopy: function() { + for (var out = [], i = 0; i < this.ranges.length; i++) + out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); + return new Selection(out, this.primIndex); + }, + somethingSelected: function() { + for (var i = 0; i < this.ranges.length; i++) + if (!this.ranges[i].empty()) return true; + }, + contains: function(pos, end) { + if (!end) end = pos; + for (var i = 0; i < this.ranges.length; i++) { + var range = this.ranges[i]; + if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) + return true; + } + } + }; + + function normalizeSelection(ranges, primIndex) { + ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); + for (var i = 1; i < ranges.length; i++) { + var cur = ranges[i], prev = ranges[i - 1]; + if (cmp(prev.to(), cur.from()) >= 0) { + var from = prev.from(), to = cur.to(), inv = prev.head == from; + if (i <= primIndex) --primIndex; + ranges.splice(i-- - 1, 2, new Range(inv ? to : from, inv ? from : to)); + } + } + return new Selection(ranges, primIndex); + } + + function simpleSelection(anchor, head) { + return new Selection([new Range(anchor, head || anchor)], 0); + } + + function Range(anchor, head) { + this.anchor = anchor; this.head = head; + } + + Range.prototype = { + from: function() { + return cmp(this.anchor, this.head) < 0 ? this.anchor : this.head; + }, + to: function() { + return cmp(this.anchor, this.head) > 0 ? this.anchor : this.head; + }, + empty: function() { + return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch; + } + }; + function updateSelection(cm) { - var display = cm.display; + var display = cm.display, doc = cm.doc; removeChildren(display.cursorDiv); removeChildren(display.selectionDiv); - var collapsed = posEq(cm.doc.sel.from, cm.doc.sel.to); - if (collapsed || cm.options.showCursorWhenSelecting) - updateSelectionCursor(cm); - if (!collapsed) - updateSelectionRange(cm); + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + var collapsed = range.empty(); + if (collapsed || cm.options.showCursorWhenSelecting) + updateSelectionCursor(cm, range); + if (!collapsed) + updateSelectionRange(cm, range); + } // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { - var headPos = cursorCoords(cm, cm.doc.sel.head, "div"); + var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = getRect(display.wrapper), lineOff = getRect(display.lineDiv); display.inputDiv.style.top = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)) + "px"; @@ -797,8 +868,8 @@ window.CodeMirror = (function() { } // No selection, plain cursor - function updateSelectionCursor(cm) { - var display = cm.display, pos = cursorCoords(cm, cm.doc.sel.head, "div"); + function updateSelectionCursor(cm, range) { + var display = cm.display, pos = cursorCoords(cm, range.head, "div"); var cursor = display.cursorDiv.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); cursor.style.left = pos.left + "px"; @@ -816,8 +887,8 @@ window.CodeMirror = (function() { } // Highlight selection - function updateSelectionRange(cm) { - var display = cm.display, doc = cm.doc, sel = cm.doc.sel; + function updateSelectionRange(cm, range) { + var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var clientWidth = display.lineSpace.offsetWidth, pl = paddingLeft(cm.display); @@ -864,13 +935,14 @@ window.CodeMirror = (function() { return {start: start, end: end}; } - if (sel.from.line == sel.to.line) { - drawForLine(sel.from.line, sel.from.ch, sel.to.ch); + var sFrom = range.from(), sTo = range.to(); + if (sFrom.line == sTo.line) { + drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { - var fromLine = getLine(doc, sel.from.line), toLine = getLine(doc, sel.to.line); + var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine); - var leftEnd = drawForLine(sel.from.line, sel.from.ch, singleVLine ? fromLine.text.length : null).end; - var rightStart = drawForLine(sel.to.line, singleVLine ? 0 : null, sel.to.ch).start; + var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length : null).end; + var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); @@ -883,7 +955,7 @@ window.CodeMirror = (function() { add(pl, leftEnd.bottom, null, rightStart.top); } - removeChildrenAndAdd(display.selectionDiv, fragment); + display.selectionDiv.appendChild(fragment); display.selectionDiv.style.display = ""; } @@ -1385,7 +1457,7 @@ window.CodeMirror = (function() { if (op.updateScrollPos) { newScrollPos = op.updateScrollPos; } else if (op.selectionChanged && display.scroller.clientHeight) { // don't rescroll if not visible - var coords = cursorCoords(cm, doc.sel.head); + var coords = cursorCoords(cm, doc.sel.primary().head); newScrollPos = calculateScrollPos(cm, coords.left, coords.top, coords.left, coords.bottom); } if (op.changes.length || op.forceUpdate || newScrollPos && newScrollPos.scrollTop != null) { @@ -1487,14 +1559,14 @@ window.CodeMirror = (function() { // events that indicate IME taking place, but these are not widely // supported or compatible enough yet to rely on.) function readInput(cm) { - var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc, sel = doc.sel; + var input = cm.display.input, prevInput = cm.display.prevInput, doc = cm.doc; if (!cm.state.focused || hasSelection(input) || isReadOnly(cm) || cm.options.disableInput) return false; if (cm.state.pasteIncoming && cm.state.fakedLastChar) { input.value = input.value.substring(0, input.value.length - 1); cm.state.fakedLastChar = false; } var text = input.value; - if (text == prevInput && posEq(sel.from, sel.to)) return false; + if (text == prevInput && !cm.somethingSelected()) return false; if (ie && !ie_lt9 && cm.display.inputHasSelection === text) { resetInput(cm, true); return false; @@ -1505,28 +1577,32 @@ window.CodeMirror = (function() { cm.display.shift = false; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; - var from = sel.from, to = sel.to; var inserted = text.slice(same); - if (same < prevInput.length) - from = Pos(from.line, from.ch - (prevInput.length - same)); - else if (cm.state.overwrite && posEq(from, to) && !cm.state.pasteIncoming) - to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + inserted.length)); - - var updateInput = cm.curOp.updateInput; - var changeEvent = {from: from, to: to, text: splitLines(inserted), - origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; - makeChange(cm.doc, changeEvent, "end"); - cm.curOp.updateInput = updateInput; - signalLater(cm, "inputRead", cm, changeEvent); - if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && - cm.options.smartIndent && sel.head.ch < 100) { - var electric = cm.getModeAt(sel.head).electricChars; - if (electric) for (var i = 0; i < electric.length; i++) - if (inserted.indexOf(electric.charAt(i)) > -1) { - indentLine(cm, sel.head.line, "smart"); - break; - } + for (var i = doc.sel.ranges.length - 1; i >= 0; i--) { + var range = doc.sel.ranges[i]; + var from = range.from(), to = range.to(); + if (same < prevInput.length) + from = Pos(from.line, from.ch - (prevInput.length - same)); + else if (cm.state.overwrite && cmp(from, to) == 0 && !cm.state.pasteIncoming) + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + inserted.length)); + + var updateInput = cm.curOp.updateInput; + var changeEvent = {from: from, to: to, text: splitLines(inserted), + origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; + makeChange(cm.doc, changeEvent); + signalLater(cm, "inputRead", cm, changeEvent); + if (inserted && !cm.state.pasteIncoming && cm.options.electricChars && + cm.options.smartIndent && range.head.ch < 100 && + (!i || doc.sel.ranges[i - 1].head.line != range.head.line)) { + var electric = cm.getModeAt(range.head).electricChars; + if (electric) for (var i = 0; i < electric.length; i++) + if (inserted.indexOf(electric.charAt(i)) > -1) { + indentLine(cm, range.head.line, "smart"); + break; + } + } } + cm.curOp.updateInput = updateInput; if (text.length > 1000 || text.indexOf("\n") > -1) input.value = cm.display.prevInput = ""; else cm.display.prevInput = text; @@ -1537,10 +1613,11 @@ window.CodeMirror = (function() { function resetInput(cm, user) { var minimal, selected, doc = cm.doc; - if (!posEq(doc.sel.from, doc.sel.to)) { + var range = doc.sel.primary(); + if (!range.empty()) { cm.display.prevInput = ""; minimal = hasCopyEvent && - (doc.sel.to.line - doc.sel.from.line > 100 || (selected = cm.getSelection()).length > 1000); + (range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000); var content = minimal ? "-" : selected || cm.getSelection(); cm.display.input.value = content; if (cm.state.focused) selectInput(cm.display.input); @@ -1549,7 +1626,7 @@ window.CodeMirror = (function() { cm.display.prevInput = cm.display.input.value = ""; if (ie && !ie_lt9) cm.display.inputHasSelection = null; } - cm.display.inaccurateSelection = minimal; + cm.display.inaccurateSelection = minimal || doc.sel.ranges.length > 1; } function focusInput(cm) { @@ -1714,7 +1791,7 @@ window.CodeMirror = (function() { var lastClick, lastDoubleClick; function onMouseDown(e) { if (signalDOMEvent(this, e)) return; - var cm = this, display = cm.display, doc = cm.doc, sel = doc.sel; + var cm = this, display = cm.display, doc = cm.doc; display.shift = e.shiftKey; if (eventInWidget(display, e)) { @@ -1746,12 +1823,12 @@ window.CodeMirror = (function() { if (!cm.state.focused) onFocus(cm); var now = +new Date, type = "single"; - if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { + if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { type = "triple"; e_preventDefault(e); setTimeout(bind(focusInput, cm), 20); selectLine(cm, start.line); - } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { + } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { type = "double"; lastDoubleClick = {time: now, pos: start}; e_preventDefault(e); @@ -1759,9 +1836,9 @@ window.CodeMirror = (function() { extendSelection(cm.doc, word.from, word.to); } else { lastClick = {time: now, pos: start}; } - var last = start; - if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !posEq(sel.from, sel.to) && - !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") { + var last = start, range = doc.sel.primary(); + if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !range.empty() && + type == "single" && doc.sel.contains(start) && doc.sel.somethingSelected()) { var dragEnd = operation(cm, function(e2) { if (webkit) display.scroller.draggable = false; cm.state.draggingText = false; @@ -1788,10 +1865,11 @@ window.CodeMirror = (function() { e_preventDefault(e); if (type == "single") extendSelection(cm.doc, clipPos(doc, start)); - var startstart = sel.from, startend = sel.to, lastPos = start; + // FIXME[sel] Build a consistent model for handling clicks + multisel + var startstart = range.from(), startend = range.to(), lastPos = start; function doSelect(cur) { - if (posEq(lastPos, cur)) return; + if (cmp(lastPos, cur) == 0) return; lastPos = cur; if (type == "single") { @@ -1803,10 +1881,10 @@ window.CodeMirror = (function() { startend = clipPos(doc, startend); if (type == "double") { var word = findWordAt(getLine(doc, cur.line).text, cur); - if (posLess(cur, startstart)) extendSelection(cm.doc, word.from, startend); + if (cmp(cur, startstart) < 0) extendSelection(cm.doc, word.from, startend); else extendSelection(cm.doc, startstart, word.to); } else if (type == "triple") { - if (posLess(cur, startstart)) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0))); + if (cmp(cur, startstart) < 0) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0))); else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0))); } } @@ -1822,7 +1900,7 @@ window.CodeMirror = (function() { var curCount = ++counter; var cur = posFromMouse(cm, e, true); if (!cur) return; - if (!posEq(cur, last)) { + if (cmp(cur, last) != 0) { if (!cm.state.focused) onFocus(cm); last = cur; doSelect(cur); @@ -1908,7 +1986,9 @@ window.CodeMirror = (function() { text[i] = reader.result; if (++read == n) { pos = clipPos(cm.doc, pos); - makeChange(cm.doc, {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}, "around"); + var change = {from: pos, to: pos, text: splitLines(text.join("\n")), origin: "paste"}; + makeChange(cm.doc, change); + setSelectionAddToHistory(cm.doc, simpleSelection(pos, changeEnd(change))); } }; reader.readAsText(file); @@ -1916,7 +1996,7 @@ window.CodeMirror = (function() { for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Don't do a replace if the drop happened inside of the selected text. - if (cm.state.draggingText && !(posLess(pos, cm.doc.sel.from) || posLess(cm.doc.sel.to, pos))) { + if (cm.state.draggingText && cm.doc.sel.contains(pos)) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(bind(focusInput, cm), 20); @@ -1925,9 +2005,10 @@ window.CodeMirror = (function() { try { var text = e.dataTransfer.getData("Text"); if (text) { - var curFrom = cm.doc.sel.from, curTo = cm.doc.sel.to; - setSelection(cm.doc, pos, pos); - if (cm.state.draggingText) replaceRange(cm.doc, "", curFrom, curTo, "paste"); + var selected = cm.state.draggingText && cm.getSelection(); + setSimpleSelection(cm.doc, pos); + if (selected) for (var i = 0; i < selected.length; ++i) + replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag"); cm.replaceSelection(text, null, "paste"); focusInput(cm); } @@ -2201,7 +2282,7 @@ window.CodeMirror = (function() { var detectingSelectAll; function onContextMenu(cm, e) { if (signalDOMEvent(cm, e, "contextmenu")) return; - var display = cm.display, sel = cm.doc.sel; + var display = cm.display; if (eventInWidget(display, e) || contextMenuInGutter(cm, e)) return; var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; @@ -2210,7 +2291,7 @@ window.CodeMirror = (function() { // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; - if (reset && (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))) + if (reset && !cm.doc.sel.contains(pos)) operation(cm, setSelection)(cm.doc, pos, pos); var oldCSS = display.input.style.cssText; @@ -2221,11 +2302,12 @@ window.CodeMirror = (function() { focusInput(cm); resetInput(cm, true); // Adds "Select all" to context menu in FF - if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " "; + var hasSelection = cm.somethingSelected(); + if (!hasSelection) display.input.value = display.prevInput = " "; function prepareSelectAllHack() { if (display.input.selectionStart != null) { - var extval = display.input.value = "\u200b" + (posEq(sel.from, sel.to) ? "" : display.input.value); + var extval = display.input.value = "\u200b" + (hasSelection ? display.input.value : ""); display.prevInput = "\u200b"; display.input.selectionStart = 1; display.input.selectionEnd = extval.length; } @@ -2271,44 +2353,50 @@ window.CodeMirror = (function() { lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)); }; - // Make sure a position will be valid after the given change. - function clipPostChange(doc, change, pos) { - if (!posLess(change.from, pos)) return clipPos(doc, pos); - var diff = (change.text.length - 1) - (change.to.line - change.from.line); - if (pos.line > change.to.line + diff) { - var preLine = pos.line - diff, lastLine = doc.first + doc.size - 1; - if (preLine > lastLine) return Pos(lastLine, getLine(doc, lastLine).text.length); - return clipToLen(pos, getLine(doc, preLine).text.length); - } - if (pos.line == change.to.line + diff) - return clipToLen(pos, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0) + - getLine(doc, change.to.line).text.length - change.to.ch); - var inside = pos.line - change.from.line; - return clipToLen(pos, change.text[inside].length + (inside ? 0 : change.from.ch)); - } - - // Hint can be null|"end"|"start"|"around"|{anchor,head} - function computeSelAfterChange(doc, change, hint) { - if (hint && typeof hint == "object") // Assumed to be {anchor, head} object - return {anchor: clipPostChange(doc, change, hint.anchor), - head: clipPostChange(doc, change, hint.head)}; - - if (hint == "start") return {anchor: change.from, head: change.from}; - - var end = changeEnd(change); - if (hint == "around") return {anchor: change.from, head: end}; - if (hint == "end") return {anchor: end, head: end}; - - // hint is null, leave the selection alone as much as possible - var adjustPos = function(pos) { - if (posLess(pos, change.from)) return pos; - if (!posLess(change.to, pos)) return end; - - var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; - if (pos.line == change.to.line) ch += end.ch - change.to.ch; - return Pos(line, ch); - }; - return {anchor: adjustPos(doc.sel.anchor), head: adjustPos(doc.sel.head)}; + function adjustForChange(pos, change) { + if (cmp(pos, change.from) < 0) return pos; + if (cmp(pos, change.to) <= 0) return changeEnd(change); + + var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; + if (pos.line == change.to.line) ch += changeEnd(change).ch - change.to.ch; + return Pos(line, ch); + } + + function computeSelAfterChange(doc, change) { + var out = []; + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + out.push(new Range(adjustForChange(range.anchor, change), + adjustForChange(range.head, change))); + } + return normalizeSelection(out, doc.sel.primIndex); + } + + function offsetPos(pos, old, nw) { + if (pos.line == old.line) + return Pos(nw.line, pos.ch - old.ch + nw.ch); + else + return Pos(nw.line + (pos.line - old.line), pos.ch); + } + + // Hint can be "start"|"around" + function computeReplacedSel(doc, changes, hint) { + var out = []; + var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; + for (var i = 0; i < changes.length; i++) { + var change = changes[i]; + var from = offsetPos(change.from, oldPrev, newPrev); + var to = offsetPos(changeEnd(change), oldPrev, newPrev); + oldPrev = change.to; + newPrev = to; + if (hint == "around") { + var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; + out[i] = new Range(inv ? to : from, inv ? from : to); + } else { + out[i] = new Range(from, from); + } + } + return new Selection(out, doc.sel.primIndex); } function filterChange(doc, change, update) { @@ -2335,9 +2423,9 @@ window.CodeMirror = (function() { // Replace the range from from to to by the strings in replacement. // change is a {from, to, text [, origin]} object - function makeChange(doc, change, selUpdate, ignoreReadOnly) { + function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { - if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, selUpdate, ignoreReadOnly); + if (!doc.cm.curOp) return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly); if (doc.cm.state.suppressEdits) return; } @@ -2350,18 +2438,16 @@ window.CodeMirror = (function() { // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { - for (var i = split.length - 1; i >= 1; --i) - makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: [""]}); - if (split.length) - makeChangeNoReadonly(doc, {from: split[0].from, to: split[0].to, text: change.text}, selUpdate); + for (var i = split.length - 1; i >= 0; --i) + makeChangeNoReadonly(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); } else { - makeChangeNoReadonly(doc, change, selUpdate); + makeChangeNoReadonly(doc, change); } } - function makeChangeNoReadonly(doc, change, selUpdate) { - if (change.text.length == 1 && change.text[0] == "" && posEq(change.from, change.to)) return; - var selAfter = computeSelAfterChange(doc, change, selUpdate); + function makeChangeNoReadonly(doc, change) { + if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) return; + var selAfter = computeSelAfterChange(doc, change); addToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); @@ -2383,8 +2469,7 @@ window.CodeMirror = (function() { var event = (type == "undo" ? hist.done : hist.undone).pop(); if (!event) return; - var anti = {changes: [], anchorBefore: event.anchorAfter, headBefore: event.headAfter, - anchorAfter: event.anchorBefore, headAfter: event.headBefore, + var anti = {changes: [], selBefore: event.selAfter, selAfter: event.selBefore, generation: hist.generation}; (type == "undo" ? hist.undone : hist.done).push(anti); hist.generation = event.generation || ++hist.maxGeneration; @@ -2401,8 +2486,7 @@ window.CodeMirror = (function() { anti.changes.push(historyChangeFromChange(doc, change)); - var after = i ? computeSelAfterChange(doc, change, null) - : {anchor: event.anchorBefore, head: event.headBefore}; + var after = i ? computeSelAfterChange(doc, change, null) : event.selBefore; makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); var rebased = []; @@ -2417,11 +2501,12 @@ window.CodeMirror = (function() { } function shiftDoc(doc, distance) { - function shiftPos(pos) {return Pos(pos.line + distance, pos.ch);} doc.first += distance; + doc.sel = new Selection(map(doc.sel.ranges, function(range) { + return new Range(Pos(range.anchor.line + distance, range.anchor.ch), + Pos(range.head.line + distance, range.head.ch)); + }), doc.sel.primIndex); if (doc.cm) regChange(doc.cm, doc.first, doc.first, distance); - doc.sel.head = shiftPos(doc.sel.head); doc.sel.anchor = shiftPos(doc.sel.anchor); - doc.sel.from = shiftPos(doc.sel.from); doc.sel.to = shiftPos(doc.sel.to); } function makeChangeSingleDoc(doc, change, selAfter, spans) { @@ -2468,7 +2553,7 @@ window.CodeMirror = (function() { }); } - if (!posLess(doc.sel.head, change.from) && !posLess(change.to, doc.sel.head)) + if (doc.sel.contains(change.from, change.to)) cm.curOp.cursorActivity = true; updateDoc(doc, change, spans, selAfter, estimateHeight(cm)); @@ -2508,9 +2593,9 @@ window.CodeMirror = (function() { function replaceRange(doc, code, from, to, origin) { if (!to) to = from; - if (posLess(to, from)) { var tmp = to; to = from; from = tmp; } + if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } if (typeof code == "string") code = splitLines(code); - makeChange(doc, {from: from, to: to, text: code, origin: origin}, null); + makeChange(doc, {from: from, to: to, text: code, origin: origin}); } // POSITION OBJECT @@ -2521,9 +2606,7 @@ window.CodeMirror = (function() { } CodeMirror.Pos = Pos; - function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} - function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} - function cmp(a, b) {return a.line - b.line || a.ch - b.ch;} + var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; }; function copyPos(x) {return Pos(x.line, x.ch);} // SELECTION @@ -2543,70 +2626,113 @@ window.CodeMirror = (function() { } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} - // If shift is held, this will move the selection anchor. Otherwise, - // it'll set the whole selection. - function extendSelection(doc, pos, other, bias) { + function extendRange(doc, range, pos, other) { if (doc.cm && doc.cm.display.shift || doc.extend) { - var anchor = doc.sel.anchor; + var anchor = range.anchor; if (other) { - var posBefore = posLess(pos, anchor); - if (posBefore != posLess(other, anchor)) { + var posBefore = cmp(pos, anchor) < 0; + if (posBefore != (cmp(other, anchor) < 0)) { anchor = pos; pos = other; - } else if (posBefore != posLess(pos, other)) { + } else if (posBefore != (cmp(pos, other) < 0)) { pos = other; } } - setSelection(doc, anchor, pos, bias); + return new Range(anchor, pos); } else { - setSelection(doc, pos, other || pos, bias); + return new Range(pos, other || pos); } + } + + // If shift is held, this will move the selection anchor. Otherwise, + // it'll set the whole selection. + function extendSelection(doc, pos, other, bias) { + var prim = doc.sel.primary(); + replaceOneSelection(doc, doc.sel.primIndex, extendRange(doc, prim, pos, other), bias); + if (doc.cm) doc.cm.curOp.userSelChange = true; + } + + // Extend all selections (pos is an array of selections with length + // equal the number of selections) + function extendSelections(doc, pos, other, bias) { + for (var out = [], i = 0; i < doc.sel.ranges.length; i++) + out[i] = extendRange(doc, doc.sel.ranges[i], pos[i], other && other[i]); + var newSel = normalizeSelection(out, doc.sel.primIndex); + setSelection(doc, newSel, bias); if (doc.cm) doc.cm.curOp.userSelChange = true; } - function filterSelectionChange(doc, anchor, head) { - var obj = {anchor: anchor, head: head}; + function replaceOneSelection(doc, i, range, bias) { + var ranges = doc.sel.ranges.slice(0); + ranges[i] = range; + setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), bias); + } + + function setSimpleSelection(doc, anchor, head, bias, checkAtomic) { + setSelection(doc, simpleSelection(anchor, head), bias, checkAtomic); + } + + function setSelectionAddToHistory(doc, sel) { + setSelection(doc, sel); + lst(doc.history.done).selAfter = sel; + } + + function filterSelectionChange(doc, sel) { + var obj = { + ranges: sel.ranges, + update: function(ranges) { + this.ranges = []; + for (var i = 0; i < ranges.length; i++) + this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), + clipPos(doc, ranges[i].head)); + } + }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) signal(doc.cm, "beforeSelectionChange", doc.cm, obj); - obj.anchor = clipPos(doc, obj.anchor); obj.head = clipPos(doc, obj.head); - return obj; + if (obj.ranges != sel.ranges) return normalizeSelection(obj.ranges, obj.ranges.length - 1); + else return sel; } // Update the selection. Last two args are only used by // updateDoc, since they have to be expressed in the line // numbers before the update. - function setSelection(doc, anchor, head, bias, checkAtomic) { - if (!checkAtomic && hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) { - var filtered = filterSelectionChange(doc, anchor, head); - head = filtered.head; - anchor = filtered.anchor; - } + function setSelection(doc, sel, bias) { + if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) + sel = filterSelectionChange(doc, sel); - var sel = doc.sel; - sel.goalColumn = null; - if (bias == null) bias = posLess(head, sel.head) ? -1 : 1; - // Skip over atomic spans. - if (checkAtomic || !posEq(anchor, sel.anchor)) - anchor = skipAtomic(doc, anchor, bias, checkAtomic != "push"); - if (checkAtomic || !posEq(head, sel.head)) - head = skipAtomic(doc, head, bias, checkAtomic != "push"); + if (bias == null) + bias = cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1; - if (posEq(sel.anchor, anchor) && posEq(sel.head, head)) return; + setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); + } + + function setSelectionInner(doc, sel) { + if (sel.equals(doc.sel)) return; - sel.anchor = anchor; sel.head = head; - var inv = posLess(head, anchor); - sel.from = inv ? head : anchor; - sel.to = inv ? anchor : head; + doc.sel = sel; if (doc.cm) doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = doc.cm.curOp.cursorActivity = true; - signalLater(doc, "cursorActivity", doc); } - function reCheckSelection(cm) { - setSelection(cm.doc, cm.doc.sel.from, cm.doc.sel.to, null, "push"); + function reCheckSelection(doc) { + setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); + } + + function skipAtomicInSelection(doc, sel, bias, mayClear) { + var out; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + var newAnchor = skipAtomic(doc, range.anchor, bias, mayClear); + var newHead = skipAtomic(doc, range.head, bias, mayClear); + if (out || newAnchor != range.anchor || newHead != range.head) { + if (!out) out = sel.ranges.slice(0, i); + out[i] = new Range(newAnchor, newHead); + } + } + return out ? normalizeSelection(out, sel.primIndex) : sel; } function skipAtomic(doc, pos, bias, mayClear) { @@ -2629,7 +2755,7 @@ window.CodeMirror = (function() { } if (!m.atomic) continue; var newPos = m.find()[dir < 0 ? "from" : "to"]; - if (posEq(newPos, curPos)) { + if (cmp(newPos, curPos) == 0) { newPos.ch += dir; if (newPos.ch < 0) { if (newPos.line > doc.first) newPos = clipPos(doc, Pos(newPos.line - 1)); @@ -2662,7 +2788,7 @@ window.CodeMirror = (function() { // SCROLLING function scrollCursorIntoView(cm) { - var coords = scrollPosIntoView(cm, cm.doc.sel.head, null, cm.options.cursorScrollMargin); + var coords = scrollPosIntoView(cm, cm.doc.sel.primary().head, null, cm.options.cursorScrollMargin); if (!cm.state.focused) return; var display = cm.display, box = getRect(display.sizer), doScroll = null; if (coords.top + box.top < 0) doScroll = true; @@ -2784,10 +2910,18 @@ window.CodeMirror = (function() { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} if (pos < indentation) indentString += spaceStr(indentation - pos); - if (indentString != curSpaceString) + if (indentString != curSpaceString) { replaceRange(cm.doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); - else if (doc.sel.head.line == n && doc.sel.head.ch < curSpaceString.length) - setSelection(doc, Pos(n, curSpaceString.length), Pos(n, curSpaceString.length), 1); + } else { + for (var i = 0; i < doc.sel.ranges.length; i++) { + var range = doc.sel.ranges[i]; + if (range.head.line == n && range.head.ch < curSpaceString.length) { + var pos = Pos(n, curSpaceString.length); + replaceOneSelection(doc, i, new Range(pos, pos), 1); + break; + } + } + } line.stateAfter = null; } @@ -2941,10 +3075,17 @@ window.CodeMirror = (function() { if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); }), indentSelection: operation(null, function(how) { - var sel = this.doc.sel; - if (posEq(sel.from, sel.to)) return indentLine(this, sel.from.line, how, true); - var e = sel.to.line - (sel.to.ch ? 0 : 1); - for (var i = sel.from.line; i <= e; ++i) indentLine(this, i, how); + for (var i = this.doc.sel.ranges.length - 1; i >= 0; i--) { + var range = this.doc.sel.ranges[i]; + if (range.empty()) { + indentLine(this, range.from().line, how, true); + } else { + var to = range.to(); + var e = to.line - (to.ch ? 0 : 1); + for (var j = range.from().line; j <= e; ++j) + indentLine(this, j, how); + } + } }), // Fetch the parser token for a given character. Useful for hacks @@ -3021,10 +3162,10 @@ window.CodeMirror = (function() { }, cursorCoords: function(start, mode) { - var pos, sel = this.doc.sel; - if (start == null) pos = sel.head; + var pos, range = this.doc.sel.primary(); + if (start == null) pos = range.head; else if (typeof start == "object") pos = clipPos(this.doc, start); - else pos = start ? sel.from : sel.to; + else pos = start ? range.from() : range.to(); return cursorCoords(this, pos, mode || "page"); }, @@ -3174,18 +3315,27 @@ window.CodeMirror = (function() { }, moveH: operation(null, function(dir, unit) { - var sel = this.doc.sel, pos; - if (this.shift || this.doc.extend || posEq(sel.from, sel.to)) - pos = findPosH(this.doc, sel.head, dir, unit, this.options.rtlMoveVisually); - else - pos = dir < 0 ? sel.from : sel.to; - extendSelection(this.doc, pos, pos, dir); + var sel = this.doc.sel, pos = []; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + if (this.shift || this.doc.extend || range.empty()) + pos[i] = findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually); + else + pos[i] = dir < 0 ? range.from() : range.to(); + } + extendSelections(this.doc, pos, null, dir); }), deleteH: operation(null, function(dir, unit) { var sel = this.doc.sel; - if (!posEq(sel.from, sel.to)) replaceRange(this.doc, "", sel.from, sel.to, "+delete"); - else replaceRange(this.doc, "", sel.from, findPosH(this.doc, sel.head, dir, unit, false), "+delete"); + if (sel.somethingSelected()) { + this.doc.replaceSelection("", null, "+delete"); + } else { + for (var i = sel.ranges.length - 1; i >= 0; i--) { + var range = sel.ranges[i]; + replaceRange(this.doc, "", range.head, findPosH(this.doc, range.head, dir, unit, false), "+delete"); + } + } this.curOp.userSelChange = true; }), @@ -3203,18 +3353,24 @@ window.CodeMirror = (function() { }, moveV: operation(null, function(dir, unit) { - var sel = this.doc.sel, target, goal; - if (sel.shift || sel.extend || posEq(sel.from, sel.to)) { - var pos = cursorCoords(this, sel.head, "div"); - if (sel.goalColumn != null) pos.left = sel.goalColumn; - target = findPosV(this, pos, dir, unit); - if (unit == "page") addToScrollPos(this, 0, charCoords(this, target, "div").top - pos.top); - goal = pos.left; + var sel = this.doc.sel, pos = [], cols = []; + if (sel.shift || sel.extend || !sel.somethingSelected()) { + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i], headPos = cursorCoords(this, range.head, "div"); + if (range.goalColumn != null) headPos.left = range.goalColumn; + pos[i] = findPosV(this, headPos, dir, unit); + cols[i] = headPos.left; + if (unit == "page" && i == sel.primIndex) + addToScrollPos(this, 0, charCoords(this, pos[i], "div").top - headPos.top); + } } else { - target = dir < 0 ? sel.from : sel.to; + for (var i = 0; i < sel.ranges.length; i++) + pos.push(dir < 0 ? sel.ranges[i].from() : sel.ranges[i].to()); } - extendSelection(this.doc, target, target, dir); - if (goal != null) sel.goalColumn = goal; + extendSelections(this.doc, pos, null, dir); + sel = this.doc.sel; + if (cols.length) for (var i = 0; i < sel.ranges.length; i++) + sel.ranges[i].goalColumn = cols[i]; }), toggleOverwrite: function(value) { @@ -3237,7 +3393,7 @@ window.CodeMirror = (function() { }, scrollIntoView: operation(null, function(range, margin) { - if (range == null) range = {from: this.doc.sel.head, to: null}; + if (range == null) range = {from: this.doc.sel.primary().head, to: null}; else if (typeof range == "number") range = {from: Pos(range, 0), to: null}; else if (range.from == null) range = {from: range, to: null}; if (!range.to) range.to = range.from; @@ -3533,7 +3689,7 @@ window.CodeMirror = (function() { var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));}, killLine: function(cm) { - var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); + var from = cm.getCursor(true), to = cm.getCursor(false), sel = cmp(from, to) != 0; if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete"); else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete"); @@ -3881,7 +4037,7 @@ window.CodeMirror = (function() { this.explicitlyCleared = true; if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; - if (cm) reCheckSelection(cm); + if (cm) reCheckSelection(cm.doc); } if (withOp) endOperation(cm); }; @@ -3940,9 +4096,9 @@ window.CodeMirror = (function() { if (options && options.shared) return markTextShared(doc, from, to, options, type); if (doc.cm && !doc.cm.curOp) return operation(doc.cm, markText)(doc, from, to, options, type); - var marker = new TextMarker(doc, type); + var marker = new TextMarker(doc, type), diff = cmp(from, to); if (options) copyObj(options, marker); - if (posLess(to, from) || posEq(from, to) && marker.clearWhenEmpty !== false) + if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) return marker; if (marker.replacedWith) { marker.collapsed = true; @@ -3957,8 +4113,7 @@ window.CodeMirror = (function() { } if (marker.addToHistory) - addToHistory(doc, {from: from, to: to, origin: "markText"}, - {head: doc.sel.head, anchor: doc.sel.anchor}, NaN); + addToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function(line) { @@ -3990,7 +4145,7 @@ window.CodeMirror = (function() { if (updateMaxLine) cm.curOp.updateMaxLine = true; if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.collapsed) regChange(cm, from.line, to.line + 1); - if (marker.atomic) reCheckSelection(cm); + if (marker.atomic) reCheckSelection(cm.doc); } return marker; } @@ -4085,7 +4240,7 @@ window.CodeMirror = (function() { var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) return null; - var startCh = change.from.ch, endCh = change.to.ch, isInsert = posEq(change.from, change.to); + var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh, isInsert); var last = markedSpansAfter(oldLast, endCh, isInsert); @@ -4186,11 +4341,11 @@ window.CodeMirror = (function() { var mk = markers[i], m = mk.find(); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; - if (posLess(p.to, m.from) || posLess(m.to, p.from)) continue; - var newParts = [j, 1]; - if (posLess(p.from, m.from) || !mk.inclusiveLeft && posEq(p.from, m.from)) + if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; + var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); + if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) newParts.push({from: p.from, to: m.from}); - if (posLess(m.to, p.to) || !mk.inclusiveRight && posEq(p.to, m.to)) + if (dto > 0 || !mk.inclusiveRight && !dto) newParts.push({from: m.to, to: p.to}); parts.splice.apply(parts, newParts); j += newParts.length - 1; @@ -4758,7 +4913,7 @@ window.CodeMirror = (function() { } signalLater(doc, "change", doc, change); - setSelection(doc, selAfter.anchor, selAfter.head, null, true); + setSelection(doc, selAfter, null, true); } function LeafChunk(lines) { @@ -4903,12 +5058,12 @@ window.CodeMirror = (function() { this.cleanGeneration = 1; this.frontier = firstLine; var start = Pos(firstLine, 0); - this.sel = {from: start, to: start, head: start, anchor: start, goalColumn: null}; + this.sel = simpleSelection(start); this.id = ++nextDocId; this.modeOption = mode; if (typeof text == "string") text = splitLines(text); - updateDoc(this, {from: start, to: start, text: text}, null, {head: start, anchor: start}); + updateDoc(this, {from: start, to: start, text: text}, null, simpleSelection(start)); }; Doc.prototype = createObj(BranchChunk.prototype, { @@ -4930,12 +5085,12 @@ window.CodeMirror = (function() { if (lineSep === false) return lines; return lines.join(lineSep || "\n"); }, - setValue: function(code) { + setValue: docOperation(function(code) { var top = Pos(this.first, 0), last = this.first + this.size - 1; makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), - text: splitLines(code), origin: "setValue"}, - {head: top, anchor: top}, true); - }, + text: splitLines(code), origin: "setValue"}, true); + setSelectionAddToHistory(this, simpleSelection(top)); + }), replaceRange: function(code, from, to, origin) { from = clipPos(this, from); to = to ? clipPos(this, to) : from; @@ -4972,30 +5127,74 @@ window.CodeMirror = (function() { clipPos: function(pos) {return clipPos(this, pos);}, getCursor: function(start) { - var sel = this.sel, pos; - if (start == null || start == "head") pos = sel.head; - else if (start == "anchor") pos = sel.anchor; - else if (start == "end" || start === false) pos = sel.to; - else pos = sel.from; + var range = this.sel.primary(), pos; + if (start == null || start == "head") pos = range.head; + else if (start == "anchor") pos = range.anchor; + else if (start == "end" || start == "to" || start === false) pos = range.to(); + else pos = range.from(); return copyPos(pos); }, - somethingSelected: function() {return !posEq(this.sel.head, this.sel.anchor);}, + listSelections: function() { return this.sel.ranges; }, + somethingSelected: function() {return this.sel.somethingSelected();}, setCursor: docOperation(function(line, ch, extend) { var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line); if (extend) extendSelection(this, pos); - else setSelection(this, pos, pos); + else setSimpleSelection(this, pos); }), setSelection: docOperation(function(anchor, head, bias) { - setSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias); + setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias); }), extendSelection: docOperation(function(from, to, bias) { extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias); }), + setSelections: docOperation(function(ranges, primary, bias) { + for (var i = 0, out = []; i < ranges.length; i++) + out[i] = new Range(clipPos(this, ranges[i].anchor), + clipPos(this, ranges[i].head)); + if (primary == null) primary = ranges.length - 1; + setSelection(this, normalizeSelection(out, primary), bias); + }), + addSelection: docOperation(function(anchor, head) { + var ranges = this.sel.ranges.slice(0); + ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); + setSelection(this, normalizeSelection(ranges, ranges.length - 1)); + }), - getSelection: function(lineSep) {return this.getRange(this.sel.from, this.sel.to, lineSep);}, - replaceSelection: function(code, collapse, origin) { - makeChange(this, {from: this.sel.from, to: this.sel.to, text: splitLines(code), origin: origin}, collapse || "around"); + getSelection: function(lineSep) { + var ranges = this.sel.ranges, lines; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + lines = lines ? lines.concat(sel) : sel; + } + if (lineSep === false) return lines; + else return lines.join(lineSep || "\n"); + }, + getSelections: function(lineSep) { + var parts = [], ranges = this.sel.ranges; + for (var i = 0; i < ranges.length; i++) { + var sel = getBetween(this, ranges[i].from(), ranges[i].to()); + if (lineSep !== false) sel = sel.join(lineSep || "\n"); + parts[i] = sel; + } + return parts; + }, + replaceSelection: docOperation(function(code, collapse, origin) { + var dup = []; + for (var i = 0; i < this.sel.ranges.length; i++) + dup[i] = code; + this.replaceSelections(dup, collapse, origin); + }), + replaceSelections: function(code, collapse, origin) { + var changes = [], sel = this.sel; + for (var i = 0; i < sel.ranges.length; i++) { + var range = sel.ranges[i]; + changes[i] = {from: range.from(), to: range.to(), text: splitLines(code[i]), origin: origin}; + } + var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); + for (var i = changes.length - 1; i >= 0; i--) + makeChange(this, changes[i]); + if (newSel) setSelectionAddToHistory(this, newSel); }, undo: docOperation(function() {makeChangeFromHistory(this, "undo");}), redo: docOperation(function() {makeChangeFromHistory(this, "redo");}), @@ -5026,8 +5225,8 @@ window.CodeMirror = (function() { }, setHistory: function(histData) { var hist = this.history = makeHistory(this.history.maxGeneration); - hist.done = histData.done.slice(0); - hist.undone = histData.undone.slice(0); + hist.done = copyHistoryArray(histData.done.slice(0), null, true); + hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, markText: function(from, to, options) { @@ -5084,8 +5283,7 @@ window.CodeMirror = (function() { copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; - doc.sel = {from: this.sel.from, to: this.sel.to, head: this.sel.head, anchor: this.sel.anchor, - goalColumn: this.sel.goalColumn}; + doc.sel = this.sel; doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; @@ -5302,7 +5500,7 @@ window.CodeMirror = (function() { change.origin.charAt(0) == "*"))) { // Merge this change into the last event var last = lst(cur.changes); - if (posEq(change.from, change.to) && posEq(change.from, last.to)) { + if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed last.to = changeEnd(change); @@ -5310,13 +5508,12 @@ window.CodeMirror = (function() { // Add new sub-event cur.changes.push(historyChangeFromChange(doc, change)); } - cur.anchorAfter = selAfter.anchor; cur.headAfter = selAfter.head; + cur.selAfter = selAfter; } else { // Can not be merged, start a new event. cur = {changes: [historyChangeFromChange(doc, change)], generation: hist.generation, - anchorBefore: doc.sel.anchor, headBefore: doc.sel.head, - anchorAfter: selAfter.anchor, headAfter: selAfter.head}; + selBefore: doc.sel, selAfter: selAfter}; hist.done.push(cur); while (hist.done.length > hist.undoDepth) hist.done.shift(); @@ -5344,13 +5541,21 @@ window.CodeMirror = (function() { return nw; } + function copyHistorySel(sel, instantiate) { + if (!instantiate) return sel; + for (var out = [], i = 0; i < sel.ranges.length; i++) + out[i] = new Range(sel.ranges[i].anchor, sel.ranges[i].head); + return new Selection(out, sel.primIndex); + } + // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two - function copyHistoryArray(events, newGroup) { + function copyHistoryArray(events, newGroup, instantiateSel) { for (var i = 0, copy = []; i < events.length; ++i) { var event = events[i], changes = event.changes, newChanges = []; - copy.push({changes: newChanges, anchorBefore: event.anchorBefore, headBefore: event.headBefore, - anchorAfter: event.anchorAfter, headAfter: event.headAfter}); + copy.push({changes: newChanges, + selBefore: copyHistorySel(event.selBefore, instantiateSel), + selAfter: copyHistorySel(event.selAfter, instantiateSel)}); for (var j = 0; j < changes.length; ++j) { var change = changes[j], m; newChanges.push({from: change.from, to: change.to, text: change.text}); @@ -5367,7 +5572,13 @@ window.CodeMirror = (function() { // Rebasing/resetting history to deal with externally-sourced changes - function rebaseHistSel(pos, from, to, diff) { + function rebaseHistSel(array, from, to, diff) { + for (var i = 0; i < array.length; i++) { + rebaseHistSelSingle(array[i].anchor, from, to, diff); + rebaseHistSelSingle(array[i].head, from, to, diff); + } + } + function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { pos.line += diff; } else if (from < pos.line) { @@ -5388,26 +5599,25 @@ window.CodeMirror = (function() { var sub = array[i], ok = true; for (var j = 0; j < sub.changes.length; ++j) { var cur = sub.changes[j]; - if (!sub.copied) { cur.from = copyPos(cur.from); cur.to = copyPos(cur.to); } if (to < cur.from.line) { - cur.from.line += diff; - cur.to.line += diff; + cur.from = Pos(cur.from.line + diff, cur.from.ch); + cur.to = Pos(cur.to.line + diff, cur.to.ch); } else if (from <= cur.to.line) { ok = false; break; } } if (!sub.copied) { - sub.anchorBefore = copyPos(sub.anchorBefore); sub.headBefore = copyPos(sub.headBefore); - sub.anchorAfter = copyPos(sub.anchorAfter); sub.readAfter = copyPos(sub.headAfter); + sub.selBefore = sub.selBefore.deepCopy(); + sub.selAfter = sub.selAfter.deepCopy(); sub.copied = true; } if (!ok) { array.splice(0, i + 1); i = 0; } else { - rebaseHistSel(sub.anchorBefore); rebaseHistSel(sub.headBefore); - rebaseHistSel(sub.anchorAfter); rebaseHistSel(sub.headAfter); + rebaseHistSel(sub.selBefore, from, to, diff); + rebaseHistSel(sub.selAfter, from, to, diff); } } } @@ -5581,6 +5791,12 @@ window.CodeMirror = (function() { if (collection[i] == elt) return i; return -1; } + function map(array, f) { + var out = []; + for (var i = 0; i < array.length; i++) + out[i] = f(array[i], i); + return out; + } function createObj(base, props) { function Obj() {} diff --git a/test/test.js b/test/test.js index 6661645cd4..7cfb61980c 100644 --- a/test/test.js +++ b/test/test.js @@ -87,7 +87,7 @@ testCM("selection", function(cm) { is(!cm.somethingSelected()); eq(cm.getSelection(), ""); eqPos(cm.getCursor(true), Pos(1, 0)); - cm.replaceSelection("abc"); + cm.replaceSelection("abc", "around"); eq(cm.getSelection(), "abc"); eq(cm.getValue(), "111111\nabc222222\n333333"); cm.replaceSelection("def", "end"); @@ -1440,14 +1440,14 @@ testCM("readOnlyMarker", function(cm) { eqPos(cm.getCursor(), Pos(0, 2)); eq(cm.getLine(0), "abcde"); cm.execCommand("selectAll"); - cm.replaceSelection("oops"); + cm.replaceSelection("oops", "around"); eq(cm.getValue(), "oopsbcd"); cm.undo(); eqPos(m.find().from, Pos(0, 1)); eqPos(m.find().to, Pos(0, 4)); m.clear(); cm.setCursor(Pos(0, 2)); - cm.replaceSelection("hi"); + cm.replaceSelection("hi", "around"); eq(cm.getLine(0), "abhicde"); eqPos(cm.getCursor(), Pos(0, 4)); m = mark(0, 2, 2, 2, true); @@ -1459,7 +1459,7 @@ testCM("readOnlyMarker", function(cm) { cm.execCommand("goCharLeft"); eqPos(cm.getCursor(), Pos(0, 2)); cm.setSelection(Pos(0, 1), Pos(0, 3)); - cm.replaceSelection("xx"); + cm.replaceSelection("xx", "around"); eqPos(cm.getCursor(), Pos(0, 3)); eq(cm.getLine(0), "axxhicde"); }, {value: "abcde\nfghij\nklmno\n"}); @@ -1587,9 +1587,9 @@ testCM("beforeSelectionChange", function(cm) { if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1); return pos; } - cm.on("beforeSelectionChange", function(cm, sel) { - sel.head = notAtEnd(cm, sel.head); - sel.anchor = notAtEnd(cm, sel.anchor); + cm.on("beforeSelectionChange", function(cm, obj) { + obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor), + head: notAtEnd(cm, obj.ranges[0].head)}]); }); addDoc(cm, 10, 10); @@ -1670,3 +1670,72 @@ testCM("helpers", function(cm) { cm.setOption("mode", "javascript"); eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), ""); }); + +// Multiple selections + +function hasSelections(cm) { + var sels = cm.listSelections(); + var given = (arguments.length - 1) / 4; + if (sels.length != given) + throw new Failure("expected " + given + " selections, found " + sels.length); + for (var i = 0, p = 1; i < given; i++, p += 4) { + var anchor = Pos(arguments[p], arguments[p + 1]); + var head = Pos(arguments[p + 2], arguments[p + 3]); + eqPos(sels[i].anchor, anchor, "anchor of selection " + i); + eqPos(sels[i].head, head, "head of selection " + i); + } +} + +testCM("replaceMultiSelection", function(cm) { + var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)}, + {anchor: Pos(0, 2), head: Pos(0, 3)}, + {anchor: Pos(0, 4), head: Pos(0, 5)}, + {anchor: Pos(2, 1), head: Pos(2, 4)}, + {anchor: Pos(2, 5), head: Pos(2, 6)}]; + var val = "123456\n123456\n123456"; + cm.setValue(val); + cm.setSelections(selections); + cm.replaceSelection("ab", "around"); + eq(cm.getValue(), "ab2ab4ab6\n123456\n1ab5ab"); + hasSelections(cm, 0, 0, 0, 2, + 0, 3, 0, 5, + 0, 6, 0, 8, + 2, 1, 2, 3, + 2, 4, 2, 6); + cm.setValue(val); + cm.setSelections(selections); + cm.replaceSelection("", "around"); + eq(cm.getValue(), "246\n123456\n15"); + hasSelections(cm, 0, 0, 0, 0, + 0, 1, 0, 1, + 0, 2, 0, 2, + 2, 1, 2, 1, + 2, 2, 2, 2); + cm.setValue(val); + cm.setSelections(selections); + cm.replaceSelection("X\nY\nZ", "around"); + hasSelections(cm, 0, 0, 2, 1, + 2, 2, 4, 1, + 4, 2, 6, 1, + 8, 1, 10, 1, + 10, 2, 12, 1); + cm.replaceSelection("a", "around"); + hasSelections(cm, 0, 0, 0, 1, + 0, 2, 0, 3, + 0, 4, 0, 5, + 2, 1, 2, 2, + 2, 3, 2, 4); + cm.replaceSelection("xy", "start"); + hasSelections(cm, 0, 0, 0, 0, + 0, 3, 0, 3, + 0, 6, 0, 6, + 2, 1, 2, 1, + 2, 4, 2, 4); + cm.replaceSelection("z\nf"); + hasSelections(cm, 1, 1, 1, 1, + 2, 1, 2, 1, + 3, 1, 3, 1, + 6, 1, 6, 1, + 7, 1, 7, 1); + eq(cm.getValue(), "z\nfxy2z\nfxy4z\nfxy6\n123456\n1z\nfxy5z\nfxy"); +}); From c6354933de236bcddb4be5fe1acb0eda1f471edd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 13:01:45 +0100 Subject: [PATCH 006/195] Fix selection handling in indentSelection and killLine Issue #778 --- lib/codemirror.js | 37 ++++++++++++------ test/index.html | 1 + test/multi_test.js | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test/test.js | 69 ---------------------------------- 4 files changed, 134 insertions(+), 80 deletions(-) create mode 100644 test/multi_test.js diff --git a/lib/codemirror.js b/lib/codemirror.js index d07f0007ec..46e43bc978 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3075,15 +3075,18 @@ window.CodeMirror = (function() { if (isLine(this.doc, n)) indentLine(this, n, dir, aggressive); }), indentSelection: operation(null, function(how) { - for (var i = this.doc.sel.ranges.length - 1; i >= 0; i--) { - var range = this.doc.sel.ranges[i]; - if (range.empty()) { - indentLine(this, range.from().line, how, true); - } else { + var ranges = this.doc.sel.ranges, end = -1; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty()) { + var start = Math.max(end, range.from().line); var to = range.to(); - var e = to.line - (to.ch ? 0 : 1); - for (var j = range.from().line; j <= e; ++j) + end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; + for (var j = start; j < end; ++j) indentLine(this, j, how); + } else if (range.head.line > end) { + indentLine(this, range.head.line, how, true); + end = range.head.line; } } }), @@ -3689,10 +3692,22 @@ window.CodeMirror = (function() { var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));}, killLine: function(cm) { - var from = cm.getCursor(true), to = cm.getCursor(false), sel = cmp(from, to) != 0; - if (!sel && cm.getLine(from.line).length == from.ch) - cm.replaceRange("", from, Pos(from.line + 1, 0), "+delete"); - else cm.replaceRange("", from, sel ? to : Pos(from.line), "+delete"); + var ranges = cm.listSelections(), kill = []; + for (var i = 0; i < ranges.length; i++) { + var r = ranges[i], from = r.from(), to = r.to(); + if (r.empty()) { + var len = cm.getLine(to.line).length; + if (to.ch == len) to = Pos(to.line + 1, 0); + else to = Pos(to.line, len); + if (i < ranges.length - 1 && cmp(to, ranges[i + 1].from()) > 0) + to = ranges[i + 1].from(); + } + kill.push(from, to); + } + cm.operation(function() { + for (var i = kill.length - 2; i >= 0; i -= 2) + cm.replaceRange("", kill[i], kill[i + 1], "+delete"); + }); }, deleteLine: function(cm) { var l = cm.getCursor().line; diff --git a/test/index.html b/test/index.html index 8177038e30..bdd88b0cb9 100644 --- a/test/index.html +++ b/test/index.html @@ -71,6 +71,7 @@ + diff --git a/test/multi_test.js b/test/multi_test.js new file mode 100644 index 0000000000..186f0f47f7 --- /dev/null +++ b/test/multi_test.js @@ -0,0 +1,107 @@ +(function() { + namespace = "multi_"; + + function hasSelections(cm) { + var sels = cm.listSelections(); + var given = (arguments.length - 1) / 4; + if (sels.length != given) + throw new Failure("expected " + given + " selections, found " + sels.length); + for (var i = 0, p = 1; i < given; i++, p += 4) { + var anchor = Pos(arguments[p], arguments[p + 1]); + var head = Pos(arguments[p + 2], arguments[p + 3]); + eqPos(sels[i].anchor, anchor, "anchor of selection " + i); + eqPos(sels[i].head, head, "head of selection " + i); + } + } + + testCM("replaceSelection", function(cm) { + var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)}, + {anchor: Pos(0, 2), head: Pos(0, 3)}, + {anchor: Pos(0, 4), head: Pos(0, 5)}, + {anchor: Pos(2, 1), head: Pos(2, 4)}, + {anchor: Pos(2, 5), head: Pos(2, 6)}]; + var val = "123456\n123456\n123456"; + cm.setValue(val); + cm.setSelections(selections); + cm.replaceSelection("ab", "around"); + eq(cm.getValue(), "ab2ab4ab6\n123456\n1ab5ab"); + hasSelections(cm, 0, 0, 0, 2, + 0, 3, 0, 5, + 0, 6, 0, 8, + 2, 1, 2, 3, + 2, 4, 2, 6); + cm.setValue(val); + cm.setSelections(selections); + cm.replaceSelection("", "around"); + eq(cm.getValue(), "246\n123456\n15"); + hasSelections(cm, 0, 0, 0, 0, + 0, 1, 0, 1, + 0, 2, 0, 2, + 2, 1, 2, 1, + 2, 2, 2, 2); + cm.setValue(val); + cm.setSelections(selections); + cm.replaceSelection("X\nY\nZ", "around"); + hasSelections(cm, 0, 0, 2, 1, + 2, 2, 4, 1, + 4, 2, 6, 1, + 8, 1, 10, 1, + 10, 2, 12, 1); + cm.replaceSelection("a", "around"); + hasSelections(cm, 0, 0, 0, 1, + 0, 2, 0, 3, + 0, 4, 0, 5, + 2, 1, 2, 2, + 2, 3, 2, 4); + cm.replaceSelection("xy", "start"); + hasSelections(cm, 0, 0, 0, 0, + 0, 3, 0, 3, + 0, 6, 0, 6, + 2, 1, 2, 1, + 2, 4, 2, 4); + cm.replaceSelection("z\nf"); + hasSelections(cm, 1, 1, 1, 1, + 2, 1, 2, 1, + 3, 1, 3, 1, + 6, 1, 6, 1, + 7, 1, 7, 1); + eq(cm.getValue(), "z\nfxy2z\nfxy4z\nfxy6\n123456\n1z\nfxy5z\nfxy"); + }); + + function select(cm) { + var sels = []; + for (var i = 1; i < arguments.length; i++) { + var arg = arguments[i]; + if (arg.head) sels.push(arg); + else sels.push({head: arg, anchor: arg}); + } + cm.setSelections(sels); + } + + testCM("indentSelection", function(cm) { + select(cm, Pos(0, 1), Pos(1, 1)); + cm.indentSelection(4); + eq(cm.getValue(), " foo\n bar\nbaz"); + + select(cm, Pos(0, 2), Pos(0, 3), Pos(0, 4)); + cm.indentSelection(-2); + eq(cm.getValue(), " foo\n bar\nbaz"); + + select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)}, + {anchor: Pos(1, 3), head: Pos(2, 0)}); + cm.indentSelection(-2); + eq(cm.getValue(), "foo\n bar\nbaz"); + }, {value: "foo\nbar\nbaz"}); + + testCM("killLine", function(cm) { + select(cm, Pos(0, 1), Pos(0, 2), Pos(1, 1)); + cm.execCommand("killLine"); + eq(cm.getValue(), "f\nb\nbaz"); + cm.execCommand("killLine"); + eq(cm.getValue(), "fbbaz"); + cm.setValue("foo\nbar\nbaz"); + select(cm, Pos(0, 1), {anchor: Pos(0, 2), head: Pos(2, 1)}); + cm.execCommand("killLine"); + eq(cm.getValue(), "faz"); + }, {value: "foo\nbar\nbaz"}); +})(); diff --git a/test/test.js b/test/test.js index 7cfb61980c..6a32ca553f 100644 --- a/test/test.js +++ b/test/test.js @@ -1670,72 +1670,3 @@ testCM("helpers", function(cm) { cm.setOption("mode", "javascript"); eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), ""); }); - -// Multiple selections - -function hasSelections(cm) { - var sels = cm.listSelections(); - var given = (arguments.length - 1) / 4; - if (sels.length != given) - throw new Failure("expected " + given + " selections, found " + sels.length); - for (var i = 0, p = 1; i < given; i++, p += 4) { - var anchor = Pos(arguments[p], arguments[p + 1]); - var head = Pos(arguments[p + 2], arguments[p + 3]); - eqPos(sels[i].anchor, anchor, "anchor of selection " + i); - eqPos(sels[i].head, head, "head of selection " + i); - } -} - -testCM("replaceMultiSelection", function(cm) { - var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)}, - {anchor: Pos(0, 2), head: Pos(0, 3)}, - {anchor: Pos(0, 4), head: Pos(0, 5)}, - {anchor: Pos(2, 1), head: Pos(2, 4)}, - {anchor: Pos(2, 5), head: Pos(2, 6)}]; - var val = "123456\n123456\n123456"; - cm.setValue(val); - cm.setSelections(selections); - cm.replaceSelection("ab", "around"); - eq(cm.getValue(), "ab2ab4ab6\n123456\n1ab5ab"); - hasSelections(cm, 0, 0, 0, 2, - 0, 3, 0, 5, - 0, 6, 0, 8, - 2, 1, 2, 3, - 2, 4, 2, 6); - cm.setValue(val); - cm.setSelections(selections); - cm.replaceSelection("", "around"); - eq(cm.getValue(), "246\n123456\n15"); - hasSelections(cm, 0, 0, 0, 0, - 0, 1, 0, 1, - 0, 2, 0, 2, - 2, 1, 2, 1, - 2, 2, 2, 2); - cm.setValue(val); - cm.setSelections(selections); - cm.replaceSelection("X\nY\nZ", "around"); - hasSelections(cm, 0, 0, 2, 1, - 2, 2, 4, 1, - 4, 2, 6, 1, - 8, 1, 10, 1, - 10, 2, 12, 1); - cm.replaceSelection("a", "around"); - hasSelections(cm, 0, 0, 0, 1, - 0, 2, 0, 3, - 0, 4, 0, 5, - 2, 1, 2, 2, - 2, 3, 2, 4); - cm.replaceSelection("xy", "start"); - hasSelections(cm, 0, 0, 0, 0, - 0, 3, 0, 3, - 0, 6, 0, 6, - 2, 1, 2, 1, - 2, 4, 2, 4); - cm.replaceSelection("z\nf"); - hasSelections(cm, 1, 1, 1, 1, - 2, 1, 2, 1, - 3, 1, 3, 1, - 6, 1, 6, 1, - 7, 1, 7, 1); - eq(cm.getValue(), "z\nfxy2z\nfxy4z\nfxy6\n123456\n1z\nfxy5z\nfxy"); -}); From 25197f4994b8cc0bf66640416cf105cd8cf12063 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 13:56:58 +0100 Subject: [PATCH 007/195] Refine and add tests for selection-related deletion commands Issue #778 --- lib/codemirror.js | 72 +++++++++++++++++++++++++++++++++--------------------- test/multi_test.js | 25 +++++++++++++++++++ 2 files changed, 69 insertions(+), 28 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 46e43bc978..83f5aad0e6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2935,6 +2935,25 @@ window.CodeMirror = (function() { return line; } + function deleteNearSelection(cm, compute) { + var ranges = cm.doc.sel.ranges, kill = []; + for (var i = 0; i < ranges.length; i++) { + var toKill = compute(ranges[i]); + while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { + var replaced = kill.pop(); + if (cmp(replaced.from, toKill.from) < 0) { + toKill.from = replaced.from; + break; + } + } + kill.push(toKill); + } + cm.operation(function() { + for (var i = kill.length - 1; i >= 0; i--) + replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); + }); + } + function findPosH(doc, pos, dir, unit, visually) { var line = pos.line, ch = pos.ch, origDir = dir; var lineObj = getLine(doc, line); @@ -3321,7 +3340,7 @@ window.CodeMirror = (function() { var sel = this.doc.sel, pos = []; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; - if (this.shift || this.doc.extend || range.empty()) + if (this.display.shift || this.doc.extend || range.empty()) pos[i] = findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually); else pos[i] = dir < 0 ? range.from() : range.to(); @@ -3330,15 +3349,14 @@ window.CodeMirror = (function() { }), deleteH: operation(null, function(dir, unit) { - var sel = this.doc.sel; - if (sel.somethingSelected()) { - this.doc.replaceSelection("", null, "+delete"); - } else { - for (var i = sel.ranges.length - 1; i >= 0; i--) { - var range = sel.ranges[i]; - replaceRange(this.doc, "", range.head, findPosH(this.doc, range.head, dir, unit, false), "+delete"); - } - } + var sel = this.doc.sel, doc = this.doc; + if (sel.somethingSelected()) + doc.replaceSelection("", null, "+delete"); + else + deleteNearSelection(this, function(range) { + var other = findPosH(doc, range.head, dir, unit, false); + return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}; + }); this.curOp.userSelChange = true; }), @@ -3692,30 +3710,28 @@ window.CodeMirror = (function() { var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));}, killLine: function(cm) { - var ranges = cm.listSelections(), kill = []; - for (var i = 0; i < ranges.length; i++) { - var r = ranges[i], from = r.from(), to = r.to(); - if (r.empty()) { - var len = cm.getLine(to.line).length; - if (to.ch == len) to = Pos(to.line + 1, 0); - else to = Pos(to.line, len); - if (i < ranges.length - 1 && cmp(to, ranges[i + 1].from()) > 0) - to = ranges[i + 1].from(); + deleteNearSelection(cm, function(range) { + if (range.empty()) { + var len = getLine(cm.doc, range.head.line).text.length; + if (range.head.ch == len && range.head.line < cm.lastLine()) + return {from: range.head, to: Pos(range.head.line + 1, 0)}; + else + return {from: range.head, to: Pos(range.head.line, len)}; + } else { + return {from: range.from(), to: range.to()}; } - kill.push(from, to); - } - cm.operation(function() { - for (var i = kill.length - 2; i >= 0; i -= 2) - cm.replaceRange("", kill[i], kill[i + 1], "+delete"); }); }, deleteLine: function(cm) { - var l = cm.getCursor().line; - cm.replaceRange("", Pos(l, 0), Pos(l), "+delete"); + deleteNearSelection(cm, function(range) { + return {from: Pos(range.from().line, 0), + to: clipPos(cm.doc, Pos(range.to().line + 1, 0))}; + }); }, delLineLeft: function(cm) { - var cur = cm.getCursor(); - cm.replaceRange("", Pos(cur.line, 0), cur, "+delete"); + deleteNearSelection(cm, function(range) { + return {from: Pos(range.from().line, 0), to: range.from()}; + }); }, undo: function(cm) {cm.undo();}, redo: function(cm) {cm.redo();}, diff --git a/test/multi_test.js b/test/multi_test.js index 186f0f47f7..64e7a52122 100644 --- a/test/multi_test.js +++ b/test/multi_test.js @@ -104,4 +104,29 @@ cm.execCommand("killLine"); eq(cm.getValue(), "faz"); }, {value: "foo\nbar\nbaz"}); + + testCM("deleteLine", function(cm) { + select(cm, Pos(0, 0), + {head: Pos(0, 1), anchor: Pos(2, 0)}, + Pos(4, 0)); + cm.execCommand("deleteLine"); + eq(cm.getValue(), "4\n6\n7"); + select(cm, Pos(2, 1)); + cm.execCommand("deleteLine"); + eq(cm.getValue(), "4\n6\n"); + }, {value: "1\n2\n3\n4\n5\n6\n7"}); + + testCM("deleteH", function(cm) { + select(cm, Pos(0, 4), {anchor: Pos(1, 4), head: Pos(1, 5)}); + cm.execCommand("delWordAfter"); + eq(cm.getValue(), "foo bar baz\nabc ef ghi\n"); + cm.execCommand("delWordAfter"); + eq(cm.getValue(), "foo baz\nabc ghi\n"); + cm.execCommand("delCharBefore"); + cm.execCommand("delCharBefore"); + eq(cm.getValue(), "fo baz\nab ghi\n"); + select(cm, Pos(0, 3), Pos(0, 4), Pos(0, 5)); + cm.execCommand("delWordAfter"); + eq(cm.getValue(), "fo \nab ghi\n"); + }, {value: "foo bar baz\nabc def ghi\n"}); })(); From 246e27cfd92b4e11911614c4b2c92b95d5d8c540 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 14:20:34 +0100 Subject: [PATCH 008/195] Make goLineStart[Smart] and goLineEnd work in multi-selection env Issue #778 --- lib/codemirror.js | 49 +++++++++++++++++++++++++++++++++---------------- test/multi_test.js | 14 ++++++++++++++ 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 83f5aad0e6..31dc010b58 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2625,6 +2625,10 @@ window.CodeMirror = (function() { else return pos; } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size;} + function clipPosArray(doc, array) { + for (var out = [], i = 0; i < array.length; i++) out[i] = clipPos(doc, array[i]); + return out; + } function extendRange(doc, range, pos, other) { if (doc.cm && doc.cm.display.shift || doc.extend) { @@ -3738,28 +3742,35 @@ window.CodeMirror = (function() { goDocStart: function(cm) {cm.extendSelection(Pos(cm.firstLine(), 0));}, goDocEnd: function(cm) {cm.extendSelection(Pos(cm.lastLine()));}, goLineStart: function(cm) { - cm.extendSelection(lineStart(cm, cm.getCursor().line)); + cm.extendSelectionsBy(function(range) { return lineStart(cm, range.head.line); }); }, goLineStartSmart: function(cm) { - var cur = cm.getCursor(), start = lineStart(cm, cur.line); - var line = cm.getLineHandle(start.line); - var order = getOrder(line); - if (!order || order[0].level == 0) { - var firstNonWS = Math.max(0, line.text.search(/\S/)); - var inWS = cur.line == start.line && cur.ch <= firstNonWS && cur.ch; - cm.extendSelection(Pos(start.line, inWS ? 0 : firstNonWS)); - } else cm.extendSelection(start); + cm.extendSelectionsBy(function(range) { + var start = lineStart(cm, range.head.line); + var line = cm.getLineHandle(start.line); + var order = getOrder(line); + if (!order || order[0].level == 0) { + var firstNonWS = Math.max(0, line.text.search(/\S/)); + var inWS = range.head.line == start.line && range.head.ch <= firstNonWS && range.head.ch; + return Pos(start.line, inWS ? 0 : firstNonWS); + } + return start; + }); }, goLineEnd: function(cm) { - cm.extendSelection(lineEnd(cm, cm.getCursor().line)); + cm.extendSelectionsBy(function(range) { return lineEnd(cm, range.head.line); }); }, goLineRight: function(cm) { - var top = cm.charCoords(cm.getCursor(), "div").top + 5; - cm.extendSelection(cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")); + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); + }); }, goLineLeft: function(cm) { - var top = cm.charCoords(cm.getCursor(), "div").top + 5; - cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div")); + cm.extendSelectionsBy(function(range) { + var top = cm.charCoords(range.head, "div").top + 5; + cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div")); + }); }, goLineUp: function(cm) {cm.moveV(-1, "line");}, goLineDown: function(cm) {cm.moveV(1, "line");}, @@ -5179,6 +5190,12 @@ window.CodeMirror = (function() { extendSelection: docOperation(function(from, to, bias) { extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias); }), + extendSelections: docOperation(function(from, to, bias) { + extendSelections(this, clipPosArray(this, from), to && clipPosArray(this, to), bias); + }), + extendSelectionsBy: docOperation(function(f, bias) { + extendSelections(this, map(this.sel.ranges, f), null, bias); + }), setSelections: docOperation(function(ranges, primary, bias) { for (var i = 0, out = []; i < ranges.length; i++) out[i] = new Range(clipPos(this, ranges[i].anchor), @@ -5400,6 +5417,7 @@ window.CodeMirror = (function() { n -= chunk.first; while (!chunk.lines) { for (var i = 0;; ++i) { + if (!chunk.children[i]) debugger; var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break; } n -= sz; @@ -5824,8 +5842,7 @@ window.CodeMirror = (function() { } function map(array, f) { var out = []; - for (var i = 0; i < array.length; i++) - out[i] = f(array[i], i); + for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); return out; } diff --git a/test/multi_test.js b/test/multi_test.js index 64e7a52122..7d20e7cecb 100644 --- a/test/multi_test.js +++ b/test/multi_test.js @@ -129,4 +129,18 @@ cm.execCommand("delWordAfter"); eq(cm.getValue(), "fo \nab ghi\n"); }, {value: "foo bar baz\nabc def ghi\n"}); + + testCM("goLineStart", function(cm) { + select(cm, Pos(0, 2), Pos(0, 3), Pos(1, 1)); + cm.execCommand("goLineStart"); + hasSelections(cm, 0, 0, 0, 0, + 1, 0, 1, 0); + select(cm, Pos(1, 1), Pos(0, 1)); + cm.setExtending(true); + cm.execCommand("goLineStart"); + hasSelections(cm, 0, 1, 0, 0, + 1, 1, 1, 0); + }, {value: "foo\nbar\nbaz"}); + + })(); From c1cb20531d3fa643fbc013dc0e16a70a6290ade6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 16:38:18 +0100 Subject: [PATCH 009/195] More selection command fixes and tests Issue #778 --- lib/codemirror.js | 98 +++++++++++++++++++++-------------------- test/multi_test.js | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 174 insertions(+), 51 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 31dc010b58..0d70bd8cc2 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -799,6 +799,7 @@ window.CodeMirror = (function() { somethingSelected: function() { for (var i = 0; i < this.ranges.length; i++) if (!this.ranges[i].empty()) return true; + return false; }, contains: function(pos, end) { if (!end) end = pos; @@ -1626,7 +1627,7 @@ window.CodeMirror = (function() { cm.display.prevInput = cm.display.input.value = ""; if (ie && !ie_lt9) cm.display.inputHasSelection = null; } - cm.display.inaccurateSelection = minimal || doc.sel.ranges.length > 1; + cm.display.inaccurateSelection = minimal; } function focusInput(cm) { @@ -2651,16 +2652,15 @@ window.CodeMirror = (function() { // If shift is held, this will move the selection anchor. Otherwise, // it'll set the whole selection. function extendSelection(doc, pos, other, bias) { - var prim = doc.sel.primary(); - replaceOneSelection(doc, doc.sel.primIndex, extendRange(doc, prim, pos, other), bias); + setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), pos, other)], 0), bias); if (doc.cm) doc.cm.curOp.userSelChange = true; } // Extend all selections (pos is an array of selections with length // equal the number of selections) - function extendSelections(doc, pos, other, bias) { + function extendSelections(doc, pos, bias) { for (var out = [], i = 0; i < doc.sel.ranges.length; i++) - out[i] = extendRange(doc, doc.sel.ranges[i], pos[i], other && other[i]); + out[i] = extendRange(doc, doc.sel.ranges[i], pos[i], null); var newSel = normalizeSelection(out, doc.sel.primIndex); setSelection(doc, newSel, bias); if (doc.cm) doc.cm.curOp.userSelChange = true; @@ -3341,15 +3341,13 @@ window.CodeMirror = (function() { }, moveH: operation(null, function(dir, unit) { - var sel = this.doc.sel, pos = []; - for (var i = 0; i < sel.ranges.length; i++) { - var range = sel.ranges[i]; - if (this.display.shift || this.doc.extend || range.empty()) - pos[i] = findPosH(this.doc, range.head, dir, unit, this.options.rtlMoveVisually); + var cm = this; + cm.extendSelectionsBy(function(range) { + if (cm.display.shift || cm.doc.extend || range.empty()) + return findPosH(cm.doc, range.head, dir, unit, cm.options.rtlMoveVisually); else - pos[i] = dir < 0 ? range.from() : range.to(); - } - extendSelections(this.doc, pos, null, dir); + return dir < 0 ? range.from() : range.to(); + }, dir); }), deleteH: operation(null, function(dir, unit) { @@ -3378,24 +3376,22 @@ window.CodeMirror = (function() { }, moveV: operation(null, function(dir, unit) { - var sel = this.doc.sel, pos = [], cols = []; - if (sel.shift || sel.extend || !sel.somethingSelected()) { - for (var i = 0; i < sel.ranges.length; i++) { - var range = sel.ranges[i], headPos = cursorCoords(this, range.head, "div"); - if (range.goalColumn != null) headPos.left = range.goalColumn; - pos[i] = findPosV(this, headPos, dir, unit); - cols[i] = headPos.left; - if (unit == "page" && i == sel.primIndex) - addToScrollPos(this, 0, charCoords(this, pos[i], "div").top - headPos.top); - } - } else { - for (var i = 0; i < sel.ranges.length; i++) - pos.push(dir < 0 ? sel.ranges[i].from() : sel.ranges[i].to()); - } - extendSelections(this.doc, pos, null, dir); - sel = this.doc.sel; - if (cols.length) for (var i = 0; i < sel.ranges.length; i++) - sel.ranges[i].goalColumn = cols[i]; + var cm = this, doc = this.doc, goals = [], addScroll; + var collapse = !cm.display.shift && !doc.sel.extend && doc.sel.somethingSelected(); + doc.extendSelectionsBy(function(range) { + if (collapse) + return dir < 0 ? range.from() : range.to(); + var headPos = cursorCoords(cm, range.head, "div"); + if (range.goalColumn != null) headPos.left = range.goalColumn; + goals.push(headPos.left); + var pos = findPosV(cm, headPos, dir, unit); + if (unit == "page" && range == doc.sel.primary()) + addScroll = charCoords(cm, pos, "div").top - headPos.top; + return pos; + }); + if (addScroll) addToScrollPos(this, 0, addScroll); + if (goals.length) for (var i = 0; i < doc.sel.ranges.length; i++) + doc.sel.ranges[i].goalColumn = goals[i]; }), toggleOverwrite: function(value) { @@ -3769,7 +3765,7 @@ window.CodeMirror = (function() { goLineLeft: function(cm) { cm.extendSelectionsBy(function(range) { var top = cm.charCoords(range.head, "div").top + 5; - cm.extendSelection(cm.coordsChar({left: 0, top: top}, "div")); + return cm.coordsChar({left: 0, top: top}, "div"); }); }, goLineUp: function(cm) {cm.moveV(-1, "line");}, @@ -3794,22 +3790,31 @@ window.CodeMirror = (function() { indentMore: function(cm) {cm.indentSelection("add");}, indentLess: function(cm) {cm.indentSelection("subtract");}, insertTab: function(cm) { - cm.replaceSelection("\t", "end", "+input"); + cm.replaceSelection("\t", null, "+input"); }, defaultTab: function(cm) { if (cm.somethingSelected()) cm.indentSelection("add"); - else cm.replaceSelection("\t", "end", "+input"); + else cm.replaceSelection("\t", null, "+input"); }, transposeChars: function(cm) { - var cur = cm.getCursor(), line = cm.getLine(cur.line); - if (cur.ch > 0 && cur.ch < line.length - 1) - cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), - Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); + cm.operation(function() { + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var cur = ranges[i].head, line = getLine(cm.doc, cur.line); + if (cur.ch > 0 && cur.ch < line.length - 1) + cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), + Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); + } + }); }, newlineAndIndent: function(cm) { operation(cm, function() { - cm.replaceSelection("\n", "end", "+input"); - cm.indentLine(cm.getCursor().line, null, true); + var len = cm.listSelections().length; + for (var i = 0; i < len; i++) { + var range = cm.listSelections()[i]; + cm.replaceRange("\n", range.anchor, range.head, "+input"); + cm.indentLine(range.from().line + 1, null, true); + } })(); }, toggleOverwrite: function(cm) {cm.toggleOverwrite();} @@ -5179,10 +5184,8 @@ window.CodeMirror = (function() { listSelections: function() { return this.sel.ranges; }, somethingSelected: function() {return this.sel.somethingSelected();}, - setCursor: docOperation(function(line, ch, extend) { - var pos = clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line); - if (extend) extendSelection(this, pos); - else setSimpleSelection(this, pos); + setCursor: docOperation(function(line, ch) { + setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line)); }), setSelection: docOperation(function(anchor, head, bias) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias); @@ -5190,11 +5193,11 @@ window.CodeMirror = (function() { extendSelection: docOperation(function(from, to, bias) { extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias); }), - extendSelections: docOperation(function(from, to, bias) { - extendSelections(this, clipPosArray(this, from), to && clipPosArray(this, to), bias); + extendSelections: docOperation(function(array, bias) { + extendSelections(this, clipPosArray(this, array), bias); }), extendSelectionsBy: docOperation(function(f, bias) { - extendSelections(this, map(this.sel.ranges, f), null, bias); + extendSelections(this, map(this.sel.ranges, f), bias); }), setSelections: docOperation(function(ranges, primary, bias) { for (var i = 0, out = []; i < ranges.length; i++) @@ -5417,7 +5420,6 @@ window.CodeMirror = (function() { n -= chunk.first; while (!chunk.lines) { for (var i = 0;; ++i) { - if (!chunk.children[i]) debugger; var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break; } n -= sz; diff --git a/test/multi_test.js b/test/multi_test.js index 7d20e7cecb..b159ab058d 100644 --- a/test/multi_test.js +++ b/test/multi_test.js @@ -13,6 +13,76 @@ eqPos(sels[i].head, head, "head of selection " + i); } } + function hasCursors(cm) { + var sels = cm.listSelections(); + var given = (arguments.length - 1) / 2; + if (sels.length != given) + throw new Failure("expected " + given + " selections, found " + sels.length); + for (var i = 0, p = 1; i < given; i++, p += 2) { + eqPos(sels[i].anchor, sels[i].head, "something selected for " + i); + var head = Pos(arguments[p], arguments[p + 1]); + eqPos(sels[i].head, head, "selection " + i); + } + } + + testCM("getSelection", function(cm) { + select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)}, {anchor: Pos(2, 2), head: Pos(2, 0)}); + eq(cm.getSelection(), "1234\n56\n90"); + eq(cm.getSelection(false).join("|"), "1234|56|90"); + eq(cm.getSelections().join("|"), "1234\n56|90"); + }, {value: "1234\n5678\n90"}); + + testCM("setSelection", function(cm) { + select(cm, Pos(3, 0), Pos(0, 0), {anchor: Pos(2, 5), head: Pos(1, 0)}); + hasSelections(cm, 0, 0, 0, 0, + 2, 5, 1, 0, + 3, 0, 3, 0); + cm.setSelection(Pos(1, 2), Pos(1, 1)); + hasSelections(cm, 1, 2, 1, 1); + select(cm, {anchor: Pos(1, 1), head: Pos(2, 4)}, + {anchor: Pos(0, 0), head: Pos(1, 3)}, + Pos(3, 0), Pos(2, 2)); + hasSelections(cm, 0, 0, 2, 4, + 3, 0, 3, 0); + cm.setSelections([{anchor: Pos(0, 1), head: Pos(0, 2)}, + {anchor: Pos(1, 1), head: Pos(1, 2)}, + {anchor: Pos(2, 1), head: Pos(2, 2)}], 1); + eqPos(cm.getCursor("head"), Pos(1, 2)); + eqPos(cm.getCursor("anchor"), Pos(1, 1)); + eqPos(cm.getCursor("from"), Pos(1, 1)); + eqPos(cm.getCursor("to"), Pos(1, 2)); + cm.setCursor(Pos(1, 1)); + hasCursors(cm, 1, 1); + }, {value: "abcde\nabcde\nabcde\n"}); + + testCM("somethingSelected", function(cm) { + select(cm, Pos(0, 1), {anchor: Pos(0, 3), head: Pos(0, 5)}); + eq(cm.somethingSelected(), true); + select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5)); + eq(cm.somethingSelected(), false); + }, {value: "123456789"}); + + testCM("extendSelection", function(cm) { + select(cm, Pos(0, 1), Pos(1, 1), Pos(2, 1)); + cm.setExtending(true); + cm.extendSelections([Pos(0, 2), Pos(1, 0), Pos(2, 3)]); + hasSelections(cm, 0, 1, 0, 2, + 1, 1, 1, 0, + 2, 1, 2, 3); + cm.extendSelection(Pos(2, 4), Pos(2, 0)); + hasSelections(cm, 2, 4, 2, 0); + }, {value: "1234\n1234\n1234"}); + + testCM("addSelection", function(cm) { + select(cm, Pos(0, 1), Pos(1, 1)); + cm.addSelection(Pos(0, 0), Pos(0, 4)); + hasSelections(cm, 0, 0, 0, 4, + 1, 1, 1, 1); + cm.addSelection(Pos(2, 2)); + hasSelections(cm, 0, 0, 0, 4, + 1, 1, 1, 1, + 2, 2, 2, 2); + }, {value: "1234\n1234\n1234"}); testCM("replaceSelection", function(cm) { var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)}, @@ -133,8 +203,7 @@ testCM("goLineStart", function(cm) { select(cm, Pos(0, 2), Pos(0, 3), Pos(1, 1)); cm.execCommand("goLineStart"); - hasSelections(cm, 0, 0, 0, 0, - 1, 0, 1, 0); + hasCursors(cm, 0, 0, 1, 0); select(cm, Pos(1, 1), Pos(0, 1)); cm.setExtending(true); cm.execCommand("goLineStart"); @@ -142,5 +211,57 @@ 1, 1, 1, 0); }, {value: "foo\nbar\nbaz"}); - + testCM("moveV", function(cm) { + select(cm, Pos(0, 2), Pos(1, 2)); + cm.execCommand("goLineDown"); + hasCursors(cm, 1, 2, 2, 2); + cm.execCommand("goLineUp"); + hasCursors(cm, 0, 2, 1, 2); + cm.execCommand("goLineUp"); + hasCursors(cm, 0, 0, 0, 2); + cm.execCommand("goLineUp"); + hasCursors(cm, 0, 0); + select(cm, Pos(0, 2), Pos(1, 2)); + cm.setExtending(true); + cm.execCommand("goLineDown"); + hasSelections(cm, 0, 2, 2, 2); + }, {value: "12345\n12345\n12345"}); + + testCM("moveH", function(cm) { + select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5), Pos(2, 3)); + cm.execCommand("goCharRight"); + hasCursors(cm, 0, 2, 0, 4, 1, 0, 2, 4); + cm.execCommand("goCharLeft"); + hasCursors(cm, 0, 1, 0, 3, 0, 5, 2, 3); + for (var i = 0; i < 15; i++) + cm.execCommand("goCharRight"); + hasCursors(cm, 2, 4, 2, 5); + }, {value: "12345\n12345\n12345"}); + + testCM("newlineAndIndent", function(cm) { + select(cm, Pos(0, 5), Pos(1, 5)); + cm.execCommand("newlineAndIndent"); + hasCursors(cm, 1, 2, 3, 2); + eq(cm.getValue(), "x = [\n 1];\ny = [\n 2];"); + cm.undo(); + eq(cm.getValue(), "x = [1];\ny = [2];"); + hasCursors(cm, 0, 5, 1, 5); + select(cm, Pos(0, 5), Pos(0, 6)); + cm.execCommand("newlineAndIndent"); + hasCursors(cm, 1, 2, 2, 0); + eq(cm.getValue(), "x = [\n 1\n];\ny = [2];"); + }, {value: "x = [1];\ny = [2];", mode: "javascript"}); + + testCM("goDocStartEnd", function(cm) { + select(cm, Pos(0, 1), Pos(1, 1)); + cm.execCommand("goDocStart"); + hasCursors(cm, 0, 0); + select(cm, Pos(0, 1), Pos(1, 1)); + cm.execCommand("goDocEnd"); + hasCursors(cm, 1, 3); + select(cm, Pos(0, 1), Pos(1, 1)); + cm.setExtending(true); + cm.execCommand("goDocEnd"); + hasSelections(cm, 1, 1, 1, 3); + }, {value: "abc\ndef"}); })(); From 5395b144107d7538641126416a5b6c7bfdb30ef6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 17:30:59 +0100 Subject: [PATCH 010/195] Simplify and organize click handling --- lib/codemirror.js | 134 +++++++++++++++++++++++++++++------------------------- 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 0d70bd8cc2..3e83146a17 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1789,10 +1789,9 @@ window.CodeMirror = (function() { return coordsChar(cm, x - space.left, y - space.top); } - var lastClick, lastDoubleClick; function onMouseDown(e) { if (signalDOMEvent(this, e)) return; - var cm = this, display = cm.display, doc = cm.doc; + var cm = this, display = cm.display; display.shift = e.shiftKey; if (eventInWidget(display, e)) { @@ -1806,87 +1805,101 @@ window.CodeMirror = (function() { var start = posFromMouse(cm, e); switch (e_button(e)) { - case 3: - if (captureRightClick) onContextMenu.call(cm, cm, e); - return; + case 1: + if (start) + leftButtonDown(cm, e, start); + else if (e_target(e) == display.scroller) + e_preventDefault(e); + break; case 2: if (webkit) cm.state.lastMiddleDown = +new Date; if (start) extendSelection(cm.doc, start); setTimeout(bind(focusInput, cm), 20); e_preventDefault(e); - return; + break; + case 3: + if (captureRightClick) onContextMenu.call(cm, cm, e); + break; } - // For button 1, if it was clicked inside the editor - // (posFromMouse returning non-null), we have to adjust the - // selection. - if (!start) {if (e_target(e) == display.scroller) e_preventDefault(e); return;} + } + var lastClick, lastDoubleClick; + function leftButtonDown(cm, e, start) { if (!cm.state.focused) onFocus(cm); - var now = +new Date, type = "single"; + var now = +new Date, type; if (lastDoubleClick && lastDoubleClick.time > now - 400 && cmp(lastDoubleClick.pos, start) == 0) { type = "triple"; - e_preventDefault(e); - setTimeout(bind(focusInput, cm), 20); - selectLine(cm, start.line); } else if (lastClick && lastClick.time > now - 400 && cmp(lastClick.pos, start) == 0) { type = "double"; lastDoubleClick = {time: now, pos: start}; - e_preventDefault(e); - var word = findWordAt(getLine(doc, start.line).text, start); - extendSelection(cm.doc, word.from, word.to); - } else { lastClick = {time: now, pos: start}; } - - var last = start, range = doc.sel.primary(); - if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !range.empty() && - type == "single" && doc.sel.contains(start) && doc.sel.somethingSelected()) { - var dragEnd = operation(cm, function(e2) { - if (webkit) display.scroller.draggable = false; - cm.state.draggingText = false; - off(document, "mouseup", dragEnd); - off(display.scroller, "drop", dragEnd); - if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { - e_preventDefault(e2); - extendSelection(cm.doc, start); - focusInput(cm); - // Work around unexplainable focus problem in IE9 (#2127) - if (old_ie && !ie_lt9) - setTimeout(function() {document.body.focus(); focusInput(cm);}, 20); - } - }); - // Let the drag handler handle this. - if (webkit) display.scroller.draggable = true; - cm.state.draggingText = dragEnd; - // IE's approach to draggable - if (display.scroller.dragDrop) display.scroller.dragDrop(); - on(document, "mouseup", dragEnd); - on(display.scroller, "drop", dragEnd); - return; + } else { + type = "single"; + lastClick = {time: now, pos: start}; } + + var sel = cm.doc.sel; + if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !sel.primary().empty() && + type == "single" && sel.contains(start) && sel.somethingSelected()) + leftButtonStartDrag(cm, e, start); + else + leftButtonNoDrag(cm, e, start, type); + } + + function leftButtonStartDrag(cm, e, start) { + var display = cm.display; + var dragEnd = operation(cm, function(e2) { + if (webkit) display.scroller.draggable = false; + cm.state.draggingText = false; + off(document, "mouseup", dragEnd); + off(display.scroller, "drop", dragEnd); + if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { + e_preventDefault(e2); + extendSelection(cm.doc, start); + focusInput(cm); + // Work around unexplainable focus problem in IE9 (#2127) + if (old_ie && !ie_lt9) + setTimeout(function() {document.body.focus(); focusInput(cm);}, 20); + } + }); + // Let the drag handler handle this. + if (webkit) display.scroller.draggable = true; + cm.state.draggingText = dragEnd; + // IE's approach to draggable + if (display.scroller.dragDrop) display.scroller.dragDrop(); + on(document, "mouseup", dragEnd); + on(display.scroller, "drop", dragEnd); + } + + function leftButtonNoDrag(cm, e, start, type) { + var display = cm.display, doc = cm.doc; e_preventDefault(e); - if (type == "single") extendSelection(cm.doc, clipPos(doc, start)); + + if (type == "single") { + extendSelection(cm.doc, clipPos(doc, start)); + } else if (type == "double") { + var word = findWordAt(getLine(cm.doc, start.line).text, start); + extendSelection(cm.doc, word.from, word.to); + } else { + selectLine(cm, start.line); + } // FIXME[sel] Build a consistent model for handling clicks + multisel - var startstart = range.from(), startend = range.to(), lastPos = start; + var startSel = doc.sel.primary(), lastPos = start; function doSelect(cur) { if (cmp(lastPos, cur) == 0) return; lastPos = cur; if (type == "single") { - extendSelection(cm.doc, clipPos(doc, start), cur); - return; - } - - startstart = clipPos(doc, startstart); - startend = clipPos(doc, startend); - if (type == "double") { + extendSelection(doc, clipPos(doc, start), cur); + } else if (type == "double") { var word = findWordAt(getLine(doc, cur.line).text, cur); - if (cmp(cur, startstart) < 0) extendSelection(cm.doc, word.from, startend); - else extendSelection(cm.doc, startstart, word.to); + if (cmp(word.from, startSel.from()) < 0) extendSelection(doc, word.from, clipPos(doc, startSel.to())); + else extendSelection(doc, clipPos(doc, startSel.from()), word.to); } else if (type == "triple") { - if (cmp(cur, startstart) < 0) extendSelection(cm.doc, startend, clipPos(doc, Pos(cur.line, 0))); - else extendSelection(cm.doc, startstart, clipPos(doc, Pos(cur.line + 1, 0))); + if (cmp(cur, startSel.from()) < 0) extendSelection(doc, Pos(cur.line, 0), clipPos(doc, startSel.to())); + else extendSelection(doc, clipPos(doc, startSel.from()), clipPos(doc, Pos(cur.line + 1, 0))); } } @@ -1901,9 +1914,8 @@ window.CodeMirror = (function() { var curCount = ++counter; var cur = posFromMouse(cm, e, true); if (!cur) return; - if (cmp(cur, last) != 0) { + if (cmp(cur, lastPos) != 0) { if (!cm.state.focused) onFocus(cm); - last = cur; doSelect(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) @@ -2006,11 +2018,11 @@ window.CodeMirror = (function() { try { var text = e.dataTransfer.getData("Text"); if (text) { - var selected = cm.state.draggingText && cm.getSelection(); + var selected = cm.state.draggingText && cm.listSelections(); setSimpleSelection(cm.doc, pos); if (selected) for (var i = 0; i < selected.length; ++i) replaceRange(cm.doc, "", selected[i].anchor, selected[i].head, "drag"); - cm.replaceSelection(text, null, "paste"); + cm.replaceSelection(text, "around", "paste"); focusInput(cm); } } From 22f63cdbc495f2e8eb1c5ac5d550650d968a8b47 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 18:37:55 +0100 Subject: [PATCH 011/195] Implement ctrl-click multi-selection Issue #778 --- lib/codemirror.js | 113 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 3e83146a17..d96067f84d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -806,17 +806,21 @@ window.CodeMirror = (function() { for (var i = 0; i < this.ranges.length; i++) { var range = this.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) - return true; + return i; } + return -1; } }; function normalizeSelection(ranges, primIndex) { + var prim = ranges[primIndex]; ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); + primIndex = ranges.indexOf(prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; if (cmp(prev.to(), cur.from()) >= 0) { - var from = prev.from(), to = cur.to(), inv = prev.head == from; + var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); + var inv = from == prev.head || from == cur.head; if (i <= primIndex) --primIndex; ranges.splice(i-- - 1, 2, new Range(inv ? to : from, inv ? from : to)); } @@ -1840,7 +1844,7 @@ window.CodeMirror = (function() { var sel = cm.doc.sel; if (cm.options.dragDrop && dragAndDrop && !isReadOnly(cm) && !sel.primary().empty() && - type == "single" && sel.contains(start) && sel.somethingSelected()) + type == "single" && sel.contains(start) > -1 && sel.somethingSelected()) leftButtonStartDrag(cm, e, start); else leftButtonNoDrag(cm, e, start, type); @@ -1875,34 +1879,45 @@ window.CodeMirror = (function() { var display = cm.display, doc = cm.doc; e_preventDefault(e); - if (type == "single") { - extendSelection(cm.doc, clipPos(doc, start)); - } else if (type == "double") { - var word = findWordAt(getLine(cm.doc, start.line).text, start); - extendSelection(cm.doc, word.from, word.to); + if (e.ctrlKey) { + var inside = doc.sel.contains(start); + if (inside > -1) + setSelection(doc, new Selection(doc.sel.ranges, inside)); + else + setSelection(doc, normalizeSelection(doc.sel.ranges.concat([new Range(start, start)]), + doc.sel.ranges.length)); } else { - selectLine(cm, start.line); + extendSelection(doc, start, start); } - // FIXME[sel] Build a consistent model for handling clicks + multisel - var startSel = doc.sel.primary(), lastPos = start; + var lastPos = start, startSel = doc.sel; + function extendTo(pos, force) { + if (!force && cmp(lastPos, pos) == 0) return; + lastPos = pos; - function doSelect(cur) { - if (cmp(lastPos, cur) == 0) return; - lastPos = cur; - - if (type == "single") { - extendSelection(doc, clipPos(doc, start), cur); - } else if (type == "double") { - var word = findWordAt(getLine(doc, cur.line).text, cur); - if (cmp(word.from, startSel.from()) < 0) extendSelection(doc, word.from, clipPos(doc, startSel.to())); - else extendSelection(doc, clipPos(doc, startSel.from()), word.to); - } else if (type == "triple") { - if (cmp(cur, startSel.from()) < 0) extendSelection(doc, Pos(cur.line, 0), clipPos(doc, startSel.to())); - else extendSelection(doc, clipPos(doc, startSel.from()), clipPos(doc, Pos(cur.line + 1, 0))); + var oldRange = startSel.primary(); + var anchor = oldRange.anchor, head = pos; + if (type != "single") { + if (type == "double") + var range = findWordAt(getLine(doc, pos.line).text, pos); + else + var range = {from: Pos(pos.line, 0), to: clipPos(pos.line + 1, 0)}; + if (cmp(range.from, anchor) < 0) { + head = range.from; + anchor = maxPos(oldRange.to(), range.to); + } else { + head = range.to; + anchor = minPos(oldRange.from(), range.from); + } } + + var ranges = startSel.ranges.slice(0); + ranges[startSel.primIndex] = new Range(clipPos(doc, anchor), head); + setSelection(doc, normalizeSelection(ranges, startSel.primIndex)); } + if (type != "single") { extendTo(start, true); startSel = doc.sel; } + var editorSize = getRect(display.wrapper); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at @@ -1916,7 +1931,7 @@ window.CodeMirror = (function() { if (!cur) return; if (cmp(cur, lastPos) != 0) { if (!cm.state.focused) onFocus(cm); - doSelect(cur); + extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) setTimeout(operation(cm, function(){if (counter == curCount) extend(e);}), 150); @@ -2009,7 +2024,7 @@ window.CodeMirror = (function() { for (var i = 0; i < n; ++i) loadFile(files[i], i); } else { // Don't do a replace if the drop happened inside of the selected text. - if (cm.state.draggingText && cm.doc.sel.contains(pos)) { + if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(bind(focusInput, cm), 20); @@ -2304,7 +2319,7 @@ window.CodeMirror = (function() { // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; - if (reset && !cm.doc.sel.contains(pos)) + if (reset && !cm.doc.sel.contains(pos) > -1) operation(cm, setSelection)(cm.doc, pos, pos); var oldCSS = display.input.style.cssText; @@ -2566,7 +2581,7 @@ window.CodeMirror = (function() { }); } - if (doc.sel.contains(change.from, change.to)) + if (doc.sel.contains(change.from, change.to) > -1) cm.curOp.cursorActivity = true; updateDoc(doc, change, spans, selAfter, estimateHeight(cm)); @@ -2621,6 +2636,8 @@ window.CodeMirror = (function() { var cmp = CodeMirror.cmpPos = function(a, b) { return a.line - b.line || a.ch - b.ch; }; function copyPos(x) {return Pos(x.line, x.ch);} + function maxPos(a, b) { return cmp(a, b) < 0 ? b : a; } + function minPos(a, b) { return cmp(a, b) < 0 ? a : b; } // SELECTION @@ -2643,36 +2660,36 @@ window.CodeMirror = (function() { return out; } - function extendRange(doc, range, pos, other) { + function extendRange(doc, range, head, other) { if (doc.cm && doc.cm.display.shift || doc.extend) { var anchor = range.anchor; if (other) { - var posBefore = cmp(pos, anchor) < 0; + var posBefore = cmp(head, anchor) < 0; if (posBefore != (cmp(other, anchor) < 0)) { - anchor = pos; - pos = other; - } else if (posBefore != (cmp(pos, other) < 0)) { - pos = other; + anchor = head; + head = other; + } else if (posBefore != (cmp(head, other) < 0)) { + head = other; } } - return new Range(anchor, pos); + return new Range(anchor, head); } else { - return new Range(pos, other || pos); + return new Range(other || head, head); } } // If shift is held, this will move the selection anchor. Otherwise, // it'll set the whole selection. - function extendSelection(doc, pos, other, bias) { - setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), pos, other)], 0), bias); + function extendSelection(doc, head, other, bias) { + setSelection(doc, new Selection([extendRange(doc, doc.sel.primary(), head, other)], 0), bias); if (doc.cm) doc.cm.curOp.userSelChange = true; } // Extend all selections (pos is an array of selections with length // equal the number of selections) - function extendSelections(doc, pos, bias) { + function extendSelections(doc, heads, bias) { for (var out = [], i = 0; i < doc.sel.ranges.length; i++) - out[i] = extendRange(doc, doc.sel.ranges[i], pos[i], null); + out[i] = extendRange(doc, doc.sel.ranges[i], heads[i], null); var newSel = normalizeSelection(out, doc.sel.primIndex); setSelection(doc, newSel, bias); if (doc.cm) doc.cm.curOp.userSelChange = true; @@ -2684,8 +2701,8 @@ window.CodeMirror = (function() { setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), bias); } - function setSimpleSelection(doc, anchor, head, bias, checkAtomic) { - setSelection(doc, simpleSelection(anchor, head), bias, checkAtomic); + function setSimpleSelection(doc, anchor, head, bias) { + setSelection(doc, simpleSelection(anchor, head), bias); } function setSelectionAddToHistory(doc, sel) { @@ -3046,10 +3063,6 @@ window.CodeMirror = (function() { return {from: Pos(pos.line, start), to: Pos(pos.line, end)}; } - function selectLine(cm, line) { - extendSelection(cm.doc, Pos(line, 0), clipPos(cm.doc, Pos(line + 1, 0))); - } - // PROTOTYPE // The publicly visible API. Note that operation(null, f) means @@ -5202,11 +5215,11 @@ window.CodeMirror = (function() { setSelection: docOperation(function(anchor, head, bias) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), bias); }), - extendSelection: docOperation(function(from, to, bias) { - extendSelection(this, clipPos(this, from), to && clipPos(this, to), bias); + extendSelection: docOperation(function(head, other, bias) { + extendSelection(this, clipPos(this, head), other && clipPos(this, other), bias); }), - extendSelections: docOperation(function(array, bias) { - extendSelections(this, clipPosArray(this, array), bias); + extendSelections: docOperation(function(heads, bias) { + extendSelections(this, clipPosArray(this, heads), bias); }), extendSelectionsBy: docOperation(function(f, bias) { extendSelections(this, map(this.sel.ranges, f), bias); From 13364623ac9d33a6bd53dc52d055eb04b07e0176 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 21:14:10 +0100 Subject: [PATCH 012/195] Implement alt-drag rectangle-selection Issue #778 --- lib/codemirror.js | 69 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index d96067f84d..5f194669dd 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1779,7 +1779,7 @@ window.CodeMirror = (function() { } } - function posFromMouse(cm, e, liberal) { + function posFromMouse(cm, e, liberal, forRect) { var display = cm.display; if (!liberal) { var target = e_target(e); @@ -1789,8 +1789,12 @@ window.CodeMirror = (function() { } var x, y, space = getRect(display.lineSpace); // Fails unpredictably on IE[67] when mouse is dragged around quickly. - try { x = e.clientX; y = e.clientY; } catch (e) { return null; } - return coordsChar(cm, x - space.left, y - space.top); + try { x = e.clientX - space.left; y = e.clientY - space.top; } + catch (e) { return null; } + var coords = coordsChar(cm, x, y); + if (forRect && coords.ch == getLine(cm.doc, coords.line).text.length && coords.xRel == 1) + coords = Pos(coords.line, Math.round((x - paddingLeft(cm.display)) / charWidth(cm.display))); + return coords; } function onMouseDown(e) { @@ -1879,13 +1883,17 @@ window.CodeMirror = (function() { var display = cm.display, doc = cm.doc; e_preventDefault(e); - if (e.ctrlKey) { + if (mac ? e.metaKey : e.ctrlKey) { var inside = doc.sel.contains(start); if (inside > -1) setSelection(doc, new Selection(doc.sel.ranges, inside)); else setSelection(doc, normalizeSelection(doc.sel.ranges.concat([new Range(start, start)]), doc.sel.ranges.length)); + } else if (e.altKey) { + setSimpleSelection(doc, start, start); + type = "rect"; + start = posFromMouse(cm, e, true, true); } else { extendSelection(doc, start, start); } @@ -1895,28 +1903,43 @@ window.CodeMirror = (function() { if (!force && cmp(lastPos, pos) == 0) return; lastPos = pos; - var oldRange = startSel.primary(); - var anchor = oldRange.anchor, head = pos; - if (type != "single") { - if (type == "double") - var range = findWordAt(getLine(doc, pos.line).text, pos); - else - var range = {from: Pos(pos.line, 0), to: clipPos(pos.line + 1, 0)}; - if (cmp(range.from, anchor) < 0) { - head = range.from; - anchor = maxPos(oldRange.to(), range.to); - } else { - head = range.to; - anchor = minPos(oldRange.from(), range.from); + if (type == "rect") { + var ranges = []; + var left = Math.min(start.ch, pos.ch), right = Math.max(start.ch, pos.ch); + for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); + line <= end; line++) { + var text = getLine(doc, line).text; + if (text.length >= left) + ranges.push(new Range(Pos(line, left), Pos(line, Math.min(text.length, right)))); + } + if (!ranges.length) ranges.push(new Range(start, start)); + setSelection(doc, new Selection(ranges, ranges.length - 1)); + } else { + var oldRange = startSel.primary(); + var anchor = oldRange.anchor, head = pos; + if (type != "single") { + if (type == "double") + var range = findWordAt(getLine(doc, pos.line).text, pos); + else + var range = {from: Pos(pos.line, 0), to: clipPos(pos.line + 1, 0)}; + if (cmp(range.from, anchor) < 0) { + head = range.from; + anchor = maxPos(oldRange.to(), range.to); + } else { + head = range.to; + anchor = minPos(oldRange.from(), range.from); + } } + var ranges = startSel.ranges.slice(0); + ranges[startSel.primIndex] = new Range(clipPos(doc, anchor), head); + setSelection(doc, normalizeSelection(ranges, startSel.primIndex)); } - - var ranges = startSel.ranges.slice(0); - ranges[startSel.primIndex] = new Range(clipPos(doc, anchor), head); - setSelection(doc, normalizeSelection(ranges, startSel.primIndex)); } - if (type != "single") { extendTo(start, true); startSel = doc.sel; } + if (type == "double" || type == "triple") { + extendTo(start, true); + startSel = doc.sel; + } var editorSize = getRect(display.wrapper); // Used to ensure timeout re-tries don't fire when another extend @@ -1927,7 +1950,7 @@ window.CodeMirror = (function() { function extend(e) { var curCount = ++counter; - var cur = posFromMouse(cm, e, true); + var cur = posFromMouse(cm, e, true, type == "rect"); if (!cur) return; if (cmp(cur, lastPos) != 0) { if (!cm.state.focused) onFocus(cm); From e01274588d9700768c1fedeffde170fc816b00b1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 21:26:04 +0100 Subject: [PATCH 013/195] Implement line-per-selection paste Issue #778 --- lib/codemirror.js | 9 +++++---- test/test.js | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 5f194669dd..e9aabb2332 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1582,17 +1582,18 @@ window.CodeMirror = (function() { cm.display.shift = false; var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) ++same; - var inserted = text.slice(same); + var inserted = text.slice(same), textLines = splitLines(inserted); + var multiPaste = cm.state.pasteIncoming && textLines.length > 1 && doc.sel.ranges.length == textLines.length; + for (var i = doc.sel.ranges.length - 1; i >= 0; i--) { var range = doc.sel.ranges[i]; var from = range.from(), to = range.to(); if (same < prevInput.length) from = Pos(from.line, from.ch - (prevInput.length - same)); else if (cm.state.overwrite && cmp(from, to) == 0 && !cm.state.pasteIncoming) - to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + inserted.length)); - + to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); var updateInput = cm.curOp.updateInput; - var changeEvent = {from: from, to: to, text: splitLines(inserted), + var changeEvent = {from: from, to: to, text: multiPaste ? [textLines[i]] : textLines, origin: cm.state.pasteIncoming ? "paste" : cm.state.cutIncoming ? "cut" : "+input"}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); diff --git a/test/test.js b/test/test.js index 6a32ca553f..0c129e9444 100644 --- a/test/test.js +++ b/test/test.js @@ -132,8 +132,8 @@ testCM("extendSelection", function(cm) { eqPos(cm.getCursor("anchor"), Pos(4, 5)); cm.setExtending(false); cm.extendSelection(Pos(0, 3), Pos(0, 4)); - eqPos(cm.getCursor("head"), Pos(0, 4)); - eqPos(cm.getCursor("anchor"), Pos(0, 3)); + eqPos(cm.getCursor("head"), Pos(0, 3)); + eqPos(cm.getCursor("anchor"), Pos(0, 4)); }); testCM("lines", function(cm) { From 8ca09be753cefcc81a22ccb1a576add2d1866766 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 22:12:25 +0100 Subject: [PATCH 014/195] [comment & continuecomment addons] Make multi-selection aware Issue #778 --- addon/comment/comment.js | 17 ++++++++-- addon/comment/continuecomment.js | 68 ++++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/addon/comment/comment.js b/addon/comment/comment.js index 5975b0bf69..c36e176912 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -11,8 +11,21 @@ } CodeMirror.commands.toggleComment = function(cm) { - var from = cm.getCursor("start"), to = cm.getCursor("end"); - cm.uncomment(from, to) || cm.lineComment(from, to); + var minLine = Infinity, ranges = cm.listSelections(), mode = null; + for (var i = ranges.length - 1; i >= 0; i--) { + var from = ranges[i].from(), to = ranges[i].to(); + if (from.line >= minLine) continue; + if (to.line >= minLine) to = Pos(minLine, 0); + minLine = from.line; + if (mode == null) { + if (cm.uncomment(from, to)) mode = "un"; + else { cm.lineComment(from, to); mode = "line"; } + } else if (mode == "un") { + cm.uncomment(from, to); + } else { + cm.lineComment(from, to); + } + } }; CodeMirror.defineExtension("lineComment", function(from, to, options) { diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index a3370a6072..0566c74e43 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -4,42 +4,50 @@ CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); function continueComment(cm) { - var pos = cm.getCursor(), token = cm.getTokenAt(pos); - if (token.type != "comment" || cm.getOption("disableInput")) return CodeMirror.Pass; - var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(), mode, inserts = []; + for (var i = 0; i < ranges.length; i++) { + var pos = ranges[i].head, token = cm.getTokenAt(pos); + if (token.type != "comment") return CodeMirror.Pass; + var modeHere = CodeMirror.innerMode(cm.getMode(), token.state).mode; + if (!mode) mode = modeHere; + else if (mode != modeHere) return CodeMirror.Pass; - var insert; - if (mode.blockCommentStart && mode.blockCommentContinue) { - var end = token.string.indexOf(mode.blockCommentEnd); - var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; - if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { - // Comment ended, don't continue it - } else if (token.string.indexOf(mode.blockCommentStart) == 0) { - insert = full.slice(0, token.start); - if (!/^\s*$/.test(insert)) { - insert = ""; - for (var i = 0; i < token.start; ++i) insert += " "; + var insert = null; + if (mode.blockCommentStart && mode.blockCommentContinue) { + var end = token.string.indexOf(mode.blockCommentEnd); + var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; + if (end != -1 && end == token.string.length - mode.blockCommentEnd.length) { + // Comment ended, don't continue it + } else if (token.string.indexOf(mode.blockCommentStart) == 0) { + insert = full.slice(0, token.start); + if (!/^\s*$/.test(insert)) { + insert = ""; + for (var i = 0; i < token.start; ++i) insert += " "; + } + } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && + found + mode.blockCommentContinue.length > token.start && + /^\s*$/.test(full.slice(0, found))) { + insert = full.slice(0, found); } - } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && - found + mode.blockCommentContinue.length > token.start && - /^\s*$/.test(full.slice(0, found))) { - insert = full.slice(0, found); + if (insert != null) insert += mode.blockCommentContinue; } - if (insert != null) insert += mode.blockCommentContinue; - } - if (insert == null && mode.lineComment) { - var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); - if (found > -1) { - insert = line.slice(0, found); - if (/\S/.test(insert)) insert = null; - else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0]; + if (insert == null && mode.lineComment) { + var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); + if (found > -1) { + insert = line.slice(0, found); + if (/\S/.test(insert)) insert = null; + else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0]; + } } + if (insert == null) return CodeMirror.Pass; + inserts[i] = "\n" + insert; } - if (insert != null) - cm.replaceSelection("\n" + insert, "end"); - else - return CodeMirror.Pass; + cm.operation(function() { + for (var i = ranges.length - 1; i >= 0; i--) + cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert"); + }); } CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { From 27ebe5e74c2bdfff7ddfc55d443ee56318ea58d0 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 11 Nov 2013 22:41:39 +0100 Subject: [PATCH 015/195] [matchbrackets addon] Make multi-selection aware Issue #778 --- addon/edit/matchbrackets.js | 53 ++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 465b6ccaa9..4d0c853a52 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -10,12 +10,12 @@ var maxScanLen = (state && state.maxScanLineLength) || 10000; var maxScanLines = (state && state.maxScanLines) || 100; - var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1; + var line = cm.getLineHandle(where.line), pos = where.ch - 1; var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; if (!match) return null; var forward = match.charAt(1) == ">", d = forward ? 1 : -1; - if (strict && forward != (pos == cur.ch)) return null; - var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1)); + if (strict && forward != (pos == where.ch)) return null; + var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; function scan(line, lineNo, start) { @@ -33,41 +33,50 @@ } } } - for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) { - if (i == cur.line) found = scan(line, i, pos); + for (var i = where.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) { + if (i == where.line) found = scan(line, i, pos); else found = scan(cm.getLineHandle(i), i); if (found) break; } - return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), + return {from: Pos(where.line, pos), to: found && Pos(i, found.pos), match: found && found.match, forward: forward}; } function matchBrackets(cm, autoclear) { // Disable brace matching in long lines, since it'll cause hugely slow updates var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; - var found = findMatchingBracket(cm); - if (!found || cm.getLine(found.from.line).length > maxHighlightLen || - found.to && cm.getLine(found.to.line).length > maxHighlightLen) - return; + var marks = [], ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head); + if (match && cm.getLine(match.from.line).length <= maxHighlightLen && + match.to && cm.getLine(match.to.line).length <= maxHighlightLen) { + var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; + marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); + if (match.to) + marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); + } + } - var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - var one = cm.markText(found.from, Pos(found.from.line, found.from.ch + 1), {className: style}); - var two = found.to && cm.markText(found.to, Pos(found.to.line, found.to.ch + 1), {className: style}); - // Kludge to work around the IE bug from issue #1193, where text - // input stops going to the textarea whenever this fires. - if (ie_lt8 && cm.state.focused) cm.display.input.focus(); - var clear = function() { - cm.operation(function() { one.clear(); two && two.clear(); }); - }; - if (autoclear) setTimeout(clear, 800); - else return clear; + if (marks.length) { + // Kludge to work around the IE bug from issue #1193, where text + // input stops going to the textare whever this fires. + if (ie_lt8 && cm.state.focused) cm.display.input.focus(); + + var clear = function() { + cm.operation(function() { + for (var i = 0; i < marks.length; i++) marks[i].clear(); + }); + }; + if (autoclear) setTimeout(clear, 800); + else return clear; + } } var currentlyHighlighted = null; function doMatchBrackets(cm) { cm.operation(function() { if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} - if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false); + currentlyHighlighted = matchBrackets(cm, false); }); } From 3b9fd6060bfb5d04cbd5013f5615eb94b6d43707 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 09:35:52 +0100 Subject: [PATCH 016/195] [closebrackets addon] Adjust to multi-selections And fix example code in demo. Issue #778 --- addon/edit/closebrackets.js | 101 +++++++++++++++++++++++++++++--------------- demo/closebrackets.html | 49 +++++++++------------ 2 files changed, 85 insertions(+), 65 deletions(-) diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 0575222be6..64c56f9530 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -28,56 +28,87 @@ var map = { name : "autoCloseBrackets", Backspace: function(cm) { - if (cm.somethingSelected() || cm.getOption("disableInput")) return CodeMirror.Pass; - var cur = cm.getCursor(), around = charsAround(cm, cur); - if (around && pairs.indexOf(around) % 2 == 0) + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) return CodeMirror.Pass; + var around = charsAround(cm, ranges[i].head); + if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + } + for (var i = ranges.length - 1; i >= 0; i--) { + var cur = ranges[i].head; cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1)); - else - return CodeMirror.Pass; + } } }; var closingBrackets = ""; for (var i = 0; i < pairs.length; i += 2) (function(left, right) { if (left != right) closingBrackets += right; - function surround(cm) { - var selection = cm.getSelection(); - cm.replaceSelection(left + selection + right); - } - function maybeOverwrite(cm) { - var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1)); - if (ahead != right || cm.somethingSelected()) return CodeMirror.Pass; - else cm.execCommand("goCharRight"); - } map["'" + left + "'"] = function(cm) { - if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment" || - cm.getOption("disableInput")) - return CodeMirror.Pass; - if (cm.somethingSelected()) return surround(cm); - if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return; - var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1); - var line = cm.getLine(cur.line), nextChar = line.charAt(cur.ch), curChar = cur.ch > 0 ? line.charAt(cur.ch - 1) : ""; - if (left == right && CodeMirror.isWordChar(curChar)) - return CodeMirror.Pass; - if (line.length == cur.ch || closingBrackets.indexOf(nextChar) >= 0 || SPACE_CHAR_REGEX.test(nextChar)) - cm.replaceSelection(left + right, {head: ahead, anchor: ahead}); - else - return CodeMirror.Pass; + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(), type, next; + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i], cur = range.head, curType; + if (left == "'" && cm.getTokenTypeAt(cur) == "comment") + return CodeMirror.Pass; + if (!range.empty()) + curType = "surround"; + else if ((next = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1))) == right) + curType = "skip"; + else if (left == right && CodeMirror.isWordChar(next)) + return CodeMirror.Pass; + else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next)) + curType = "both"; + else + return CodeMirror.Pass; + if (!type) type = curType; + else if (type != curType) return CodeMirror.Pass; + } + + if (type == "skip") { + cm.execCommand("goCharRight"); + } else if (type == "surround") { + var sels = cm.getSelections(); + for (var i = 0; i < sels.length; i++) + sels[i] = left + sels[i] + right; + cm.replaceSelections(sels, "around", "+insert"); + } else if (type == "both") { + cm.replaceSelection(left + right, null, "+insert"); + cm.execCommand("goCharLeft"); + } + }; + if (left != right) map["'" + right + "'"] = function(cm) { + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var range = ranges[i]; + if (!range.empty() || + cm.getRange(range.head, CodeMirror.Pos(range.head.line, range.head.ch + 1)) != right) + return CodeMirror.Pass; + } + cm.execCommand("goCharRight"); }; - if (left != right) map["'" + right + "'"] = maybeOverwrite; })(pairs.charAt(i), pairs.charAt(i + 1)); return map; } function buildExplodeHandler(pairs) { return function(cm) { - var cur = cm.getCursor(), around = charsAround(cm, cur); - if (!around || pairs.indexOf(around) % 2 != 0 || cm.getOption("disableInput")) - return CodeMirror.Pass; + if (cm.getOption("disableInput")) return CodeMirror.Pass; + var ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + if (!ranges[i].empty()) return CodeMirror.Pass; + var around = charsAround(cm, ranges[i].head); + if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; + } cm.operation(function() { - var newPos = CodeMirror.Pos(cur.line + 1, 0); - cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input"); - cm.indentLine(cur.line + 1, null, true); - cm.indentLine(cur.line + 2, null, true); + cm.replaceSelection("\n\n", null, "+input"); + cm.execCommand("goCharLeft"); + ranges = cm.listSelections(); + for (var i = 0; i < ranges.length; i++) { + var line = ranges[i].head.line; + cm.indentLine(line, null, true); + cm.indentLine(line + 1, null, true); + } }); }; } diff --git a/demo/closebrackets.html b/demo/closebrackets.html index 8c0dc1cc3e..f7d5afbd87 100644 --- a/demo/closebrackets.html +++ b/demo/closebrackets.html @@ -26,36 +26,25 @@

Closebrackets Demo

-
+
+ + + + + +
+

Multiple Selection Keybindings

+ +
+ + + +

Demonstrates the extra bindings provided by +the multiselect addon.

+ +
diff --git a/doc/compress.html b/doc/compress.html index c2ba8f7021..55e25f3e68 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -207,6 +207,7 @@ + diff --git a/keymap/multiselect.js b/keymap/multiselect.js new file mode 100644 index 0000000000..f45f27254a --- /dev/null +++ b/keymap/multiselect.js @@ -0,0 +1,47 @@ +// A number of extra keybindings for multiple-selection handling +// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js + +(function() { + "use strict"; + + var pc = CodeMirror.keyMap.pcDefault, mac = CodeMirror.keyMap.macDefault; + var Pos = CodeMirror.Pos; + + function dialog(cm, text, shortText, f) { + if (cm.openDialog) cm.openDialog(text, f); + else f(prompt(shortText, "")); + } + + // Split selection + pc["Shift-Ctrl-L"] = mac["Shift-Cmd-L"] = function(cm) { + var ranges = [], from = cm.getCursor("from"), to = cm.getCursor("to"); + for (var line = from.line; line <= to.line; ++line) + ranges.push({anchor: line == from.line ? from : Pos(line, 0), + head: line == to.line ? to : Pos(line)}); + cm.setSelections(ranges, 0); + }; + + // Add next occurrence to selection + pc["Ctrl-D"] = mac["Cmd-D"] = function(cm) { + var at = cm.getCursor("to"); + var cur = cm.getSearchCursor(cm.getRange(cm.getCursor("from"), at), at); + if (!cur.findNext()) return; + cm.addSelection(cur.from(), cur.to()); + }; + + // Go to a single selection + pc["Alt-Esc"] = mac["Alt-Esc"] = function(cm) { + cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head")); + }; + + // Select matching parts of current selection + var dialogText = 'Match: (Use /re/ syntax for regexp)'; + pc["Alt-F"] = mac["Alt-F"] = function(cm) { + if (!cm.somethingSelected()) return; + dialog(cm, dialogText, "Match:", function(query) { + var isRE = query.match(/^\/(.*)\/([a-z]*)$/); + if (isRE) query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); + cm.selectMatches(query); + }); + }; +})(); From 765d3a967a75c9827dcce556afeb2a8106c32f1f Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 13:15:54 +0100 Subject: [PATCH 022/195] [tern addon] Add select-variable-instances functionality --- addon/tern/tern.js | 23 ++++++++++++++++++++++- demo/tern.html | 2 ++ lib/codemirror.js | 1 + 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/addon/tern/tern.js b/addon/tern/tern.js index 7f83c4e4c0..fa2a2c3528 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -106,6 +106,8 @@ rename: function(cm) { rename(this, cm); }, + selectName: function(cm) { selectName(this, cm); }, + request: function (cm, query, c, pos) { var self = this; var doc = findDoc(this, cm.getDoc()); @@ -429,6 +431,25 @@ }); } + function selectName(ts, cm) { + var cur = cm.getCursor(), token = cm.getTokenAt(cur); + if (!/\w/.test(token.string)) showError(ts, cm, "Not at a variable"); + var name = findDoc(ts, cm.doc).name; + ts.request(cm, {type: "refs"}, function(error, data) { + if (error) return showError(ts, cm, error); + var ranges = [], cur = 0; + for (var i = 0; i < data.refs.length; i++) { + var ref = data.refs[i]; + if (ref.file == name) { + ranges.push({anchor: ref.start, head: ref.end}); + if (cmpPos(cur, ref.start) >= 0 && cmpPos(cur, ref.end) <= 0) + cur = ranges.length - 1; + } + } + cm.setSelections(ranges, cur); + }); + } + var nextChangeOrig = 0; function applyChanges(ts, changes) { var perFile = Object.create(null); @@ -521,7 +542,7 @@ // Generic utilities - function cmpPos(a, b) { return a.line - b.line || a.ch - b.ch; } + var cmpPos = CodeMirror.cmpPos; function elt(tagname, cls /*, ... elts*/) { var e = document.createElement(tagname); diff --git a/demo/tern.html b/demo/tern.html index 6843c7f5ec..57027f7267 100644 --- a/demo/tern.html +++ b/demo/tern.html @@ -86,6 +86,7 @@
Ctrl-I
Find type at cursor
Alt-.
Jump to definition (Alt-, to jump back)
Ctrl-Q
Rename variable
+
Ctrl-.
Select all occurrences of a variable

Documentation is sparse for now. See the top of @@ -116,6 +117,7 @@ "Alt-.": function(cm) { server.jumpToDef(cm); }, "Alt-,": function(cm) { server.jumpBack(cm); }, "Ctrl-Q": function(cm) { server.rename(cm); }, + "Ctrl-.": function(cm) { server.selectName(cm); } }) editor.on("cursorActivity", function(cm) { server.updateArgHints(cm); }); }); diff --git a/lib/codemirror.js b/lib/codemirror.js index e9aabb2332..be6ac6f999 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5249,6 +5249,7 @@ window.CodeMirror = (function() { extendSelections(this, map(this.sel.ranges, f), bias); }), setSelections: docOperation(function(ranges, primary, bias) { + if (!ranges.length) return; for (var i = 0, out = []; i < ranges.length; i++) out[i] = new Range(clipPos(this, ranges[i].anchor), clipPos(this, ranges[i].head)); From 62827d68cac988c09cac7cda9fb7d56f40461a65 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 14:29:03 +0100 Subject: [PATCH 023/195] [doc toc script] More accurate active link highlighting --- doc/activebookmark.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/activebookmark.js b/doc/activebookmark.js index 88c879cf42..407282d02c 100644 --- a/doc/activebookmark.js +++ b/doc/activebookmark.js @@ -3,6 +3,7 @@ document.createElement("section"); document.createElement("article"); (function() { + if (!window.addEventListener) return; var pending = false, prevVal = null; function updateSoon() { @@ -41,8 +42,16 @@ document.createElement("article"); } } - if (window.addEventListener) { - window.addEventListener("scroll", updateSoon); - window.addEventListener("load", updateSoon); - } + window.addEventListener("scroll", updateSoon); + window.addEventListener("load", updateSoon); + window.addEventListener("hashchange", function() { + setTimeout(function() { + var hash = document.location.hash, found = null, m; + var marks = document.getElementById("nav").getElementsByTagName("a"); + for (var i = 0; i < marks.length; i++) + if ((m = marks[i].href.match(/(#.*)/)) && m[1] == hash) { found = i; break; } + if (found != null) for (var i = 0; i < marks.length; i++) + marks[i].className = i == found ? "active" : ""; + }, 300); + }); })(); From 7f01799b236514ab4be01b72cad8730b019ac1ea Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 16:10:46 +0100 Subject: [PATCH 024/195] Document multiple selection API Issue #778 --- doc/manual.html | 95 +++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 26 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index c56af630e5..bb0517fb9a 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -514,14 +514,15 @@

Fired whenever new input is read from the hidden textarea (typed or pasted by the user).
-
"beforeSelectionChange" (instance: CodeMirror, selection: {head, anchor})
+
"beforeSelectionChange" (instance: CodeMirror, obj: {ranges, update})
This event is fired before the selection is moved. Its - handler may modify the resulting selection head and anchor. - The selection parameter is an object - with head and anchor properties - holding {line, ch} objects, which the handler can - read and update. Handlers for this event have the same - restriction + handler may inspect the set of selection ranges, present as an + array of {anchor, head} objects in + the ranges property of the obj + argument, and optionally change them by calling + the update method on this object, passing an array + of ranges in the same format. Handlers for this event have the + same restriction as "beforeChange" handlers — they should not do anything to directly update the state of the editor.
@@ -962,46 +963,88 @@

Content manipulation methods

Cursor and selection methods

-
doc.getSelection() → string
-
Get the currently selected code.
-
doc.replaceSelection(replacement: string, ?collapse: string)
-
Replace the selection with the given string. By default, the - new selection will span the inserted text. The - optional collapse argument can be used to change - this—passing "start" or "end" will - collapse the selection to the start or end of the inserted - text.
+
doc.getSelection(?lineSep: string) → string
+
Get the currently selected code. Optionally pass a line + separator to put between the lines in the output. When multiple + selections are present, they are concatenated with instances + of lineSep in between.
+
doc.getSelections(?lineSep: string) → string
+
Returns an array containing a string for each selection, + representing the content of the selections.
+ +
doc.replaceSelection(replacement: string, ?select: string)
+
Replace the selection(s) with the given string. By default, + the new selection ends up after the inserted text. The + optional select argument can be used to change + this—passing "around" will cause the new text to be + selected, passing "start" will collapse the + selection to the start of the inserted text.
+
doc.replaceSelections(replacements: array<string>, ?select: string)
+
The length of the given array should be the same as the + number of active selections. Replaces the content of the + selections with the strings in the array. + The select argument works the same as + in replaceSelection.
doc.getCursor(?start: string) → {line, ch}
-
start is a an optional string indicating which - end of the selection to return. It may - be "start", "end", "head" +
Retrieve one end of the primary + selection. start is a an optional string indicating + which end of the selection to return. It may + be "from", "to", "head" (the side of the selection that moves when you press shift+arrow), or "anchor" (the fixed side of the selection). Omitting the argument is the same as passing "head". A {line, ch} object will be returned.
+
doc.listSelections() → array<{anchor, head}<
+
Retrieves a list of all current selections. These will + always be sorted, and never overlap (overlapping selections are + merged). Each object in the array contains anchor + and head properties referring to {line, + ch} objects.
+
doc.somethingSelected() → boolean
Return true if any text is selected.
doc.setCursor(pos: {line, ch})
Set the cursor position. You can either pass a single {line, ch} object, or the line and the - character as two separate parameters.
+ character as two separate parameters. Will replace all + selections with a single, empty selection at the given + position. +
doc.setSelection(anchor: {line, ch}, ?head: {line, ch})
-
Set the selection range. anchor +
Set a single selection range. anchor and head should be {line, ch} objects. head defaults to anchor when not given.
+
doc.setSelections(ranges: array<{anchor, head}>, ?primary: integer)
+
Sets a new set of selections. There must be at least one + selection in the given array. When primary is + given, it determines which selection is the primary one + (defaults to the last one).
+
doc.addSelection(anchor: {line, ch}, ?head: {line, ch})
+
Adds a new selection to the existing set of selections, and + makes it the primary selection.
+
doc.extendSelection(from: {line, ch}, ?to: {line, ch})
Similar to setSelection, but will, if shift is held or the extending flag is set, move the head of the selection while leaving the anchor at its current - place. to is optional, and can be passed to - ensure a region (for example a word or paragraph) will end up - selected (in addition to whatever lies between that region and - the current anchor).
+ place. to is optional, and can be passed to ensure + a region (for example a word or paragraph) will end up selected + (in addition to whatever lies between that region and the + current anchor). When multiple selections are present, all but + the primary selection will be dropped by this method. +
doc.extendSelections(heads: array<{line, ch}>)
+
An equivalent + of extendSelection + that acts on all selections at once.
+
doc.extendSelectionsBy(f: function(range: {anchor, head}) → {anchor, head}))
+
Applies the given function to all existing selections, and + calls extendSelections + on the result.
doc.setExtending(value: boolean)
Sets or clears the 'extending' flag, which acts similar to the shift key, in that it will cause cursor movement and calls @@ -1011,7 +1054,7 @@

Cursor and selection methods

cm.hasFocus() → boolean
Tells you whether the editor currently has focus.
-
cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}
+
cm.findPosH(start: {line, ch}, amount: integer, unit: string, visually: boolean) → {line, ch, ?hitSide: boolean}
Used to find the target position for horizontal cursor motion. start is a {line, ch} object, amount an integer (may be negative), From 61253d27ece551568397e63a32199e14e2dc4a5d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 16:21:23 +0100 Subject: [PATCH 025/195] Don't use .next properties in change event objects anymore --- doc/manual.html | 7 ++----- lib/codemirror.js | 23 ++++++++++------------- test/test.js | 9 +++++++-- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index bb0517fb9a..3e81b94c3a 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -460,7 +460,7 @@
"change" (instance: CodeMirror, changeObj: object)
Fires every time the content of the editor is changed. The changeObj is a {from, to, text, removed, - next} object containing information about the changes + origin} object containing information about the changes that occurred as second argument. from and to are the positions (in the pre-change coordinate system) where the change started and ended (for @@ -469,10 +469,7 @@ an array of strings representing the text that replaced the changed range (split by line). removed is the text that used to be between from and to, - which is overwritten by this change. If multiple changes - happened during a single operation, the object will have - a next property pointing to another change object - (which may point to another, etc).
+ which is overwritten by this change.
"beforeChange" (instance: CodeMirror, changeObj: object)
This event is fired before a change is applied, and its diff --git a/lib/codemirror.js b/lib/codemirror.js index be6ac6f999..c5a332264f 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1435,7 +1435,7 @@ window.CodeMirror = (function() { forceUpdate: false, updateInput: null, userSelChange: null, - textChanged: null, + changeObjs: null, selectionChanged: false, cursorActivity: false, updateMaxLine: false, @@ -1498,8 +1498,8 @@ window.CodeMirror = (function() { delayed = delayedCallbacks; delayedCallbacks = null; } - if (op.textChanged) - signal(cm, "change", cm, op.textChanged); + if (op.changeObjs) for (var i = 0; i < op.changeObjs.length; i++) + signal(cm, "change", cm, op.changeObjs[i]); if (op.cursorActivity) signal(cm, "cursorActivity", cm); if (delayed) for (var i = 0; i < delayed.length; ++i) delayed[i](); } @@ -2631,16 +2631,13 @@ window.CodeMirror = (function() { // Remember that these lines changed, for updating the display regChange(cm, from.line, to.line + 1, lendiff); - if (hasHandler(cm, "change")) { - var changeObj = {from: from, to: to, - text: change.text, - removed: change.removed, - origin: change.origin}; - if (cm.curOp.textChanged) { - for (var cur = cm.curOp.textChanged; cur.next; cur = cur.next) {} - cur.next = changeObj; - } else cm.curOp.textChanged = changeObj; - } + if (hasHandler(cm, "change")) + (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push({ + from: from, to: to, + text: change.text, + removed: change.removed, + origin: change.origin + }); } function replaceRange(doc, code, from, to, origin) { diff --git a/test/test.js b/test/test.js index 0c129e9444..b96824bd2d 100644 --- a/test/test.js +++ b/test/test.js @@ -1603,9 +1603,9 @@ testCM("beforeSelectionChange", function(cm) { testCM("change_removedText", function(cm) { cm.setValue("abc\ndef"); - var removedText; + var removedText = []; cm.on("change", function(cm, change) { - removedText = [change.removed, change.next && change.next.removed]; + removedText.push(change.removed); }); cm.operation(function() { @@ -1613,14 +1613,19 @@ testCM("change_removedText", function(cm) { cm.replaceRange("123", Pos(0,0)); }); + eq(removedText.length, 2); eq(removedText[0].join("\n"), "abc\nd"); eq(removedText[1].join("\n"), ""); + var removedText = []; cm.undo(); + eq(removedText.length, 2); eq(removedText[0].join("\n"), "123"); eq(removedText[1].join("\n"), "xyz"); + var removedText = []; cm.redo(); + eq(removedText.length, 2); eq(removedText[0].join("\n"), "abc\nd"); eq(removedText[1].join("\n"), ""); }); From 451c9a83caf1e5f9b1bc2736bf1a1432195df92e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 16:27:48 +0100 Subject: [PATCH 026/195] Add a v4 upgrade guide --- doc/upgrade_v4.html | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 doc/upgrade_v4.html diff --git a/doc/upgrade_v4.html b/doc/upgrade_v4.html new file mode 100644 index 0000000000..64a838ca26 --- /dev/null +++ b/doc/upgrade_v4.html @@ -0,0 +1,81 @@ + + +CodeMirror: Version 4 upgrade guide + + + + + + +
+ +

Upgrading to version 4

+ +

Note: Version 4 hasn't been released yet. The +information here is provisional and might still change.

+ +

CodeMirror 4's interface is very close version 3, but it +does fix a few awkward details in a backwards-incompatible ways. At +least skim the text below before upgrading.

+ +

Multiple selections

+ +

The main new feature in version 4 is multiple selections. The +single-selection variants of methods are still there, but now +typically act only on the primary selection (usually the last +one added).

+ +

The exception to this +is getSelection, +which will now return the content of all selections +(separated by newlines, or whatever lineSep parameter you passed +it).

+ +
+ +

The beforeSelectionChange event

+ +

This event still exists, but the object it is passed has +a completely new +interface, because such changes now concern multiple +selections.

+ +
+ +

replaceSelection's collapsing behavior

+ +

By +default, replaceSelection +would leave the newly inserted text selected. This is only rarely what +you want, and also (slightly) more expensive in the new model, so the +default was changed to "end", meaning the old behavior +must be explicitly specified by passing a second argument +of "around".

+ +
+ +

change event data

+ +

Rather than forcing client code to follow next +pointers from one change object to the next, the library will now +simply fire +multiple "change" +events. Existing code will probably continue to work unmodified.

+ +
+
From 38073cd5ffadd6c19eaac8b46e3094c5238358ee Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 22:42:09 +0100 Subject: [PATCH 027/195] Wrap all modules in AMD and CommonJS-supporting shims --- addon/comment/comment.js | 11 ++- addon/comment/continuecomment.js | 11 ++- addon/dialog/dialog.js | 11 ++- addon/display/fullscreen.js | 11 ++- addon/display/placeholder.js | 11 ++- addon/edit/closebrackets.js | 11 ++- addon/edit/closetag.js | 11 ++- addon/edit/continuelist.js | 11 ++- addon/edit/matchbrackets.js | 11 ++- addon/edit/matchtags.js | 11 ++- addon/edit/trailingspace.js | 37 +++++---- addon/fold/brace-fold.js | 15 +++- addon/fold/comment-fold.js | 12 +++ addon/fold/foldcode.js | 11 ++- addon/fold/foldgutter.js | 11 ++- addon/fold/indent-fold.js | 13 ++- addon/fold/xml-fold.js | 13 ++- addon/hint/anyword-hint.js | 11 ++- addon/hint/css-hint.js | 11 ++- addon/hint/html-hint.js | 14 +++- addon/hint/javascript-hint.js | 13 ++- addon/hint/pig-hint.js | 12 ++- addon/hint/python-hint.js | 14 +++- addon/hint/show-hint.js | 11 ++- addon/hint/sql-hint.js | 11 ++- addon/hint/xml-hint.js | 12 ++- addon/lint/coffeescript-lint.js | 13 ++- addon/lint/css-lint.js | 12 +++ addon/lint/javascript-lint.js | 12 ++- addon/lint/json-lint.js | 13 ++- addon/lint/lint.js | 18 +++-- addon/merge/merge.js | 11 ++- addon/mode/loadmode.js | 11 ++- addon/mode/multiplex.js | 12 +++ addon/mode/overlay.js | 15 +++- addon/runmode/colorize.js | 14 +++- addon/runmode/runmode-standalone.js | 2 - addon/runmode/runmode.js | 12 +++ addon/scroll/scrollpastend.js | 11 ++- addon/search/match-highlighter.js | 13 ++- addon/search/search.js | 11 ++- addon/search/searchcursor.js | 12 ++- addon/selection/active-line.js | 11 ++- addon/selection/mark-selection.js | 11 ++- addon/tern/tern.js | 11 ++- addon/wrap/hardwrap.js | 11 ++- demo/btree.html | 1 - doc/compress.html | 3 +- doc/manual.html | 39 ++++++++- doc/upgrade_v4.html | 16 ++++ keymap/emacs.js | 11 ++- keymap/multiselect.js | 11 ++- keymap/vim.js | 12 ++- lib/codemirror.js | 13 ++- mode/apl/apl.js | 12 +++ mode/asterisk/asterisk.js | 12 +++ mode/clike/clike.js | 12 +++ mode/clojure/clojure.js | 13 +++ mode/cobol/cobol.js | 12 +++ mode/coffeescript/coffeescript.js | 12 +++ mode/commonlisp/commonlisp.js | 12 +++ mode/css/css.js | 16 +++- mode/d/d.js | 14 +++- mode/diff/diff.js | 12 +++ mode/dtd/dtd.js | 12 +++ mode/ecl/ecl.js | 12 +++ mode/eiffel/eiffel.js | 12 +++ mode/erlang/erlang.js | 12 +++ mode/fortran/fortran.js | 12 +++ mode/gas/gas.js | 12 +++ mode/gfm/gfm.js | 12 +++ mode/gherkin/gherkin.js | 12 +++ mode/go/go.js | 12 +++ mode/groovy/groovy.js | 12 +++ mode/haml/haml.js | 12 +++ mode/haskell/haskell.js | 12 +++ mode/haxe/haxe.js | 12 +++ mode/htmlembedded/htmlembedded.js | 12 +++ mode/htmlmixed/htmlmixed.js | 12 +++ mode/http/http.js | 12 +++ mode/index.html | 2 +- mode/jade/jade.js | 12 +++ mode/javascript/javascript.js | 12 +++ mode/jinja2/jinja2.js | 67 ++++++++------- mode/julia/julia.js | 12 +++ mode/less/less.js | 12 +++ mode/livescript/livescript.js | 13 +++ mode/lua/lua.js | 12 +++ mode/markdown/markdown.js | 12 +++ mode/mirc/mirc.js | 13 +++ mode/mllike/mllike.js | 13 ++- mode/nginx/nginx.js | 12 +++ mode/ntriples/ntriples.js | 13 +++ mode/octave/octave.js | 12 +++ mode/pascal/pascal.js | 12 +++ mode/pegjs/pegjs.js | 12 +++ mode/perl/perl.js | 157 +++++++++++++++++++----------------- mode/php/php.js | 13 ++- mode/pig/pig.js | 12 +++ mode/properties/properties.js | 12 +++ mode/python/python.js | 32 +++++--- mode/q/q.js | 12 +++ mode/r/r.js | 12 +++ mode/rpm/changes/changes.js | 19 ----- mode/rpm/changes/index.html | 67 --------------- mode/rpm/{spec => }/index.html | 68 ++++++++++++---- mode/rpm/{spec/spec.js => rpm.js} | 36 ++++++++- mode/rpm/spec/spec.css | 5 -- mode/rst/rst.js | 12 +++ mode/ruby/ruby.js | 11 +++ mode/rust/rust.js | 12 +++ mode/sass/sass.js | 12 +++ mode/scheme/scheme.js | 13 +++ mode/shell/shell.js | 12 +++ mode/sieve/sieve.js | 15 +++- mode/smalltalk/smalltalk.js | 12 +++ mode/smarty/smarty.js | 13 +++ mode/smartymixed/smartymixed.js | 16 +++- mode/sparql/sparql.js | 12 +++ mode/sql/sql.js | 12 +++ mode/stex/stex.js | 12 +++ mode/tcl/tcl.js | 13 +++ mode/tiddlywiki/tiddlywiki.js | 13 +++ mode/tiki/tiki.js | 12 +++ mode/toml/toml.js | 12 +++ mode/turtle/turtle.js | 12 +++ mode/vb/vb.js | 12 +++ mode/vbscript/vbscript.js | 13 +++ mode/velocity/velocity.js | 12 +++ mode/verilog/verilog.js | 14 +++- mode/xml/xml.js | 12 +++ mode/xquery/xquery.js | 12 +++ mode/yaml/yaml.js | 12 +++ mode/z80/z80.js | 12 +++ 134 files changed, 1640 insertions(+), 363 deletions(-) delete mode 100644 mode/rpm/changes/changes.js delete mode 100644 mode/rpm/changes/index.html rename mode/rpm/{spec => }/index.html (60%) rename mode/rpm/{spec/spec.js => rpm.js} (70%) delete mode 100644 mode/rpm/spec/spec.css diff --git a/addon/comment/comment.js b/addon/comment/comment.js index c36e176912..1eb9a05c5d 100644 --- a/addon/comment/comment.js +++ b/addon/comment/comment.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var noOptions = {}; @@ -159,4 +166,4 @@ }); return true; }); -})(); +}); diff --git a/addon/comment/continuecomment.js b/addon/comment/continuecomment.js index 0566c74e43..6db3b0a2b9 100644 --- a/addon/comment/continuecomment.js +++ b/addon/comment/continuecomment.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { var modes = ["clike", "css", "javascript"]; for (var i = 0; i < modes.length; ++i) CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); @@ -59,4 +66,4 @@ cm.addKeyMap(map); } }); -})(); +}); diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js index 41d7bf8663..2c26542045 100644 --- a/addon/dialog/dialog.js +++ b/addon/dialog/dialog.js @@ -1,6 +1,13 @@ // Open simple dialogs on top of an editor. Relies on dialog.css. -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { function dialogDiv(cm, template, bottom) { var wrap = cm.getWrapperElement(); var dialog; @@ -119,4 +126,4 @@ if (duration) doneTimer = setTimeout(close, options.duration); }); -})(); +}); diff --git a/addon/display/fullscreen.js b/addon/display/fullscreen.js index a442f6a433..e39c6e162f 100644 --- a/addon/display/fullscreen.js +++ b/addon/display/fullscreen.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { @@ -28,4 +35,4 @@ window.scrollTo(info.scrollLeft, info.scrollTop); cm.refresh(); } -})(); +}); diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js index 748afe7275..0fdc9b0d5b 100644 --- a/addon/display/placeholder.js +++ b/addon/display/placeholder.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { CodeMirror.defineOption("placeholder", "", function(cm, val, old) { var prev = old && old != CodeMirror.Init; if (val && !prev) { @@ -45,4 +52,4 @@ function isEmpty(cm) { return (cm.lineCount() === 1) && (cm.getLine(0) === ""); } -})(); +}); diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js index 64c56f9530..84b2fc56f1 100644 --- a/addon/edit/closebrackets.js +++ b/addon/edit/closebrackets.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { var DEFAULT_BRACKETS = "()[]{}''\"\""; var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; var SPACE_CHAR_REGEX = /\s/; @@ -112,4 +119,4 @@ }); }; } -})(); +}); diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index 7047e27738..ac6c2fc6d4 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -22,7 +22,14 @@ * See demos/closetag.html for a usage example. */ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../fold/xml-fold")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../fold/xml-fold"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { if (old != CodeMirror.Init && old) cm.removeKeyMap("autoCloseTags"); @@ -106,4 +113,4 @@ if (collection[i] == elt) return i; return -1; } -})(); +}); diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js index a038345b4b..54c7340bbe 100644 --- a/addon/edit/continuelist.js +++ b/addon/edit/continuelist.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var listRE = /^(\s*)([*+-]|(\d+)\.)(\s*)/, @@ -25,4 +32,4 @@ cm.replaceSelections(replacements, null, "+insert"); }; -}()); +}); diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js index 4d0c853a52..dcdee7516b 100644 --- a/addon/edit/matchbrackets.js +++ b/addon/edit/matchbrackets.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && (document.documentMode == null || document.documentMode < 8); @@ -93,4 +100,4 @@ CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){ return findMatchingBracket(this, pos, strict); }); -})(); +}); diff --git a/addon/edit/matchtags.js b/addon/edit/matchtags.js index f189c1f8ef..2b7d22ed84 100644 --- a/addon/edit/matchtags.js +++ b/addon/edit/matchtags.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../fold/xml-fold")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../fold/xml-fold"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; CodeMirror.defineOption("matchTags", false, function(cm, val, old) { @@ -53,4 +60,4 @@ if (other) cm.setSelection(other.to, other.from); } }; -})(); +}); diff --git a/addon/edit/trailingspace.js b/addon/edit/trailingspace.js index f6bb02645d..ec07221e30 100644 --- a/addon/edit/trailingspace.js +++ b/addon/edit/trailingspace.js @@ -1,15 +1,24 @@ -CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { - if (prev == CodeMirror.Init) prev = false; - if (prev && !val) - cm.removeOverlay("trailingspace"); - else if (!prev && val) - cm.addOverlay({ - token: function(stream) { - for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} - if (i > stream.pos) { stream.pos = i; return null; } - stream.pos = l; - return "trailingspace"; - }, - name: "trailingspace" - }); +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { + if (prev == CodeMirror.Init) prev = false; + if (prev && !val) + cm.removeOverlay("trailingspace"); + else if (!prev && val) + cm.addOverlay({ + token: function(stream) { + for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} + if (i > stream.pos) { stream.pos = i; return null; } + stream.pos = l; + return "trailingspace"; + }, + name: "trailingspace" + }); + }); }); diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js index 2560b2b94c..f0ee62029a 100644 --- a/addon/fold/brace-fold.js +++ b/addon/fold/brace-fold.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.registerHelper("fold", "brace", function(cm, start) { var line = start.line, lineText = cm.getLine(line); var startCh, tokenType; @@ -45,7 +55,6 @@ CodeMirror.registerHelper("fold", "brace", function(cm, start) { return {from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh)}; }); -CodeMirror.braceRangeFinder = CodeMirror.fold.brace; // deprecated CodeMirror.registerHelper("fold", "import", function(cm, start) { function hasImport(line) { @@ -70,7 +79,6 @@ CodeMirror.registerHelper("fold", "import", function(cm, start) { } return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end}; }); -CodeMirror.importRangeFinder = CodeMirror.fold["import"]; // deprecated CodeMirror.registerHelper("fold", "include", function(cm, start) { function hasInclude(line) { @@ -90,4 +98,5 @@ CodeMirror.registerHelper("fold", "include", function(cm, start) { return {from: CodeMirror.Pos(start, has + 1), to: cm.clipPos(CodeMirror.Pos(end))}; }); -CodeMirror.includeRangeFinder = CodeMirror.fold.include; // deprecated + +}); diff --git a/addon/fold/comment-fold.js b/addon/fold/comment-fold.js index 26e72f09f5..d72c5479a7 100644 --- a/addon/fold/comment-fold.js +++ b/addon/fold/comment-fold.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { return mode.blockCommentStart && mode.blockCommentEnd; }, function(cm, start) { @@ -40,3 +50,5 @@ CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { return {from: CodeMirror.Pos(line, startCh), to: CodeMirror.Pos(end, endCh)}; }); + +}); diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 5c00d7093c..4627c81f9d 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; function doFold(cm, pos, options, force) { @@ -83,4 +90,4 @@ if (cur) return cur; } }); -})(); +}); diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index a4f3bb3186..9caba59aad 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("./foldcode")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "./foldcode"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; CodeMirror.defineOption("foldGutter", false, function(cm, val, old) { @@ -121,4 +128,4 @@ if (line >= state.from && line < state.to) updateFoldInfo(cm, line, line + 1); } -})(); +}); diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js index 434c2bc5ca..d0130836a2 100644 --- a/addon/fold/indent-fold.js +++ b/addon/fold/indent-fold.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.registerHelper("fold", "indent", function(cm, start) { var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); if (!/\S/.test(firstLine)) return; @@ -27,4 +37,5 @@ CodeMirror.registerHelper("fold", "indent", function(cm, start) { to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) }; }); -CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated + +}); diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js index db5aed7041..d554e2fc42 100644 --- a/addon/fold/xml-fold.js +++ b/addon/fold/xml-fold.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var Pos = CodeMirror.Pos; @@ -136,8 +143,6 @@ } } }); - CodeMirror.tagRangeFinder = CodeMirror.fold.xml; // deprecated - CodeMirror.findMatchingTag = function(cm, pos, range) { var iter = new Iter(cm, pos.line, pos.ch, range); if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return; @@ -170,4 +175,4 @@ var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); return !!findMatchingClose(iter, name); }; -})(); +}); diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js index a144768c81..1570664c4f 100644 --- a/addon/hint/anyword-hint.js +++ b/addon/hint/anyword-hint.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var WORD = /[\w$]+/, RANGE = 500; @@ -29,4 +36,4 @@ } return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; }); -})(); +}); diff --git a/addon/hint/css-hint.js b/addon/hint/css-hint.js index 6789c458ba..9e5b1e132d 100644 --- a/addon/hint/css-hint.js +++ b/addon/hint/css-hint.js @@ -1,4 +1,11 @@ -(function () { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../../mode/css/css")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../../mode/css/css"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, @@ -43,4 +50,4 @@ to: CodeMirror.Pos(cur.line, end) }; }); -})(); +}); diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js index cf256851ef..cbe7c61ad4 100755 --- a/addon/hint/html-hint.js +++ b/addon/hint/html-hint.js @@ -1,4 +1,13 @@ -(function () { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" "); var targets = ["_blank", "_self", "_top", "_parent"]; var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"]; @@ -332,6 +341,5 @@ if (options) for (var opt in options) local[opt] = options[opt]; return CodeMirror.hint.xml(cm, local); } - CodeMirror.htmlHint = htmlHint; // deprecated CodeMirror.registerHelper("hint", "html", htmlHint); -})(); +}); diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js index c347c206ec..305bb85a29 100644 --- a/addon/hint/javascript-hint.js +++ b/addon/hint/javascript-hint.js @@ -1,4 +1,11 @@ -(function () { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { var Pos = CodeMirror.Pos; function forEach(arr, f) { @@ -47,7 +54,6 @@ function (e, cur) {return e.getTokenAt(cur);}, options); }; - CodeMirror.javascriptHint = javascriptHint; // deprecated CodeMirror.registerHelper("hint", "javascript", javascriptHint); function getCoffeeScriptToken(editor, cur) { @@ -71,7 +77,6 @@ function coffeescriptHint(editor, options) { return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options); } - CodeMirror.coffeescriptHint = coffeescriptHint; // deprecated CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint); var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + @@ -128,4 +133,4 @@ } return found; } -})(); +}); diff --git a/addon/hint/pig-hint.js b/addon/hint/pig-hint.js index 6c0c523774..02342f0246 100644 --- a/addon/hint/pig-hint.js +++ b/addon/hint/pig-hint.js @@ -1,4 +1,11 @@ -(function () { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; function forEach(arr, f) { @@ -46,7 +53,6 @@ function pigHint(editor) { return scriptHint(editor, pigKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); } - CodeMirror.pigHint = pigHint; // deprecated CodeMirror.registerHelper("hint", "pig", pigHint); var pigKeywords = "VOID IMPORT RETURNS DEFINE LOAD FILTER FOREACH ORDER CUBE DISTINCT COGROUP " @@ -118,4 +124,4 @@ } return found; } -})(); +}); diff --git a/addon/hint/python-hint.js b/addon/hint/python-hint.js index 248284e8c5..eebfcc76dc 100644 --- a/addon/hint/python-hint.js +++ b/addon/hint/python-hint.js @@ -1,4 +1,13 @@ -(function () { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + function forEach(arr, f) { for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); } @@ -40,7 +49,6 @@ function pythonHint(editor) { return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);}); } - CodeMirror.pythonHint = pythonHint; // deprecated CodeMirror.registerHelper("hint", "python", pythonHint); var pythonKeywords = "and del from not while as elif global or with assert else if pass yield" @@ -88,4 +96,4 @@ } return found; } -})(); +}); diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js index 831fe089da..6a4d74e360 100644 --- a/addon/hint/show-hint.js +++ b/addon/hint/show-hint.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var HINT_ELEMENT_CLASS = "CodeMirror-hint"; @@ -332,4 +339,4 @@ }); CodeMirror.commands.autocomplete = CodeMirror.showHint; -})(); +}); diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js index d00781cff9..736dbaea06 100644 --- a/addon/hint/sql-hint.js +++ b/addon/hint/sql-hint.js @@ -1,4 +1,11 @@ -(function () { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../../mode/sql/sql")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../../mode/sql/sql"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var tables; @@ -143,4 +150,4 @@ }; } CodeMirror.registerHelper("hint", "sql", sqlHint); -})(); +}); diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js index 69f2b771fe..857564909a 100644 --- a/addon/hint/xml-hint.js +++ b/addon/hint/xml-hint.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var Pos = CodeMirror.Pos; @@ -64,6 +71,5 @@ }; } - CodeMirror.xmlHint = getHints; // deprecated CodeMirror.registerHelper("hint", "xml", getHints); -})(); +}); diff --git a/addon/lint/coffeescript-lint.js b/addon/lint/coffeescript-lint.js index 7f55a294dd..6df17f8f84 100644 --- a/addon/lint/coffeescript-lint.js +++ b/addon/lint/coffeescript-lint.js @@ -2,6 +2,16 @@ // declare global: coffeelint +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.registerHelper("lint", "coffeescript", function(text) { var found = []; var parseError = function(err) { @@ -24,4 +34,5 @@ CodeMirror.registerHelper("lint", "coffeescript", function(text) { } return found; }); -CodeMirror.coffeeValidator = CodeMirror.lint.coffeescript; // deprecated + +}); diff --git a/addon/lint/css-lint.js b/addon/lint/css-lint.js index 1de71fbcbd..2a799ddc4f 100644 --- a/addon/lint/css-lint.js +++ b/addon/lint/css-lint.js @@ -2,6 +2,16 @@ // declare global: CSSLint +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.registerHelper("lint", "css", function(text) { var found = []; var results = CSSLint.verify(text), messages = results.messages, message = null; @@ -17,3 +27,5 @@ CodeMirror.registerHelper("lint", "css", function(text) { } return found; }); + +}); diff --git a/addon/lint/javascript-lint.js b/addon/lint/javascript-lint.js index 7123ab7e64..bbb51083b8 100644 --- a/addon/lint/javascript-lint.js +++ b/addon/lint/javascript-lint.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; // declare global: JSHINT @@ -19,7 +26,6 @@ } CodeMirror.registerHelper("lint", "javascript", validator); - CodeMirror.javascriptValidator = CodeMirror.lint.javascript; // deprecated function cleanup(error) { // All problems are warnings by default @@ -123,4 +129,4 @@ } } } -})(); +}); diff --git a/addon/lint/json-lint.js b/addon/lint/json-lint.js index e10e11bcaf..1f5f82d0ca 100644 --- a/addon/lint/json-lint.js +++ b/addon/lint/json-lint.js @@ -2,6 +2,16 @@ // declare global: jsonlint +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.registerHelper("lint", "json", function(text) { var found = []; jsonlint.parseError = function(str, hash) { @@ -14,4 +24,5 @@ CodeMirror.registerHelper("lint", "json", function(text) { catch(e) {} return found; }); -CodeMirror.jsonValidator = CodeMirror.lint.json; // deprecated + +}); diff --git a/addon/lint/lint.js b/addon/lint/lint.js index 6121735d3f..db5162ee7a 100644 --- a/addon/lint/lint.js +++ b/addon/lint/lint.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; var SEVERITIES = /^(?:error|warning)$/; @@ -178,7 +185,7 @@ } } - function optionHandler(cm, val, old) { + CodeMirror.defineOption("lint", false, function(cm, val, old) { if (old && old != CodeMirror.Init) { clearMarks(cm); cm.off("change", onChange); @@ -196,8 +203,5 @@ startLinting(cm); } - } - - CodeMirror.defineOption("lintWith", false, optionHandler); // deprecated - CodeMirror.defineOption("lint", false, optionHandler); // deprecated -})(); + }); +}); diff --git a/addon/merge/merge.js b/addon/merge/merge.js index 89f852652b..99e5d075ba 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; // declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL @@ -476,4 +483,4 @@ function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; } function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; } function posEq(a, b) { return a.line == b.line && a.ch == b.ch; } -})(); +}); diff --git a/addon/mode/loadmode.js b/addon/mode/loadmode.js index 60fafbb178..e08c281321 100644 --- a/addon/mode/loadmode.js +++ b/addon/mode/loadmode.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { if (!CodeMirror.modeURL) CodeMirror.modeURL = "../mode/%N/%N.js"; var loading = {}; @@ -48,4 +55,4 @@ instance.setOption("mode", instance.getOption("mode")); }); }; -}()); +}); diff --git a/addon/mode/multiplex.js b/addon/mode/multiplex.js index 614ab1add3..07385c35f2 100644 --- a/addon/mode/multiplex.js +++ b/addon/mode/multiplex.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.multiplexingMode = function(outer /*, others */) { // Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects var others = Array.prototype.slice.call(arguments, 1); @@ -101,3 +111,5 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { } }; }; + +}); diff --git a/addon/mode/overlay.js b/addon/mode/overlay.js index b7928a7bbf..6f556a13a7 100644 --- a/addon/mode/overlay.js +++ b/addon/mode/overlay.js @@ -6,8 +6,17 @@ // overlay wins, unless the combine argument was true, in which case // the styles are combined. -// overlayParser is the old, deprecated name -CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, combine) { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.overlayMode = function(base, overlay, combine) { return { startState: function() { return { @@ -57,3 +66,5 @@ CodeMirror.overlayMode = CodeMirror.overlayParser = function(base, overlay, comb } }; }; + +}); diff --git a/addon/runmode/colorize.js b/addon/runmode/colorize.js index 62286d21e4..0f9530b17b 100644 --- a/addon/runmode/colorize.js +++ b/addon/runmode/colorize.js @@ -1,4 +1,12 @@ -CodeMirror.colorize = (function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("./runmode")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "./runmode"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/; @@ -10,7 +18,7 @@ CodeMirror.colorize = (function() { } } - return function(collection, defaultMode) { + CodeMirror.colorize = function(collection, defaultMode) { if (!collection) collection = document.body.getElementsByTagName("pre"); for (var i = 0; i < collection.length; ++i) { @@ -26,4 +34,4 @@ CodeMirror.colorize = (function() { node.className += " cm-s-default"; } }; -})(); +}); diff --git a/addon/runmode/runmode-standalone.js b/addon/runmode/runmode-standalone.js index ec06ba11cb..eaa2b8f2fb 100644 --- a/addon/runmode/runmode-standalone.js +++ b/addon/runmode/runmode-standalone.js @@ -1,5 +1,3 @@ -/* Just enough of CodeMirror to run runMode under node.js */ - window.CodeMirror = {}; (function() { diff --git a/addon/runmode/runmode.js b/addon/runmode/runmode.js index 2cafa811f8..351840e08e 100644 --- a/addon/runmode/runmode.js +++ b/addon/runmode/runmode.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.runMode = function(string, modespec, callback, options) { var mode = CodeMirror.getMode(CodeMirror.defaults, modespec); var ie = /MSIE \d/.test(navigator.userAgent); @@ -54,3 +64,5 @@ CodeMirror.runMode = function(string, modespec, callback, options) { } } }; + +}); diff --git a/addon/scroll/scrollpastend.js b/addon/scroll/scrollpastend.js index 58720ad413..467b7aa1cf 100644 --- a/addon/scroll/scrollpastend.js +++ b/addon/scroll/scrollpastend.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) { @@ -33,4 +40,4 @@ cm.setSize(); } } -})(); +}); diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js index a2eda2e614..d9c818b838 100644 --- a/addon/search/match-highlighter.js +++ b/addon/search/match-highlighter.js @@ -12,7 +12,16 @@ // actual CSS class name. showToken, when enabled, will cause the // current token to be highlighted when nothing is selected. -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + var DEFAULT_MIN_CHARS = 2; var DEFAULT_TOKEN_STYLE = "matchhighlight"; var DEFAULT_DELAY = 100; @@ -88,4 +97,4 @@ stream.skipTo(query.charAt(0)) || stream.skipToEnd(); }}; } -})(); +}); diff --git a/addon/search/search.js b/addon/search/search.js index 38b488522f..3df006d4b7 100644 --- a/addon/search/search.js +++ b/addon/search/search.js @@ -6,7 +6,14 @@ // replace by making sure the match is no longer selected when hitting // Ctrl-G. -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; function searchOverlay(query, caseInsensitive) { var startChar; @@ -139,4 +146,4 @@ CodeMirror.commands.clearSearch = clearSearch; CodeMirror.commands.replace = replace; CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; -})(); +}); diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js index 6afac80fb8..899f44c4ab 100644 --- a/addon/search/searchcursor.js +++ b/addon/search/searchcursor.js @@ -1,4 +1,12 @@ -(function(){ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; var Pos = CodeMirror.Pos; function SearchCursor(doc, query, pos, caseFold) { @@ -175,4 +183,4 @@ if (ranges.length) this.setSelections(ranges, 0); }); -})(); +}); diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js index c8b7001ced..a818f109b6 100644 --- a/addon/selection/active-line.js +++ b/addon/selection/active-line.js @@ -4,7 +4,14 @@ // active line's wrapping
the CSS class "CodeMirror-activeline", // and gives its background
the class "CodeMirror-activeline-background". -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var WRAP_CLASS = "CodeMirror-activeline"; var BACK_CLASS = "CodeMirror-activeline-background"; @@ -56,4 +63,4 @@ function selectionChange(cm, sel) { updateActiveLines(cm, sel.ranges); } -})(); +}); diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js index c40a9c99bd..ae0d393143 100644 --- a/addon/selection/mark-selection.js +++ b/addon/selection/mark-selection.js @@ -4,7 +4,14 @@ // selected text the CSS class given as option value, or // "CodeMirror-selectedtext" when the value is not a string. -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) { @@ -105,4 +112,4 @@ } } } -})(); +}); diff --git a/addon/tern/tern.js b/addon/tern/tern.js index fa2a2c3528..1cc7e972e5 100644 --- a/addon/tern/tern.js +++ b/addon/tern/tern.js @@ -40,7 +40,14 @@ // load. Or, if you minified those into a single script and included // them in the workerScript, simply leave this undefined. -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; // declare global: tern @@ -650,4 +657,4 @@ this.delFile = function(name) { send({type: "del", name: name}); }; this.request = function(body, c) { send({type: "req", body: body}, c); }; } -})(); +}); diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js index f6d99212a8..f900d6e81b 100644 --- a/addon/wrap/hardwrap.js +++ b/addon/wrap/hardwrap.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var Pos = CodeMirror.Pos; @@ -108,4 +115,4 @@ }); return madeChange; }); -})(); +}); diff --git a/demo/btree.html b/demo/btree.html index 7635ca1bb4..b7bd39088d 100644 --- a/demo/btree.html +++ b/demo/btree.html @@ -58,7 +58,6 @@ var gray = Math.min(line.text.length * 3, 230), col = gray.toString(16); if (col.length == 1) col = "0" + col; lineElt.style.background = "#" + col + col + col; - console.log(line.height, line); lineElt.style.width = Math.max(Math.round(line.height / 3), 1) + "px"; } } else { diff --git a/doc/compress.html b/doc/compress.html index 55e25f3e68..d4c108c516 100644 --- a/doc/compress.html +++ b/doc/compress.html @@ -128,8 +128,7 @@ - - + diff --git a/doc/manual.html b/doc/manual.html index 3e81b94c3a..9a2424eb48 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -91,6 +91,9 @@ <link rel="stylesheet" href="../lib/codemirror.css"> <script src="mode/javascript/javascript.js"></script> +

(Alternatively, use a module loader. More + about that later.)

+

Having done this, an editor instance can be created like this:

@@ -135,7 +138,41 @@ of a form) is submitted. See the API reference for a full description of this method.

+

Module loaders

+ +

The files in the CodeMirror distribution contain shims for + loading them (and their dependencies) in AMD or CommonJS + environments. If the variables exports + and module exist and have type object, CommonJS-style + require will be used. If not, but there is a + function define with an amd property + present, AMD-style (RequireJS) will be used.

+ +

It is possible to + use Browserify or similar + tools to statically build modules using CodeMirror. Alternatively, + use RequireJS to dynamically + load dependencies at runtime. Both of these approaches have the + advantage that they don't use the global namespace and can, thus, + do things like load multiple versions of CodeMirror alongside each + other.

+ +

Here's a simple example of using RequireJS to load CodeMirror:

+ +
require([
+  "cm/lib/codemirror", "cm/mode/htmlmixed/htmlmixed"
+], function(CodeMirror) {
+  CodeMirror.fromTextArea(document.getElementById("code"), {
+    lineNumbers: true,
+    mode: "htmlmixed"
+  });
+});
+ +

It will automatically load the modes that the mixed HTML mode + depends on (XML, JavaScript, and CSS).

+ +

Configuration

@@ -2168,7 +2205,7 @@

Static properties

"close" ()
Fired when the completion is finished.
- This addon depends styles + This addon depends on styles from addon/hint/show-hint.css. Check out the demo for an example. diff --git a/doc/upgrade_v4.html b/doc/upgrade_v4.html index 64a838ca26..d390228682 100644 --- a/doc/upgrade_v4.html +++ b/doc/upgrade_v4.html @@ -19,6 +19,7 @@
  • The beforeSelectionChange event
  • replaceSelection and collapsing
  • change event data +
  • Deprecated interfaces dropped @@ -78,4 +79,19 @@

    Upgrading to version 4

    events. Existing code will probably continue to work unmodified.

    + +

    Deprecated interfaces dropped

    + +

    A few properties that have been deprecated for a while are now +gone. Most notably, the names for folding and completing functions +(CodeMirror.braceRangeFinder, CodeMirror.javascriptHint, +etc) are now gone +(use CodeMirror.fold.brace, CodeMirror.hint.javascript).

    + +

    The className property in the return value +of getTokenAt, which +has been superseded by the type property, is also no +longer present.

    + +
  • diff --git a/keymap/emacs.js b/keymap/emacs.js index c56d7eb7e7..35f199cb73 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -1,4 +1,11 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var Pos = CodeMirror.Pos; @@ -395,4 +402,4 @@ } for (var i = 0; i < 10; ++i) regPrefix(String(i)); regPrefix("-"); -})(); +}); diff --git a/keymap/multiselect.js b/keymap/multiselect.js index f45f27254a..46379484a6 100644 --- a/keymap/multiselect.js +++ b/keymap/multiselect.js @@ -1,7 +1,14 @@ // A number of extra keybindings for multiple-selection handling // Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { "use strict"; var pc = CodeMirror.keyMap.pcDefault, mac = CodeMirror.keyMap.macDefault; @@ -44,4 +51,4 @@ cm.selectMatches(query); }); }; -})(); +}); diff --git a/keymap/vim.js b/keymap/vim.js index 9e5b257f4f..720ef4a2c3 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -56,7 +56,14 @@ * 8. Set up Vim to work as a keymap for CodeMirror. */ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { 'use strict'; var defaultKeymap = [ @@ -3800,5 +3807,4 @@ }; // Initialize Vim and make it available as an API. CodeMirror.Vim = Vim(); -} -)(); +}); diff --git a/lib/codemirror.js b/lib/codemirror.js index c5a332264f..c4adf99f65 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1,5 +1,11 @@ -// CodeMirror is the only global var we claim -window.CodeMirror = (function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + module.exports = mod(); + else if (typeof define == "function" && define.amd) // AMD + return define([], mod); + else // Plain browser env + this.CodeMirror = mod(); +})(function() { "use strict"; // BROWSER SNIFFING @@ -3175,7 +3181,6 @@ window.CodeMirror = (function() { return {start: stream.start, end: stream.pos, string: stream.current(), - className: style || null, // Deprecated, use 'type' instead type: style || null, state: state}; }, @@ -6363,4 +6368,4 @@ window.CodeMirror = (function() { CodeMirror.version = "4.0.0"; return CodeMirror; -})(); +}); diff --git a/mode/apl/apl.js b/mode/apl/apl.js index 5c23af85da..2ba74c18c2 100644 --- a/mode/apl/apl.js +++ b/mode/apl/apl.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("apl", function() { var builtInOps = { ".": "innerProduct", @@ -158,3 +168,5 @@ CodeMirror.defineMode("apl", function() { }); CodeMirror.defineMIME("text/apl", "apl"); + +}); diff --git a/mode/asterisk/asterisk.js b/mode/asterisk/asterisk.js index 60b689d1d5..c56fc0b38a 100644 --- a/mode/asterisk/asterisk.js +++ b/mode/asterisk/asterisk.js @@ -14,6 +14,16 @@ * ===================================================================================== */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("asterisk", function() { var atoms = ["exten", "same", "include","ignorepat","switch"], dpcmd = ["#include","#exec"], @@ -181,3 +191,5 @@ CodeMirror.defineMode("asterisk", function() { }); CodeMirror.defineMIME("text/x-asterisk", "asterisk"); + +}); diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 4e4988dd81..b869f72723 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("clike", function(config, parserConfig) { var indentUnit = config.indentUnit, statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, @@ -379,3 +389,5 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { modeProps: {fold: ["brace", "include"]} }); }()); + +}); diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js index e3d87d23a6..dd2e2ce1b8 100644 --- a/mode/clojure/clojure.js +++ b/mode/clojure/clojure.js @@ -2,6 +2,17 @@ * Author: Hans Engel * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("clojure", function (options) { var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2", ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword"; @@ -223,3 +234,5 @@ CodeMirror.defineMode("clojure", function (options) { }); CodeMirror.defineMIME("text/x-clojure", "clojure"); + +}); diff --git a/mode/cobol/cobol.js b/mode/cobol/cobol.js index d92491dde8..e66e42a191 100644 --- a/mode/cobol/cobol.js +++ b/mode/cobol/cobol.js @@ -2,6 +2,16 @@ * Author: Gautam Mehta * Branched from CodeMirror's Scheme mode */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("cobol", function () { var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", ATOM = "atom", NUMBER = "number", KEYWORD = "keyword", MODTAG = "header", @@ -238,3 +248,5 @@ CodeMirror.defineMode("cobol", function () { }); CodeMirror.defineMIME("text/x-cobol", "cobol"); + +}); diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js index 0e9e6352f8..c6da8f2a29 100644 --- a/mode/coffeescript/coffeescript.js +++ b/mode/coffeescript/coffeescript.js @@ -2,6 +2,16 @@ * Link to the project's GitHub page: * https://github.com/pickhardt/coffeescript-codemirror-mode */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("coffeescript", function(conf) { var ERRORCLASS = "error"; @@ -351,3 +361,5 @@ CodeMirror.defineMode("coffeescript", function(conf) { }); CodeMirror.defineMIME("text/x-coffeescript", "coffeescript"); + +}); diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js index 8fa08c8ac2..a0f0732bf6 100644 --- a/mode/commonlisp/commonlisp.js +++ b/mode/commonlisp/commonlisp.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("commonlisp", function (config) { var assumeBody = /^with|^def|^do|^prog|case$|^cond$|bind$|when$|unless$/; var numLiteral = /^(?:[+\-]?(?:\d+|\d*\.\d+)(?:[efd][+\-]?\d+)?|[+\-]?\d+(?:\/[+\-]?\d+)?|#b[+\-]?[01]+|#o[+\-]?[0-7]+|#x[+\-]?[\da-f]+)/; @@ -103,3 +113,5 @@ CodeMirror.defineMode("commonlisp", function (config) { }); CodeMirror.defineMIME("text/x-common-lisp", "commonlisp"); + +}); diff --git a/mode/css/css.js b/mode/css/css.js index 06d73455da..d63bdd0827 100644 --- a/mode/css/css.js +++ b/mode/css/css.js @@ -1,6 +1,14 @@ -CodeMirror.defineMode("css", function(config, parserConfig) { - "use strict"; +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; +CodeMirror.defineMode("css", function(config, parserConfig) { if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css"); var indentUnit = config.indentUnit, @@ -334,7 +342,6 @@ CodeMirror.defineMode("css", function(config, parserConfig) { }; }); -(function() { function keySet(array) { var keys = {}; for (var i = 0; i < array.length; ++i) { @@ -689,4 +696,5 @@ CodeMirror.defineMode("css", function(config, parserConfig) { name: "css", helperType: "less" }); -})(); + +}); diff --git a/mode/d/d.js b/mode/d/d.js index ab345f1a0d..3ab8b42838 100644 --- a/mode/d/d.js +++ b/mode/d/d.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("d", function(config, parserConfig) { var indentUnit = config.indentUnit, statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, @@ -173,7 +183,6 @@ CodeMirror.defineMode("d", function(config, parserConfig) { }; }); -(function() { function words(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; @@ -202,4 +211,5 @@ CodeMirror.defineMode("d", function(config, parserConfig) { } } }); -}()); + +}); diff --git a/mode/diff/diff.js b/mode/diff/diff.js index 9a0d90ea55..d43f15d51a 100644 --- a/mode/diff/diff.js +++ b/mode/diff/diff.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("diff", function() { var TOKEN_NAMES = { @@ -30,3 +40,5 @@ CodeMirror.defineMode("diff", function() { }); CodeMirror.defineMIME("text/x-diff", "diff"); + +}); diff --git a/mode/dtd/dtd.js b/mode/dtd/dtd.js index 7033bf0f8b..b4a6cb3155 100644 --- a/mode/dtd/dtd.js +++ b/mode/dtd/dtd.js @@ -5,6 +5,16 @@ GitHub: @peterkroon */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("dtd", function(config) { var indentUnit = config.indentUnit, type; function ret(style, tp) {type = tp; return style;} @@ -125,3 +135,5 @@ CodeMirror.defineMode("dtd", function(config) { }); CodeMirror.defineMIME("application/xml-dtd", "dtd"); + +}); diff --git a/mode/ecl/ecl.js b/mode/ecl/ecl.js index 7601b189a0..2b841ff5f1 100644 --- a/mode/ecl/ecl.js +++ b/mode/ecl/ecl.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("ecl", function(config) { function words(str) { @@ -190,3 +200,5 @@ CodeMirror.defineMode("ecl", function(config) { }); CodeMirror.defineMIME("text/x-ecl", "ecl"); + +}); diff --git a/mode/eiffel/eiffel.js b/mode/eiffel/eiffel.js index 15f34a9325..c6c3c84600 100644 --- a/mode/eiffel/eiffel.js +++ b/mode/eiffel/eiffel.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("eiffel", function() { function wordObj(words) { var o = {}; @@ -145,3 +155,5 @@ CodeMirror.defineMode("eiffel", function() { }); CodeMirror.defineMIME("text/x-eiffel", "eiffel"); + +}); diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js index bc3bdce860..79693fca22 100644 --- a/mode/erlang/erlang.js +++ b/mode/erlang/erlang.js @@ -12,6 +12,16 @@ // old guard/bif/conversion clashes (e.g. "float/1") // type/spec/opaque +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMIME("text/x-erlang", "erlang"); CodeMirror.defineMode("erlang", function(cmCfg) { @@ -605,3 +615,5 @@ CodeMirror.defineMode("erlang", function(cmCfg) { lineComment: "%" }; }); + +}); diff --git a/mode/fortran/fortran.js b/mode/fortran/fortran.js index 83fd8fde85..58dac127c1 100644 --- a/mode/fortran/fortran.js +++ b/mode/fortran/fortran.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("fortran", function() { function words(array) { var keys = {}; @@ -171,3 +181,5 @@ CodeMirror.defineMode("fortran", function() { }); CodeMirror.defineMIME("text/x-fortran", "fortran"); + +}); diff --git a/mode/gas/gas.js b/mode/gas/gas.js index a6e6892908..a21d5bbe1b 100644 --- a/mode/gas/gas.js +++ b/mode/gas/gas.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("gas", function(_config, parserConfig) { 'use strict'; @@ -328,3 +338,5 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) { blockCommentEnd: "*/" }; }); + +}); diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index 10d5e05c06..7bd5f5ab42 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../markdown/markdown")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../markdown/markdown"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("gfm", function(config, modeConfig) { var codeDepth = 0; function blankLine(state) { @@ -100,3 +110,5 @@ CodeMirror.defineMode("gfm", function(config, modeConfig) { CodeMirror.defineMIME("gfmBase", markdownConfig); return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay); }, "markdown"); + +}); diff --git a/mode/gherkin/gherkin.js b/mode/gherkin/gherkin.js index 685b373df7..41003641ec 100644 --- a/mode/gherkin/gherkin.js +++ b/mode/gherkin/gherkin.js @@ -13,6 +13,16 @@ Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues // keywords: /(Feature| {2}(Scenario|In order to|As|I)| {4}(Given|When|Then|And))/ //}; +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("gherkin", function () { return { startState: function () { @@ -161,3 +171,5 @@ CodeMirror.defineMode("gherkin", function () { }); CodeMirror.defineMIME("text/x-feature", "gherkin"); + +}); diff --git a/mode/go/go.js b/mode/go/go.js index 862c091120..7ba780ead8 100644 --- a/mode/go/go.js +++ b/mode/go/go.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("go", function(config) { var indentUnit = config.indentUnit; @@ -166,3 +176,5 @@ CodeMirror.defineMode("go", function(config) { }); CodeMirror.defineMIME("text/x-go", "go"); + +}); diff --git a/mode/groovy/groovy.js b/mode/groovy/groovy.js index 6800e0aaf5..399452d536 100644 --- a/mode/groovy/groovy.js +++ b/mode/groovy/groovy.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("groovy", function(config) { function words(str) { var obj = {}, words = str.split(" "); @@ -209,3 +219,5 @@ CodeMirror.defineMode("groovy", function(config) { }); CodeMirror.defineMIME("text/x-groovy", "groovy"); + +}); diff --git a/mode/haml/haml.js b/mode/haml/haml.js index 5ea4ed12c8..6b205b43e4 100644 --- a/mode/haml/haml.js +++ b/mode/haml/haml.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + (function() { "use strict"; @@ -147,3 +157,5 @@ CodeMirror.defineMIME("text/x-haml", "haml"); })(); + +}); diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js index facfd00a65..2876172a95 100644 --- a/mode/haskell/haskell.js +++ b/mode/haskell/haskell.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("haskell", function(_config, modeConfig) { function switchState(source, setState, f) { @@ -250,3 +260,5 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) { }); CodeMirror.defineMIME("text/x-haskell", "haskell"); + +}); diff --git a/mode/haxe/haxe.js b/mode/haxe/haxe.js index cb761ad1ce..3f78a8d674 100644 --- a/mode/haxe/haxe.js +++ b/mode/haxe/haxe.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("haxe", function(config, parserConfig) { var indentUnit = config.indentUnit; @@ -427,3 +437,5 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { }); CodeMirror.defineMIME("text/x-haxe", "haxe"); + +}); diff --git a/mode/htmlembedded/htmlembedded.js b/mode/htmlembedded/htmlembedded.js index c316cd3406..3a07c3432e 100644 --- a/mode/htmlembedded/htmlembedded.js +++ b/mode/htmlembedded/htmlembedded.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { //config settings @@ -69,3 +79,5 @@ CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingMode CodeMirror.defineMIME("application/x-aspx", { name: "htmlembedded", scriptingModeSpec:"text/x-csharp"}); CodeMirror.defineMIME("application/x-jsp", { name: "htmlembedded", scriptingModeSpec:"text/x-java"}); CodeMirror.defineMIME("application/x-erb", { name: "htmlembedded", scriptingModeSpec:"ruby"}); + +}); diff --git a/mode/htmlmixed/htmlmixed.js b/mode/htmlmixed/htmlmixed.js index e9eab3b92d..a2c3740d3d 100644 --- a/mode/htmlmixed/htmlmixed.js +++ b/mode/htmlmixed/htmlmixed.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); var cssMode = CodeMirror.getMode(config, "css"); @@ -100,3 +110,5 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { }, "xml", "javascript", "css"); CodeMirror.defineMIME("text/html", "htmlmixed"); + +}); diff --git a/mode/http/http.js b/mode/http/http.js index 5a51636027..d2ad5994b6 100644 --- a/mode/http/http.js +++ b/mode/http/http.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("http", function() { function failFirstLine(stream, state) { stream.skipToEnd(); @@ -96,3 +106,5 @@ CodeMirror.defineMode("http", function() { }); CodeMirror.defineMIME("message/http", "http"); + +}); diff --git a/mode/index.html b/mode/index.html index 7c1f740615..29d120d012 100644 --- a/mode/index.html +++ b/mode/index.html @@ -80,7 +80,7 @@
  • Python
  • Q
  • R
  • -
  • RPM spec and changelog
  • +
  • RPM
  • reStructuredText
  • Ruby
  • Rust
  • diff --git a/mode/jade/jade.js b/mode/jade/jade.js index 61abb27ab7..020b93ec5d 100644 --- a/mode/jade/jade.js +++ b/mode/jade/jade.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("jade", function () { var symbol_regex1 = /^(?:~|!|%|\^|\*|\+|=|\\|:|;|,|\/|\?|&|<|>|\|)/; var open_paren_regex = /^(\(|\[)/; @@ -88,3 +98,5 @@ CodeMirror.defineMode("jade", function () { }); CodeMirror.defineMIME('text/x-jade', 'jade'); + +}); diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fbf574b4fe..8d6035de2c 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -1,5 +1,15 @@ // TODO actually recognize syntax of TypeScript constructs +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var statementIndent = parserConfig.statementIndent; @@ -628,3 +638,5 @@ CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); + +}); diff --git a/mode/jinja2/jinja2.js b/mode/jinja2/jinja2.js index b28af098d0..e30ce63478 100644 --- a/mode/jinja2/jinja2.js +++ b/mode/jinja2/jinja2.js @@ -1,4 +1,14 @@ -CodeMirror.defineMode("jinja2", function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + + CodeMirror.defineMode("jinja2", function() { var keywords = ["and", "as", "block", "endblock", "by", "cycle", "debug", "else", "elif", "extends", "filter", "endfilter", "firstof", "for", "endfor", "if", "endif", "ifchanged", "endifchanged", @@ -15,38 +25,39 @@ CodeMirror.defineMode("jinja2", function() { keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); function tokenBase (stream, state) { - var ch = stream.next(); - if (ch == "{") { - if (ch = stream.eat(/\{|%|#/)) { - stream.eat("-"); - state.tokenize = inTag(ch); - return "tag"; - } + var ch = stream.next(); + if (ch == "{") { + if (ch = stream.eat(/\{|%|#/)) { + stream.eat("-"); + state.tokenize = inTag(ch); + return "tag"; } + } } function inTag (close) { - if (close == "{") { - close = "}"; + if (close == "{") { + close = "}"; + } + return function (stream, state) { + var ch = stream.next(); + if ((ch == close || (ch == "-" && stream.eat(close))) + && stream.eat("}")) { + state.tokenize = tokenBase; + return "tag"; } - return function (stream, state) { - var ch = stream.next(); - if ((ch == close || (ch == "-" && stream.eat(close))) - && stream.eat("}")) { - state.tokenize = tokenBase; - return "tag"; - } - if (stream.match(keywords)) { - return "keyword"; - } - return close == "#" ? "comment" : "string"; - }; + if (stream.match(keywords)) { + return "keyword"; + } + return close == "#" ? "comment" : "string"; + }; } return { - startState: function () { - return {tokenize: tokenBase}; - }, - token: function (stream, state) { - return state.tokenize(stream, state); - } + startState: function () { + return {tokenize: tokenBase}; + }, + token: function (stream, state) { + return state.tokenize(stream, state); + } }; + }); }); diff --git a/mode/julia/julia.js b/mode/julia/julia.js index 9ec2428cd4..b603713040 100644 --- a/mode/julia/julia.js +++ b/mode/julia/julia.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("julia", function(_conf, parserConf) { var ERRORCLASS = 'error'; @@ -260,3 +270,5 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) { CodeMirror.defineMIME("text/x-julia", "julia"); + +}); diff --git a/mode/less/less.js b/mode/less/less.js index da681ea67b..9f0d43e938 100644 --- a/mode/less/less.js +++ b/mode/less/less.js @@ -5,6 +5,16 @@ GitHub: @peterkroon */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("less", function(config) { var indentUnit = config.indentUnit, type; function ret(style, tp) {type = tp; return style;} @@ -345,3 +355,5 @@ CodeMirror.defineMode("less", function(config) { CodeMirror.defineMIME("text/x-less", "less"); if (!CodeMirror.mimeModes.hasOwnProperty("text/css")) CodeMirror.defineMIME("text/css", "less"); + +}); diff --git a/mode/livescript/livescript.js b/mode/livescript/livescript.js index c000324b8c..9322ba8073 100644 --- a/mode/livescript/livescript.js +++ b/mode/livescript/livescript.js @@ -2,6 +2,17 @@ * Link to the project's GitHub page: * https://github.com/duralog/CodeMirror */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + (function() { CodeMirror.defineMode('livescript', function(){ var tokenBase, external; @@ -265,3 +276,5 @@ })(); CodeMirror.defineMIME('text/x-livescript', 'livescript'); + +}); diff --git a/mode/lua/lua.js b/mode/lua/lua.js index b8deaa2575..3673557c27 100644 --- a/mode/lua/lua.js +++ b/mode/lua/lua.js @@ -2,6 +2,16 @@ // CodeMirror 1 mode. // highlights keywords, strings, comments (no leveling supported! ("[==[")), tokens, basic indenting +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("lua", function(config, parserConfig) { var indentUnit = config.indentUnit; @@ -142,3 +152,5 @@ CodeMirror.defineMode("lua", function(config, parserConfig) { }); CodeMirror.defineMIME("text/x-lua", "lua"); + +}); diff --git a/mode/markdown/markdown.js b/mode/markdown/markdown.js index da2ba93fea..cfa56b911d 100644 --- a/mode/markdown/markdown.js +++ b/mode/markdown/markdown.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror", require("../xml/xml"))); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../xml/xml"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var htmlFound = CodeMirror.modes.hasOwnProperty("xml"); @@ -744,3 +754,5 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { }, "xml"); CodeMirror.defineMIME("text/x-markdown", "markdown"); + +}); diff --git a/mode/mirc/mirc.js b/mode/mirc/mirc.js index fc88bc56fb..6b2a23a16c 100644 --- a/mode/mirc/mirc.js +++ b/mode/mirc/mirc.js @@ -1,4 +1,15 @@ //mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMIME("text/mirc", "mirc"); CodeMirror.defineMode("mirc", function() { function parseWords(str) { @@ -175,3 +186,5 @@ CodeMirror.defineMode("mirc", function() { } }; }); + +}); diff --git a/mode/mllike/mllike.js b/mode/mllike/mllike.js index 1d64789a19..d4d59fceb7 100644 --- a/mode/mllike/mllike.js +++ b/mode/mllike/mllike.js @@ -1,5 +1,14 @@ -CodeMirror.defineMode('mllike', function(_config, parserConfig) { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; +CodeMirror.defineMode('mllike', function(_config, parserConfig) { var words = { 'let': 'keyword', 'rec': 'keyword', @@ -189,3 +198,5 @@ CodeMirror.defineMIME('text/x-fsharp', { }, slashComments: true }); + +}); diff --git a/mode/nginx/nginx.js b/mode/nginx/nginx.js index c98c8a1dd3..4e17cdb3c4 100644 --- a/mode/nginx/nginx.js +++ b/mode/nginx/nginx.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("nginx", function(config) { function words(str) { @@ -161,3 +171,5 @@ CodeMirror.defineMode("nginx", function(config) { }); CodeMirror.defineMIME("text/nginx", "text/x-nginx-conf"); + +}); diff --git a/mode/ntriples/ntriples.js b/mode/ntriples/ntriples.js index ed0cee34b0..cd5eb24f07 100644 --- a/mode/ntriples/ntriples.js +++ b/mode/ntriples/ntriples.js @@ -25,6 +25,17 @@ -> ERROR } */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("ntriples", function() { var Location = { @@ -168,3 +179,5 @@ CodeMirror.defineMode("ntriples", function() { }); CodeMirror.defineMIME("text/n-triples", "ntriples"); + +}); diff --git a/mode/octave/octave.js b/mode/octave/octave.js index 23cd2fe222..0d8f610d1d 100644 --- a/mode/octave/octave.js +++ b/mode/octave/octave.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("octave", function() { function wordRegexp(words) { return new RegExp("^((" + words.join(")|(") + "))\\b"); @@ -116,3 +126,5 @@ CodeMirror.defineMode("octave", function() { }); CodeMirror.defineMIME("text/x-octave", "octave"); + +}); diff --git a/mode/pascal/pascal.js b/mode/pascal/pascal.js index 09d9b0617b..642dd9bf79 100644 --- a/mode/pascal/pascal.js +++ b/mode/pascal/pascal.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("pascal", function() { function words(str) { var obj = {}, words = str.split(" "); @@ -92,3 +102,5 @@ CodeMirror.defineMode("pascal", function() { }); CodeMirror.defineMIME("text/x-pascal", "pascal"); + +}); diff --git a/mode/pegjs/pegjs.js b/mode/pegjs/pegjs.js index 6b2e273791..f74f2a4f22 100644 --- a/mode/pegjs/pegjs.js +++ b/mode/pegjs/pegjs.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../javascript/javascript")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../javascript/javascript"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("pegjs", function (config) { var jsMode = CodeMirror.getMode(config, "javascript"); @@ -97,3 +107,5 @@ CodeMirror.defineMode("pegjs", function (config) { } }; }, "javascript"); + +}); diff --git a/mode/perl/perl.js b/mode/perl/perl.js index 5954b1a61c..84392e4a81 100644 --- a/mode/perl/perl.js +++ b/mode/perl/perl.js @@ -1,5 +1,16 @@ // CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08) // This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com) + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("perl",function(){ // http://perldoc.perl.org var PERL={ // null - magic touch @@ -509,7 +520,7 @@ CodeMirror.defineMode("perl",function(){ return tokenSOMETHING(stream,state,'=cut');} var ch=stream.next(); if(ch=='"'||ch=="'"){ // NOTE: ' or " or <<'SOMETHING'\n...\nSOMETHING\n or <<"SOMETHING"\n...\nSOMETHING\n - if(stream.prefix(3)=="<<"+ch){ + if(prefix(stream, 3)=="<<"+ch){ var p=stream.pos; stream.eatWhile(/\w/); var n=stream.current().substr(1); @@ -518,94 +529,94 @@ CodeMirror.defineMode("perl",function(){ stream.pos=p;} return tokenChain(stream,state,[ch],"string");} if(ch=="q"){ - var c=stream.look(-2); + var c=look(stream, -2); if(!(c&&/\w/.test(c))){ - c=stream.look(0); + c=look(stream, 0); if(c=="x"){ - c=stream.look(1); + c=look(stream, 1); if(c=="("){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[")"],RXstyle,RXmodifiers);} if(c=="["){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["]"],RXstyle,RXmodifiers);} if(c=="{"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["}"],RXstyle,RXmodifiers);} if(c=="<"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);} if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}} else if(c=="q"){ - c=stream.look(1); + c=look(stream, 1); if(c=="("){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[")"],"string");} if(c=="["){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["]"],"string");} if(c=="{"){ -stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["}"],"string");} if(c=="<"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[">"],"string");} if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,[stream.eat(c)],"string");}} else if(c=="w"){ - c=stream.look(1); + c=look(stream, 1); if(c=="("){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[")"],"bracket");} if(c=="["){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["]"],"bracket");} if(c=="{"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["}"],"bracket");} if(c=="<"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[">"],"bracket");} if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,[stream.eat(c)],"bracket");}} else if(c=="r"){ - c=stream.look(1); + c=look(stream, 1); if(c=="("){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[")"],RXstyle,RXmodifiers);} if(c=="["){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["]"],RXstyle,RXmodifiers);} if(c=="{"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,["}"],RXstyle,RXmodifiers);} if(c=="<"){ - stream.eatSuffix(2); + eatSuffix(stream, 2); return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);} if(/[\^'"!~\/]/.test(c)){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,[stream.eat(c)],RXstyle,RXmodifiers);}} else if(/[\^'"!~\/(\[{<]/.test(c)){ if(c=="("){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,[")"],"string");} if(c=="["){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,["]"],"string");} if(c=="{"){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,["}"],"string");} if(c=="<"){ - stream.eatSuffix(1); + eatSuffix(stream, 1); return tokenChain(stream,state,[">"],"string");} if(/[\^'"!~\/]/.test(c)){ return tokenChain(stream,state,[stream.eat(c)],"string");}}}} if(ch=="m"){ - var c=stream.look(-2); + var c=look(stream, -2); if(!(c&&/\w/.test(c))){ c=stream.eat(/[(\[{<\^'"!~\/]/); if(c){ @@ -620,7 +631,7 @@ stream.eatSuffix(2); if(c=="<"){ return tokenChain(stream,state,[">"],RXstyle,RXmodifiers);}}}} if(ch=="s"){ - var c=/[\/>\]})\w]/.test(stream.look(-2)); + var c=/[\/>\]})\w]/.test(look(stream, -2)); if(!c){ c=stream.eat(/[(\[{<\^'"!~\/]/); if(c){ @@ -634,7 +645,7 @@ stream.eatSuffix(2); return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}} if(ch=="y"){ - var c=/[\/>\]})\w]/.test(stream.look(-2)); + var c=/[\/>\]})\w]/.test(look(stream, -2)); if(!c){ c=stream.eat(/[(\[{<\^'"!~\/]/); if(c){ @@ -648,7 +659,7 @@ stream.eatSuffix(2); return tokenChain(stream,state,[")",")"],RXstyle,RXmodifiers); return tokenChain(stream,state,[c,c],RXstyle,RXmodifiers);}}} if(ch=="t"){ - var c=/[\/>\]})\w]/.test(stream.look(-2)); + var c=/[\/>\]})\w]/.test(look(stream, -2)); if(!c){ c=stream.eat("r");if(c){ c=stream.eat(/[(\[{<\^'"!~\/]/); @@ -665,7 +676,7 @@ stream.eatSuffix(2); if(ch=="`"){ return tokenChain(stream,state,[ch],"variable-2");} if(ch=="/"){ - if(!/~\s*$/.test(stream.prefix())) + if(!/~\s*$/.test(prefix(stream))) return "operator"; else return tokenChain(stream,state,[ch],RXstyle,RXmodifiers);} @@ -677,7 +688,7 @@ stream.eatSuffix(2); stream.pos=p;} if(/[$@%]/.test(ch)){ var p=stream.pos; - if(stream.eat("^")&&stream.eat(/[A-Z]/)||!/[@$%&]/.test(stream.look(-2))&&stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/)){ + if(stream.eat("^")&&stream.eat(/[A-Z]/)||!/[@$%&]/.test(look(stream, -2))&&stream.eat(/[=|\\\-#?@;:&`~\^!\[\]*'"$+.,\/<>()]/)){ var c=stream.current(); if(PERL[c]) return "variable-2";} @@ -690,7 +701,7 @@ stream.eatSuffix(2); else return "variable";}} if(ch=="#"){ - if(stream.look(-2)!="$"){ + if(look(stream, -2)!="$"){ stream.skipToEnd(); return "comment";}} if(/[:+\-\^*$&%@=<>!?|\/~\.]/.test(ch)){ @@ -702,23 +713,23 @@ stream.eatSuffix(2); stream.pos=p;} if(ch=="_"){ if(stream.pos==1){ - if(stream.suffix(6)=="_END__"){ + if(suffix(stream, 6)=="_END__"){ return tokenChain(stream,state,['\0'],"comment");} - else if(stream.suffix(7)=="_DATA__"){ + else if(suffix(stream, 7)=="_DATA__"){ return tokenChain(stream,state,['\0'],"variable-2");} - else if(stream.suffix(7)=="_C__"){ + else if(suffix(stream, 7)=="_C__"){ return tokenChain(stream,state,['\0'],"string");}}} if(/\w/.test(ch)){ var p=stream.pos; - if(stream.look(-2)=="{"&&(stream.look(0)=="}"||stream.eatWhile(/\w/)&&stream.look(0)=="}")) + if(look(stream, -2)=="{"&&(look(stream, 0)=="}"||stream.eatWhile(/\w/)&&look(stream, 0)=="}")) return "string"; else stream.pos=p;} if(/[A-Z]/.test(ch)){ - var l=stream.look(-2); + var l=look(stream, -2); var p=stream.pos; stream.eatWhile(/[A-Z_]/); - if(/[\da-z]/.test(stream.look(0))){ + if(/[\da-z]/.test(look(stream, 0))){ stream.pos=p;} else{ var c=PERL[stream.current()]; @@ -742,7 +753,7 @@ stream.eatSuffix(2); else return "meta";}} if(/[a-zA-Z_]/.test(ch)){ - var l=stream.look(-2); + var l=look(stream, -2); stream.eatWhile(/\w/); var c=PERL[stream.current()]; if(!c) @@ -780,37 +791,37 @@ stream.eatSuffix(2); CodeMirror.defineMIME("text/x-perl", "perl"); // it's like "peek", but need for look-ahead or look-behind if index < 0 -CodeMirror.StringStream.prototype.look=function(c){ - return this.string.charAt(this.pos+(c||0));}; +function look(stream, c){ + return stream.string.charAt(stream.pos+(c||0)); +} // return a part of prefix of current stream from current position -CodeMirror.StringStream.prototype.prefix=function(c){ - if(c){ - var x=this.pos-c; - return this.string.substr((x>=0?x:0),c);} - else{ - return this.string.substr(0,this.pos-1);}}; +function prefix(stream, c){ + if(c){ + var x=stream.pos-c; + return stream.string.substr((x>=0?x:0),c);} + else{ + return stream.string.substr(0,stream.pos-1); + } +} // return a part of suffix of current stream from current position -CodeMirror.StringStream.prototype.suffix=function(c){ - var y=this.string.length; - var x=y-this.pos+1; - return this.string.substr(this.pos,(c&&c=(y=this.string.length-1)) - this.pos=y; - else - this.pos=x;}; +function eatSuffix(stream, c){ + var x=stream.pos+c; + var y; + if(x<=0) + stream.pos=0; + else if(x>=(y=stream.string.length-1)) + stream.pos=y; + else + stream.pos=x; +} + +}); diff --git a/mode/php/php.js b/mode/php/php.js index 946f770c9f..7156dbba6f 100644 --- a/mode/php/php.js +++ b/mode/php/php.js @@ -1,4 +1,13 @@ -(function() { +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../clike/clike")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../clike/clike"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { + "use strict"; + function keywords(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; @@ -128,4 +137,4 @@ CodeMirror.defineMIME("application/x-httpd-php", "php"); CodeMirror.defineMIME("application/x-httpd-php-open", {name: "php", startOpen: true}); CodeMirror.defineMIME("text/x-php", phpConfig); -})(); +}); diff --git a/mode/pig/pig.js b/mode/pig/pig.js index 4b44e7ccc3..eaf525889c 100644 --- a/mode/pig/pig.js +++ b/mode/pig/pig.js @@ -4,6 +4,16 @@ * @link https://github.com/prasanthj/pig-codemirror-2 * This implementation is adapted from PL/SQL mode in CodeMirror 2. */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("pig", function(_config, parserConfig) { var keywords = parserConfig.keywords, builtins = parserConfig.builtins, @@ -169,3 +179,5 @@ CodeMirror.defineMode("pig", function(_config, parserConfig) { types: keywords(pTypes) }); }()); + +}); diff --git a/mode/properties/properties.js b/mode/properties/properties.js index d3a13c765d..6dfe06f128 100644 --- a/mode/properties/properties.js +++ b/mode/properties/properties.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("properties", function() { return { token: function(stream, state) { @@ -61,3 +71,5 @@ CodeMirror.defineMode("properties", function() { CodeMirror.defineMIME("text/x-properties", "properties"); CodeMirror.defineMIME("text/x-ini", "properties"); + +}); diff --git a/mode/python/python.js b/mode/python/python.js index 8bea5d19d1..36cf951bef 100644 --- a/mode/python/python.js +++ b/mode/python/python.js @@ -1,3 +1,14 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + + CodeMirror.defineMode("python", function(conf, parserConf) { var ERRORCLASS = 'error'; @@ -361,14 +372,13 @@ CodeMirror.defineMode("python", function(conf, parserConf) { CodeMirror.defineMIME("text/x-python", "python"); -(function() { - "use strict"; - var words = function(str){return str.split(' ');}; - - CodeMirror.defineMIME("text/x-cython", { - name: "python", - extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ - "extern gil include nogil property public"+ - "readonly struct union DEF IF ELIF ELSE") - }); -})(); +var words = function(str){return str.split(' ');}; + +CodeMirror.defineMIME("text/x-cython", { + name: "python", + extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+ + "extern gil include nogil property public"+ + "readonly struct union DEF IF ELIF ELSE") +}); + +}); diff --git a/mode/q/q.js b/mode/q/q.js index 56017e30ad..d6e3b66610 100644 --- a/mode/q/q.js +++ b/mode/q/q.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("q",function(config){ var indentUnit=config.indentUnit, curPunc, @@ -122,3 +132,5 @@ CodeMirror.defineMode("q",function(config){ }; }); CodeMirror.defineMIME("text/x-q","q"); + +}); diff --git a/mode/r/r.js b/mode/r/r.js index 690cf40502..7f4feb238b 100644 --- a/mode/r/r.js +++ b/mode/r/r.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("r", function(config) { function wordObj(str) { var words = str.split(" "), res = {}; @@ -143,3 +153,5 @@ CodeMirror.defineMode("r", function(config) { }); CodeMirror.defineMIME("text/x-rsrc", "r"); + +}); diff --git a/mode/rpm/changes/changes.js b/mode/rpm/changes/changes.js deleted file mode 100644 index 14a08d9700..0000000000 --- a/mode/rpm/changes/changes.js +++ /dev/null @@ -1,19 +0,0 @@ -CodeMirror.defineMode("changes", function() { - var headerSeperator = /^-+$/; - var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /; - var simpleEmail = /^[\w+.-]+@[\w.-]+/; - - return { - token: function(stream) { - if (stream.sol()) { - if (stream.match(headerSeperator)) { return 'tag'; } - if (stream.match(headerLine)) { return 'tag'; } - } - if (stream.match(simpleEmail)) { return 'string'; } - stream.next(); - return null; - } - }; -}); - -CodeMirror.defineMIME("text/x-rpm-changes", "changes"); diff --git a/mode/rpm/changes/index.html b/mode/rpm/changes/index.html deleted file mode 100644 index 18fe7ab7bf..0000000000 --- a/mode/rpm/changes/index.html +++ /dev/null @@ -1,67 +0,0 @@ - - -CodeMirror: RPM changes mode - - - - - - - - - - - -
    -

    RPM changes mode

    - -
    - - -

    MIME types defined: text/x-rpm-changes.

    -
    diff --git a/mode/rpm/spec/index.html b/mode/rpm/index.html similarity index 60% rename from mode/rpm/spec/index.html rename to mode/rpm/index.html index 127b72ee9d..4257bc77e3 100644 --- a/mode/rpm/spec/index.html +++ b/mode/rpm/index.html @@ -1,34 +1,71 @@ -CodeMirror: RPM spec mode +CodeMirror: RPM changes mode - - - - - + + + +
    +

    RPM changes mode

    + +
    + +

    RPM spec mode

    -
    -

    MIME types defined: text/x-rpm-spec.

    - +

    MIME types defined: text/x-rpm-spec, text/x-rpm-changes.

    diff --git a/mode/rpm/spec/spec.js b/mode/rpm/rpm.js similarity index 70% rename from mode/rpm/spec/spec.js rename to mode/rpm/rpm.js index 0fab6c4891..497997c4ff 100644 --- a/mode/rpm/spec/spec.js +++ b/mode/rpm/rpm.js @@ -1,6 +1,36 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + +CodeMirror.defineMode("rpm-changes", function() { + var headerSeperator = /^-+$/; + var headerLine = /^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ?\d{1,2} \d{2}:\d{2}(:\d{2})? [A-Z]{3,4} \d{4} - /; + var simpleEmail = /^[\w+.-]+@[\w.-]+/; + + return { + token: function(stream) { + if (stream.sol()) { + if (stream.match(headerSeperator)) { return 'tag'; } + if (stream.match(headerLine)) { return 'tag'; } + } + if (stream.match(simpleEmail)) { return 'string'; } + stream.next(); + return null; + } + }; +}); + +CodeMirror.defineMIME("text/x-rpm-changes", "rpm-changes"); + // Quick and dirty spec file highlighting -CodeMirror.defineMode("spec", function() { +CodeMirror.defineMode("rpm-spec", function() { var arch = /^(i386|i586|i686|x86_64|ppc64|ppc|ia64|s390x|s390|sparc64|sparcv9|sparc|noarch|alphaev6|alpha|hppa|mipsel)/; var preamble = /^(Name|Version|Release|License|Summary|Url|Group|Source|BuildArch|BuildRequires|BuildRoot|AutoReqProv|Provides|Requires(\(\w+\))?|Obsoletes|Conflicts|Recommends|Source\d*|Patch\d*|ExclusiveArch|NoSource|Supplements):/; @@ -63,4 +93,6 @@ CodeMirror.defineMode("spec", function() { }; }); -CodeMirror.defineMIME("text/x-rpm-spec", "spec"); +CodeMirror.defineMIME("text/x-rpm-spec", "rpm-spec"); + +}); diff --git a/mode/rpm/spec/spec.css b/mode/rpm/spec/spec.css deleted file mode 100644 index d0a5d430ca..0000000000 --- a/mode/rpm/spec/spec.css +++ /dev/null @@ -1,5 +0,0 @@ -.cm-s-default span.cm-preamble {color: #b26818; font-weight: bold;} -.cm-s-default span.cm-macro {color: #b218b2;} -.cm-s-default span.cm-section {color: green; font-weight: bold;} -.cm-s-default span.cm-script {color: red;} -.cm-s-default span.cm-issue {color: yellow;} diff --git a/mode/rst/rst.js b/mode/rst/rst.js index 75563ba982..c53cbc36e8 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../python/python"), require("../stex/stex")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../python/python", "../stex/stex"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode('rst-base', function (config) { /////////////////////////////////////////////////////////////////////////// @@ -558,3 +568,5 @@ CodeMirror.defineMIME('text/x-rst', 'rst'); /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// + +}); diff --git a/mode/ruby/ruby.js b/mode/ruby/ruby.js index 1cdc9a4e43..8ee9eec707 100644 --- a/mode/ruby/ruby.js +++ b/mode/ruby/ruby.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("ruby", function(config) { function wordObj(words) { var o = {}; @@ -247,3 +257,4 @@ CodeMirror.defineMode("ruby", function(config) { CodeMirror.defineMIME("text/x-ruby", "ruby"); +}); diff --git a/mode/rust/rust.js b/mode/rust/rust.js index c7530b6cc6..2e6e20b231 100644 --- a/mode/rust/rust.js +++ b/mode/rust/rust.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("rust", function() { var indentUnit = 4, altIndentUnit = 2; var valKeywords = { @@ -434,3 +444,5 @@ CodeMirror.defineMode("rust", function() { }); CodeMirror.defineMIME("text/x-rustsrc", "rust"); + +}); diff --git a/mode/sass/sass.js b/mode/sass/sass.js index 9c9a0dae07..74ae91db12 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("sass", function(config) { var tokenRegexp = function(words){ return new RegExp("^" + words.join("|")); @@ -328,3 +338,5 @@ CodeMirror.defineMode("sass", function(config) { }); CodeMirror.defineMIME("text/x-sass", "sass"); + +}); diff --git a/mode/scheme/scheme.js b/mode/scheme/scheme.js index c5990ae92c..7124f7283b 100644 --- a/mode/scheme/scheme.js +++ b/mode/scheme/scheme.js @@ -1,6 +1,17 @@ /** * Author: Koh Zi Han, based on implementation by Koh Zi Chun */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("scheme", function () { var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", ATOM = "atom", NUMBER = "number", BRACKET = "bracket"; @@ -230,3 +241,5 @@ CodeMirror.defineMode("scheme", function () { }); CodeMirror.defineMIME("text/x-scheme", "scheme"); + +}); diff --git a/mode/shell/shell.js b/mode/shell/shell.js index abfd214454..7753411c98 100644 --- a/mode/shell/shell.js +++ b/mode/shell/shell.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode('shell', function() { var words = {}; @@ -116,3 +126,5 @@ CodeMirror.defineMode('shell', function() { }); CodeMirror.defineMIME('text/x-sh', 'shell'); + +}); diff --git a/mode/sieve/sieve.js b/mode/sieve/sieve.js index 8ca2a4cb82..8256dda0a4 100644 --- a/mode/sieve/sieve.js +++ b/mode/sieve/sieve.js @@ -1,7 +1,12 @@ -/* - * See LICENSE in this directory for the license under which this code - * is released. - */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; CodeMirror.defineMode("sieve", function(config) { function words(str) { @@ -181,3 +186,5 @@ CodeMirror.defineMode("sieve", function(config) { }); CodeMirror.defineMIME("application/sieve", "sieve"); + +}); diff --git a/mode/smalltalk/smalltalk.js b/mode/smalltalk/smalltalk.js index 86f749b310..deb78a4f7a 100644 --- a/mode/smalltalk/smalltalk.js +++ b/mode/smalltalk/smalltalk.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode('smalltalk', function(config) { var specialChars = /[+\-\/\\*~<>=@%|&?!.,:;^]/; @@ -151,3 +161,5 @@ CodeMirror.defineMode('smalltalk', function(config) { }); CodeMirror.defineMIME('text/x-stsrc', {name: 'smalltalk'}); + +}); diff --git a/mode/smarty/smarty.js b/mode/smarty/smarty.js index 826c2b966c..2a78c6d394 100644 --- a/mode/smarty/smarty.js +++ b/mode/smarty/smarty.js @@ -1,6 +1,17 @@ /** * Smarty 2 and 3 mode. */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("smarty", function(config) { "use strict"; @@ -203,3 +214,5 @@ CodeMirror.defineMode("smarty", function(config) { }); CodeMirror.defineMIME("text/x-smarty", "smarty"); + +}); diff --git a/mode/smartymixed/smartymixed.js b/mode/smartymixed/smartymixed.js index 48dd1a6752..7e5e12c0ec 100644 --- a/mode/smartymixed/smartymixed.js +++ b/mode/smartymixed/smartymixed.js @@ -5,6 +5,17 @@ * @version 3.0 * @date 05.07.2013 */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../smarty/smarty")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../smarty/smarty"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("smartymixed", function(config) { var settings, regs, helpers, parsers, htmlMixedMode = CodeMirror.getMode(config, "htmlmixed"), @@ -166,8 +177,9 @@ CodeMirror.defineMode("smartymixed", function(config) { }; } }; -}, -"htmlmixed"); +}, "htmlmixed", "smarty"); CodeMirror.defineMIME("text/x-smarty", "smartymixed"); // vim: et ts=2 sts=2 sw=2 + +}); diff --git a/mode/sparql/sparql.js b/mode/sparql/sparql.js index 0329057f69..f228b1dffe 100644 --- a/mode/sparql/sparql.js +++ b/mode/sparql/sparql.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("sparql", function(config) { var indentUnit = config.indentUnit; var curPunc; @@ -143,3 +153,5 @@ CodeMirror.defineMode("sparql", function(config) { }); CodeMirror.defineMIME("application/x-sparql-query", "sparql"); + +}); diff --git a/mode/sql/sql.js b/mode/sql/sql.js index 903fa6740a..c43285f338 100644 --- a/mode/sql/sql.js +++ b/mode/sql/sql.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("sql", function(config, parserConfig) { "use strict"; @@ -334,6 +344,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { }); }()); +}); + /* How Properties of Mime Types are used by SQL Mode ================================================= diff --git a/mode/stex/stex.js b/mode/stex/stex.js index ca04c24f22..59a395a0fe 100644 --- a/mode/stex/stex.js +++ b/mode/stex/stex.js @@ -3,6 +3,16 @@ * Licence: MIT */ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("stex", function() { "use strict"; @@ -244,3 +254,5 @@ CodeMirror.defineMode("stex", function() { CodeMirror.defineMIME("text/x-stex", "stex"); CodeMirror.defineMIME("text/x-latex", "stex"); + +}); diff --git a/mode/tcl/tcl.js b/mode/tcl/tcl.js index ed2c697217..4c29ee7d98 100644 --- a/mode/tcl/tcl.js +++ b/mode/tcl/tcl.js @@ -1,4 +1,15 @@ //tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("tcl", function() { function parseWords(str) { var obj = {}, words = str.split(" "); @@ -129,3 +140,5 @@ CodeMirror.defineMode("tcl", function() { }; }); CodeMirror.defineMIME("text/x-tcl", "tcl"); + +}); diff --git a/mode/tiddlywiki/tiddlywiki.js b/mode/tiddlywiki/tiddlywiki.js index 24a24786bc..ecd1d173c0 100644 --- a/mode/tiddlywiki/tiddlywiki.js +++ b/mode/tiddlywiki/tiddlywiki.js @@ -14,6 +14,17 @@ CoreVersion parameter is needed for TiddlyWiki only! ***/ //{{{ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("tiddlywiki", function () { // Tokenizer var textwords = {}; @@ -350,4 +361,6 @@ CodeMirror.defineMode("tiddlywiki", function () { }); CodeMirror.defineMIME("text/x-tiddlywiki", "tiddlywiki"); +}); + //}}} diff --git a/mode/tiki/tiki.js b/mode/tiki/tiki.js index e789163dc7..eb9a893fde 100644 --- a/mode/tiki/tiki.js +++ b/mode/tiki/tiki.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode('tiki', function(config) { function inBlock(style, terminator, returnTokenizer) { return function(stream, state) { @@ -306,3 +316,5 @@ return { }); CodeMirror.defineMIME("text/tiki", "tiki"); + +}); diff --git a/mode/toml/toml.js b/mode/toml/toml.js index 1d163f13bf..552dd57b08 100644 --- a/mode/toml/toml.js +++ b/mode/toml/toml.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("toml", function () { return { startState: function () { @@ -69,3 +79,5 @@ CodeMirror.defineMode("toml", function () { }); CodeMirror.defineMIME('text/x-toml', 'toml'); + +}); diff --git a/mode/turtle/turtle.js b/mode/turtle/turtle.js index e118bfbced..de9fc2b6c7 100644 --- a/mode/turtle/turtle.js +++ b/mode/turtle/turtle.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("turtle", function(config) { var indentUnit = config.indentUnit; var curPunc; @@ -143,3 +153,5 @@ CodeMirror.defineMode("turtle", function(config) { }); CodeMirror.defineMIME("text/turtle", "turtle"); + +}); diff --git a/mode/vb/vb.js b/mode/vb/vb.js index 27b2271951..4fd80210fb 100644 --- a/mode/vb/vb.js +++ b/mode/vb/vb.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("vb", function(conf, parserConf) { var ERRORCLASS = 'error'; @@ -257,3 +267,5 @@ CodeMirror.defineMode("vb", function(conf, parserConf) { }); CodeMirror.defineMIME("text/x-vb", "vb"); + +}); diff --git a/mode/vbscript/vbscript.js b/mode/vbscript/vbscript.js index 0a97fb6405..4be7c7f2bb 100644 --- a/mode/vbscript/vbscript.js +++ b/mode/vbscript/vbscript.js @@ -8,6 +8,17 @@ E.G.: isASP: true }); */ + +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("vbscript", function(conf, parserConf) { var ERRORCLASS = 'error'; @@ -332,3 +343,5 @@ CodeMirror.defineMode("vbscript", function(conf, parserConf) { }); CodeMirror.defineMIME("text/vbscript", "vbscript"); + +}); diff --git a/mode/velocity/velocity.js b/mode/velocity/velocity.js index 968d8799e9..b64636bb43 100644 --- a/mode/velocity/velocity.js +++ b/mode/velocity/velocity.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("velocity", function() { function parseWords(str) { var obj = {}, words = str.split(" "); @@ -184,3 +194,5 @@ CodeMirror.defineMode("velocity", function() { }); CodeMirror.defineMIME("text/velocity", "velocity"); + +}); diff --git a/mode/verilog/verilog.js b/mode/verilog/verilog.js index 708de23f49..bc4fd4feee 100644 --- a/mode/verilog/verilog.js +++ b/mode/verilog/verilog.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("verilog", function(config, parserConfig) { var indentUnit = config.indentUnit, keywords = parserConfig.keywords || {}, @@ -146,7 +156,6 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { }; }); -(function() { function words(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; @@ -179,4 +188,5 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) { atoms: words("null"), hooks: {"`": metaHook, "$": metaHook} }); -}()); + +}); diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 96b51ff970..2e62b88346 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("xml", function(config, parserConfig) { var indentUnit = config.indentUnit; var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1; @@ -330,3 +340,5 @@ CodeMirror.defineMIME("text/xml", "xml"); CodeMirror.defineMIME("application/xml", "xml"); if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); + +}); diff --git a/mode/xquery/xquery.js b/mode/xquery/xquery.js index be17984635..2c7faf425a 100644 --- a/mode/xquery/xquery.js +++ b/mode/xquery/xquery.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("xquery", function() { // The keywords object is set to the result of this self executing @@ -430,3 +440,5 @@ CodeMirror.defineMode("xquery", function() { }); CodeMirror.defineMIME("application/xquery", "xquery"); + +}); diff --git a/mode/yaml/yaml.js b/mode/yaml/yaml.js index efacd7d563..f7b3a90c03 100644 --- a/mode/yaml/yaml.js +++ b/mode/yaml/yaml.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode("yaml", function() { var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; @@ -95,3 +105,5 @@ CodeMirror.defineMode("yaml", function() { }); CodeMirror.defineMIME("text/x-yaml", "yaml"); + +}); diff --git a/mode/z80/z80.js b/mode/z80/z80.js index ff43d32b52..c778803072 100644 --- a/mode/z80/z80.js +++ b/mode/z80/z80.js @@ -1,3 +1,13 @@ +(function(mod) { + if (typeof exports == "object" && typeof module == "object") // CommonJS + mod(require("../../lib/codemirror")); + else if (typeof define == "function" && define.amd) // AMD + define(["../../lib/codemirror"], mod); + else // Plain browser env + mod(CodeMirror); +})(function(CodeMirror) { +"use strict"; + CodeMirror.defineMode('z80', function() { var keywords1 = /^(exx?|(ld|cp|in)([di]r?)?|pop|push|ad[cd]|cpl|daa|dec|inc|neg|sbc|sub|and|bit|[cs]cf|x?or|res|set|r[lr]c?a?|r[lr]d|s[lr]a|srl|djnz|nop|rst|[de]i|halt|im|ot[di]r|out[di]?)\b/i; var keywords2 = /^(call|j[pr]|ret[in]?)\b/i; @@ -83,3 +93,5 @@ CodeMirror.defineMode('z80', function() { }); CodeMirror.defineMIME("text/x-z80", "z80"); + +}); From 3e7781d99e330f24e07039735cb11b9647a17c29 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 22:57:32 +0100 Subject: [PATCH 028/195] [javascript, xml, haxe modes] Fix strict-mode violations --- mode/haxe/haxe.js | 5 +++-- mode/javascript/javascript.js | 5 +++-- mode/xml/xml.js | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mode/haxe/haxe.js b/mode/haxe/haxe.js index 3f78a8d674..53d7b1d159 100644 --- a/mode/haxe/haxe.js +++ b/mode/haxe/haxe.js @@ -245,11 +245,12 @@ CodeMirror.defineMode("haxe", function(config, parserConfig) { poplex.lex = true; function expect(wanted) { - return function(type) { + function f(type) { if (type == wanted) return cont(); else if (wanted == ";") return pass(); - else return cont(arguments.callee); + else return cont(f); }; + return f; } function statement(type) { diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index 8d6035de2c..290c31fa6c 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -305,11 +305,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { poplex.lex = true; function expect(wanted) { - return function(type) { + function exp(type) { if (type == wanted) return cont(); else if (wanted == ";") return pass(); - else return cont(arguments.callee); + else return cont(exp); }; + return exp; } function statement(type, value) { diff --git a/mode/xml/xml.js b/mode/xml/xml.js index 2e62b88346..ea5fe61b13 100644 --- a/mode/xml/xml.js +++ b/mode/xml/xml.js @@ -229,6 +229,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return baseState; } } + function closeState(type, _stream, state) { if (type != "endTag") { setStyle = "error"; From 90450a9ee98dd6363b115c5524d8ab659836b13d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 23:10:02 +0100 Subject: [PATCH 029/195] Fix bug in triple-click handling Closes #1953 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c4adf99f65..61a87538df 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1928,7 +1928,7 @@ if (type == "double") var range = findWordAt(getLine(doc, pos.line).text, pos); else - var range = {from: Pos(pos.line, 0), to: clipPos(pos.line + 1, 0)}; + var range = {from: Pos(pos.line, 0), to: clipPos(doc, Pos(pos.line + 1, 0))}; if (cmp(range.from, anchor) < 0) { head = range.from; anchor = maxPos(oldRange.to(), range.to); From 269e3ff963df837b5e054060e48b6f20e5cc67a9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 13 Nov 2013 23:10:49 +0100 Subject: [PATCH 030/195] Get rid of stray remaining copyPos call --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 61a87538df..f1a471029d 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -5230,7 +5230,7 @@ else if (start == "anchor") pos = range.anchor; else if (start == "end" || start == "to" || start === false) pos = range.to(); else pos = range.from(); - return copyPos(pos); + return pos; }, listSelections: function() { return this.sel.ranges; }, somethingSelected: function() {return this.sel.somethingSelected();}, From 7090abb14c1be99e54a40dcdaf9c862cf4623fcd Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 14 Nov 2013 13:36:50 +0100 Subject: [PATCH 031/195] Define 'singleSelection' command, bind Esc to it by default Issue #778 --- keymap/multiselect.js | 5 ----- lib/codemirror.js | 6 +++++- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/keymap/multiselect.js b/keymap/multiselect.js index 46379484a6..c258685ae3 100644 --- a/keymap/multiselect.js +++ b/keymap/multiselect.js @@ -36,11 +36,6 @@ cm.addSelection(cur.from(), cur.to()); }; - // Go to a single selection - pc["Alt-Esc"] = mac["Alt-Esc"] = function(cm) { - cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head")); - }; - // Select matching parts of current selection var dialogText = 'Match: (Use /re/ syntax for regexp)'; pc["Alt-F"] = mac["Alt-F"] = function(cm) { diff --git a/lib/codemirror.js b/lib/codemirror.js index f1a471029d..6e4bf30ba8 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3760,6 +3760,9 @@ var commands = CodeMirror.commands = { selectAll: function(cm) {cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()));}, + singleSelection: function(cm) { + cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head")); + }, killLine: function(cm) { deleteNearSelection(cm, function(range) { if (range.empty()) { @@ -3879,7 +3882,8 @@ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", - "Enter": "newlineAndIndent", "Insert": "toggleOverwrite" + "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", + "Esc": "singleSelection" }; // Note that the save and find-related commands aren't defined by // default. Unknown commands are simply ignored. From 25278eaf7285e6c770378f56e2aa17c78f35f8e9 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 14 Nov 2013 13:38:09 +0100 Subject: [PATCH 032/195] [multiselect demo] Update binding list to reflect new Esc binding --- demo/multiselect.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/multiselect.html b/demo/multiselect.html index 3babdb9353..aff0aebeb8 100644 --- a/demo/multiselect.html +++ b/demo/multiselect.html @@ -38,7 +38,7 @@ Ctrl-D / Cmd-D: Add next occurrence of current selection as another selection. -Alt-Esc / Alt-Esc: +Esc: Deselect all but the primary selection. Alt-F: From 59941dadc24891303afd104b9327f8bf5fce17cc Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 16 Nov 2013 10:56:58 +0100 Subject: [PATCH 033/195] Check for empty history in setSelectionAddToHistory Issue #1961 --- lib/codemirror.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 6e4bf30ba8..ad4fdabee7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2734,7 +2734,8 @@ function setSelectionAddToHistory(doc, sel) { setSelection(doc, sel); - lst(doc.history.done).selAfter = sel; + var lstSel = lst(doc.history.done); + if (lstSel) lstSel.selAfter = sel; } function filterSelectionChange(doc, sel) { From 6d14685784908e76cdbebd542bea3a33ed7e2409 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 19 Nov 2013 19:03:19 +0100 Subject: [PATCH 034/195] Fix right-click menu --- lib/codemirror.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index ad4fdabee7..c4ddc491ac 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2349,8 +2349,8 @@ // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; - if (reset && !cm.doc.sel.contains(pos) > -1) - operation(cm, setSelection)(cm.doc, pos, pos); + if (reset && cm.doc.sel.contains(pos) == -1) + operation(cm, setSelection)(cm.doc, simpleSelection(pos)); var oldCSS = display.input.style.cssText; display.inputDiv.style.position = "absolute"; From 25aee733d7a8af3cb1e31f19496ed4274b13afa4 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 25 Nov 2013 16:23:53 +0100 Subject: [PATCH 035/195] Remove stray call to ES5-style indexOf Closes #1989 --- lib/codemirror.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index c4ddc491ac..22860064a4 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -821,7 +821,7 @@ function normalizeSelection(ranges, primIndex) { var prim = ranges[primIndex]; ranges.sort(function(a, b) { return cmp(a.from(), b.from()); }); - primIndex = ranges.indexOf(prim); + primIndex = indexOf(ranges, prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; if (cmp(prev.to(), cur.from()) >= 0) { From 07a8e563d6e5d960346e8ee03f8cdd50a3b05f37 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 29 Nov 2013 21:51:16 +0100 Subject: [PATCH 036/195] [gfm and rst modes] Mark dependencies on overlay addon Closes #2015 --- mode/gfm/gfm.js | 4 ++-- mode/rst/rst.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js index 7bd5f5ab42..8fcc9f70a7 100644 --- a/mode/gfm/gfm.js +++ b/mode/gfm/gfm.js @@ -1,8 +1,8 @@ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror"), require("../markdown/markdown")); + mod(require("../../lib/codemirror"), require("../markdown/markdown"), require("../../addon/mode/overlay")); else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror", "../markdown/markdown"], mod); + define(["../../lib/codemirror", "../markdown/markdown", "../../addon/mode/overlay"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { diff --git a/mode/rst/rst.js b/mode/rst/rst.js index c53cbc36e8..01c6a05650 100644 --- a/mode/rst/rst.js +++ b/mode/rst/rst.js @@ -1,8 +1,8 @@ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror"), require("../python/python"), require("../stex/stex")); + mod(require("../../lib/codemirror"), require("../python/python"), require("../stex/stex"), require("../../addon/mode/overlay")); else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror", "../python/python", "../stex/stex"], mod); + define(["../../lib/codemirror", "../python/python", "../stex/stex", "../../addon/mode/overlay"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { From 0014d6621c986ebb06afebf8a2a751b855835da8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 9 Dec 2013 18:53:26 +0100 Subject: [PATCH 037/195] Fix unintended change in anchor/head orientation on double-click Issue #2038 --- lib/codemirror.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 22860064a4..3c790f88e9 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1929,12 +1929,12 @@ var range = findWordAt(getLine(doc, pos.line).text, pos); else var range = {from: Pos(pos.line, 0), to: clipPos(doc, Pos(pos.line + 1, 0))}; - if (cmp(range.from, anchor) < 0) { - head = range.from; - anchor = maxPos(oldRange.to(), range.to); - } else { + if (cmp(range.to, anchor) > 0) { head = range.to; anchor = minPos(oldRange.from(), range.from); + } else { + head = range.from; + anchor = maxPos(oldRange.to(), range.to); } } var ranges = startSel.ranges.slice(0); From c35d06a8d5a4070f64cf05e356f7679e947646d8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 12 Dec 2013 13:51:28 +0100 Subject: [PATCH 038/195] Remove onDragEvent and onKeyEvent options --- doc/manual.html | 37 +------------------------------------ doc/upgrade_v4.html | 8 +++++--- lib/codemirror.js | 21 +++++---------------- 3 files changed, 11 insertions(+), 55 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index 9a2424eb48..88ed81ef70 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -361,38 +361,6 @@

    Module loaders

    dragDrop: boolean
    Controls whether drag-and-drop is enabled. On by default.
    -
    onDragEvent: function(instance: CodeMirror, event: Event) → boolean
    -
    Deprecated! See these event - handlers for the current recommended approach.
    When given, - this will be called when the editor is handling - a dragenter, dragover, - or drop event. It will be passed the editor - instance and the event object as arguments. The callback can - choose to handle the event itself, in which case it should - return true to indicate that CodeMirror should not - do anything further.
    - -
    onKeyEvent: function(instance: CodeMirror, event: Event) → boolean
    -
    Deprecated! See these event - handlers for the current recommended approach.
    This - provides a rather low-level hook into CodeMirror's key handling. - If provided, this function will be called on - every keydown, keyup, - and keypress event that CodeMirror captures. It - will be passed two arguments, the editor instance and the key - event. This key event is pretty much the raw key event, except - that a stop() method is always added to it. You - could feed it to, for example, jQuery.Event to - further normalize it.
    This function can inspect the key - event, and handle it if it wants to. It may return true to tell - CodeMirror to ignore the event. Be wary that, on some browsers, - stopping a keydown does not stop - the keypress from firing, whereas on others it - does. If you respond to an event, you should probably inspect - its type property and only do something when it - is keydown (or keypress for actions - that need character data).
    -
    cursorBlinkRate: number
    Half-period in milliseconds used for cursor blinking. The default blink rate is 530ms. By setting this to zero, blinking can be disabled.
    @@ -414,16 +382,13 @@

    Module loaders

    click outside of the current selection, the cursor is moved to the point of the click. Defaults to true. -
    workTime, workDelay: number
    +
    workTime, workDelay: number
    Highlighting is done by a pseudo background-thread that will work for workTime milliseconds, and then use timeout to sleep for workDelay milliseconds. The defaults are 200 and 300, you can change these options to make the highlighting more or less aggressive.
    -
    workDelay: number
    -
    See workTime.
    -
    pollInterval: number
    Indicates how quickly CodeMirror should poll its input textarea for changes (when focused). Most input is captured by diff --git a/doc/upgrade_v4.html b/doc/upgrade_v4.html index d390228682..55dbbda305 100644 --- a/doc/upgrade_v4.html +++ b/doc/upgrade_v4.html @@ -82,10 +82,12 @@

    Upgrading to version 4

    Deprecated interfaces dropped

    -

    A few properties that have been deprecated for a while are now -gone. Most notably, the names for folding and completing functions +

    A few properties and methods that have been deprecated for a while +are now gone. Most notably, the onKeyEvent and onDragEvent options (use the corresponding events instead).

    + +

    The long names for folding and completing functions (CodeMirror.braceRangeFinder, CodeMirror.javascriptHint, -etc) are now gone +etc) are also gone (use CodeMirror.fold.brace, CodeMirror.hint.javascript).

    The className property in the return value diff --git a/lib/codemirror.js b/lib/codemirror.js index 3c790f88e9..aaa989dfc6 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1719,7 +1719,7 @@ setTimeout(unregister, 5000); on(d.input, "keyup", operation(cm, function(e) { - if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e)) return; if (e.keyCode == 16) cm.display.shift = false; })); on(d.input, "input", function() { @@ -1732,7 +1732,7 @@ on(d.input, "blur", bind(onBlur, cm)); function drag_(e) { - if (signalDOMEvent(cm, e) || cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e)) return; e_stop(e); } if (cm.options.dragDrop) { @@ -2030,7 +2030,7 @@ function onDrop(e) { var cm = this; - if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e) || (cm.options.onDragEvent && cm.options.onDragEvent(cm, addStop(e)))) + if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) return; e_preventDefault(e); if (ie) lastDrop = +new Date; @@ -2285,12 +2285,11 @@ function onKeyDown(e) { var cm = this; if (!cm.state.focused) onFocus(cm); - if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e)) return; if (old_ie && e.keyCode == 27) e.returnValue = false; var code = e.keyCode; // IE does strange things with escape. cm.display.shift = code == 16 || e.shiftKey; - // First give onKeyEvent option a chance to handle this. var handled = handleKeyBinding(cm, e); if (opera) { lastStoppedKey = handled ? code : null; @@ -2302,7 +2301,7 @@ function onKeyPress(e) { var cm = this; - if (signalDOMEvent(cm, e) || cm.options.onKeyEvent && cm.options.onKeyEvent(cm, addStop(e))) return; + if (signalDOMEvent(cm, e)) return; var keyCode = e.keyCode, charCode = e.charCode; if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(cm, e)) return; @@ -3576,9 +3575,6 @@ option("keyMap", "default", keyMapChanged); option("extraKeys", null); - option("onKeyEvent", null); - option("onDragEvent", null); - option("lineWrapping", false, wrappingChanged, true); option("gutters", [], function(cm) { setGuttersForLineNumbers(cm.options); @@ -5738,13 +5734,6 @@ // EVENT OPERATORS - function stopMethod() {e_stop(this);} - // Ensure an event has a stop method. - function addStop(event) { - if (!event.stop) event.stop = stopMethod; - return event; - } - function e_preventDefault(e) { if (e.preventDefault) e.preventDefault(); else e.returnValue = false; From 06220807ee4d80f7142ff3c343a360d19b4b74a5 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 13 Dec 2013 16:12:13 +0100 Subject: [PATCH 039/195] Change TextMarker.find to allow more control over result And use this to cut some indirection in its callers. --- lib/codemirror.js | 111 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index aaa989dfc6..5996ecb111 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -283,19 +283,19 @@ gutters.style.display = i ? "" : "none"; } - function lineLength(doc, line) { + function lineLength(line) { if (line.height == 0) return 0; var len = line.text.length, merged, cur = line; while (merged = collapsedSpanAtStart(cur)) { - var found = merged.find(); - cur = getLine(doc, found.from.line); + var found = merged.find(0, true); + cur = found.from.line; len += found.from.ch - found.to.ch; } cur = line; while (merged = collapsedSpanAtEnd(cur)) { - var found = merged.find(); + var found = merged.find(0, true); len -= cur.text.length - found.from.ch; - cur = getLine(doc, found.to.line); + cur = found.to.line; len += cur.text.length - found.to.ch; } return len; @@ -304,10 +304,10 @@ function computeMaxLength(cm) { var d = cm.display, doc = cm.doc; d.maxLine = getLine(doc, doc.first); - d.maxLineLength = lineLength(doc, d.maxLine); + d.maxLineLength = lineLength(d.maxLine); d.maxLineChanged = true; doc.iter(function(line) { - var len = lineLength(doc, line); + var len = lineLength(line); if (len > d.maxLineLength) { d.maxLineLength = len; d.maxLine = line; @@ -489,7 +489,7 @@ if (display.showingFrom < from && from - display.showingFrom < 20) from = Math.max(doc.first, display.showingFrom); if (display.showingTo > to && display.showingTo - to < 20) to = Math.min(end, display.showingTo); if (sawCollapsedSpans) { - from = lineNo(visualLine(doc, getLine(doc, from))); + from = lineNo(visualLine(getLine(doc, from))); while (to < end && lineIsHidden(doc, getLine(doc, to))) ++to; } @@ -506,7 +506,7 @@ for (var i = 0; i < intact.length; ++i) { var range = intact[i], merged; while (merged = collapsedSpanAtEnd(getLine(doc, range.to - 1))) { - var newTo = merged.find().from.line; + var newTo = merged.find(-1).line; if (newTo > range.from) range.to = newTo; else { intact.splice(i--, 1); break; } } @@ -577,7 +577,7 @@ } function updateViewOffset(cm) { - var off = cm.display.viewOffset = heightAtLine(cm, getLine(cm.doc, cm.display.showingFrom)); + var off = cm.display.viewOffset = heightAtLine(getLine(cm.doc, cm.display.showingFrom)); // Position the mover div to align with the current virtual scroll position cm.display.mover.style.top = off + "px"; } @@ -951,7 +951,7 @@ drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); - var singleVLine = visualLine(doc, fromLine) == visualLine(doc, toLine); + var singleVLine = visualLine(fromLine) == visualLine(toLine); var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length : null).end; var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { @@ -1258,7 +1258,7 @@ } if (context == "line") return rect; if (!context) context = "local"; - var yOff = heightAtLine(cm, lineObj); + var yOff = heightAtLine(lineObj); if (context == "local") yOff += paddingTop(cm.display); else yOff -= cm.display.viewOffset; if (context == "page" || context == "window") { @@ -1337,25 +1337,25 @@ var doc = cm.doc; y += cm.display.viewOffset; if (y < 0) return PosWithInfo(doc.first, 0, true, -1); - var lineNo = lineAtHeight(doc, y), last = doc.first + doc.size - 1; - if (lineNo > last) + var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; + if (lineN > last) return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, true, 1); if (x < 0) x = 0; + var lineObj = getLine(doc, lineN); for (;;) { - var lineObj = getLine(doc, lineNo); - var found = coordsCharInner(cm, lineObj, lineNo, x, y); + var found = coordsCharInner(cm, lineObj, lineN, x, y); var merged = collapsedSpanAtEnd(lineObj); - var mergedPos = merged && merged.find(); + var mergedPos = merged && merged.find(0, true); if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) - lineNo = mergedPos.to.line; + lineN = lineNo(lineObj = mergedPos.to.line); else return found; } } function coordsCharInner(cm, lineObj, lineNo, x, y) { - var innerOff = y - heightAtLine(cm, lineObj); + var innerOff = y - heightAtLine(lineObj); var wrongLine = false, adjust = 2 * cm.display.wrapper.clientWidth; var measurement = measureLine(cm, lineObj); @@ -2601,7 +2601,7 @@ var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { - checkWidthStart = lineNo(visualLine(doc, getLine(doc, from.line))); + checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function(line) { if (line == display.maxLine) { recomputeMaxLength = true; @@ -2617,7 +2617,7 @@ if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function(line) { - var len = lineLength(doc, line); + var len = lineLength(line); if (len > display.maxLineLength) { display.maxLine = line; display.maxLineLength = len; @@ -2814,7 +2814,7 @@ } } if (!m.atomic) continue; - var newPos = m.find()[dir < 0 ? "from" : "to"]; + var newPos = m.find(dir < 0 ? -1 : 1); if (cmp(newPos, curPos) == 0) { newPos.ch += dir; if (newPos.ch < 0) { @@ -4123,7 +4123,7 @@ updateLineHeight(line, textHeight(cm.display)); } if (cm && this.collapsed && !cm.options.lineWrapping) for (var i = 0; i < this.lines.length; ++i) { - var visual = visualLine(cm.doc, this.lines[i]), len = lineLength(cm.doc, visual); + var visual = visualLine(this.lines[i]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; @@ -4141,28 +4141,31 @@ if (withOp) endOperation(cm); }; - TextMarker.prototype.find = function(bothSides) { + // Side can be 0 (both), -1 (left), 1 (right) + TextMarker.prototype.find = function(side, lineObj) { + if (side == null && this.type == "bookmark") side = 1; var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this); - if (span.from != null || span.to != null) { - var found = lineNo(line); - if (span.from != null) from = Pos(found, span.from); - if (span.to != null) to = Pos(found, span.to); + if (span.from != null) { + from = Pos(lineObj ? line : lineNo(line), span.from); + if (side == -1) return from; + } + if (span.to != null) { + to = Pos(lineObj ? line : lineNo(line), span.to); + if (side == 1) return to; } } - if (this.type == "bookmark" && !bothSides) return from; return from && {from: from, to: to}; }; TextMarker.prototype.changed = function() { - var pos = this.find(), cm = this.doc.cm; + var pos = this.find(-1, true), cm = this.doc.cm; if (!pos || !cm) return; - if (this.type != "bookmark") pos = pos.from; - var line = getLine(this.doc, pos.line); + var line = pos.line, lineN = lineNo(pos.line); clearCachedMeasurement(cm, line); - if (pos.line >= cm.display.showingFrom && pos.line < cm.display.showingTo) { + if (lineN >= cm.display.showingFrom && lineN < cm.display.showingTo) { for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) { if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight); break; @@ -4216,7 +4219,7 @@ var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function(line) { - if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(doc, line) == cm.display.maxLine) + if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) updateMaxLine = true; var span = {from: null, to: null, marker: marker}; if (curLine == from.line) span.from = from.ch; @@ -4269,8 +4272,8 @@ this.markers[i].clear(); signalLater(this, "clear"); }; - SharedTextMarker.prototype.find = function() { - return this.primary.find(); + SharedTextMarker.prototype.find = function(side, lineObj) { + return this.primary.find(side, lineObj); }; function markTextShared(doc, from, to, options, type) { @@ -4437,7 +4440,7 @@ if (!markers) return null; var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { - var mk = markers[i], m = mk.find(); + var mk = markers[i], m = mk.find(0); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) continue; @@ -4486,7 +4489,7 @@ if (sps) for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (!sp.marker.collapsed) continue; - var found = sp.marker.find(true); + var found = sp.marker.find(0); var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) continue; @@ -4496,10 +4499,10 @@ } } - function visualLine(doc, line) { + function visualLine(line) { var merged; while (merged = collapsedSpanAtStart(line)) - line = getLine(doc, merged.find().from.line); + line = merged.find(-1, true).line; return line; } @@ -4516,8 +4519,8 @@ } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { - var end = span.marker.find().to, endLine = getLine(doc, end.line); - return lineIsHiddenInner(doc, endLine, getMarkedSpanFor(endLine.markedSpans, span.marker)); + var end = span.marker.find(1, true); + return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)); } if (span.marker.inclusiveRight && span.to == line.text.length) return true; @@ -4568,7 +4571,7 @@ if (no == null || !ws) return; for (var i = 0; i < ws.length; ++i) if (ws[i] == this) ws.splice(i--, 1); if (!ws.length) this.line.widgets = null; - var aboveVisible = heightAtLine(this.cm, this.line) < this.cm.doc.scrollTop; + var aboveVisible = heightAtLine(this.line) < this.cm.doc.scrollTop; updateLineHeight(this.line, Math.max(0, this.line.height - widgetHeight(this))); if (aboveVisible) addToScrollPos(this.cm, 0, -this.height); regChange(this.cm, no, no + 1); @@ -4599,7 +4602,7 @@ else widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); widget.line = line; if (!lineIsHidden(cm.doc, line) || widget.showIfHidden) { - var aboveVisible = heightAtLine(cm, line) < cm.doc.scrollTop; + var aboveVisible = heightAtLine(line) < cm.doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) addToScrollPos(cm, 0, widget.height); } @@ -4751,7 +4754,7 @@ function buildLineContent(cm, realLine, measure, copyWidgets) { var merged, line = realLine, empty = true; while (merged = collapsedSpanAtStart(line)) - line = getLine(cm.doc, merged.find().from.line); + line = merged.find(-1, true).line; var builder = {pre: elt("pre"), col: 0, pos: 0, measure: null, measuredSomething: false, cm: cm, @@ -5216,7 +5219,7 @@ getLineHandleVisualStart: function(line) { if (typeof line == "number") line = getLine(this, line); - return visualLine(this, line); + return visualLine(line); }, lineCount: function() {return this.size;}, @@ -5534,8 +5537,8 @@ return n + i; } - function heightAtLine(cm, lineObj) { - lineObj = visualLine(cm.doc, lineObj); + function heightAtLine(lineObj) { + lineObj = visualLine(lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { @@ -6112,19 +6115,21 @@ function lineStart(cm, lineN) { var line = getLine(cm.doc, lineN); - var visual = visualLine(cm.doc, line); + var visual = visualLine(line); if (visual != line) lineN = lineNo(visual); var order = getOrder(visual); var ch = !order ? 0 : order[0].level % 2 ? lineRight(visual) : lineLeft(visual); return Pos(lineN, ch); } function lineEnd(cm, lineN) { - var merged, line; - while (merged = collapsedSpanAtEnd(line = getLine(cm.doc, lineN))) - lineN = merged.find().to.line; + var merged, line = getLine(cm.doc, lineN); + while (merged = collapsedSpanAtEnd(line)) { + line = merged.find(1, true).line; + lineN = null; + } var order = getOrder(line); var ch = !order ? line.text.length : order[0].level % 2 ? lineLeft(line) : lineRight(line); - return Pos(lineN, ch); + return Pos(lineN == null ? lineNo(line) : lineN, ch); } function compareBidiLevel(order, a, b) { From b9c1db55da9e2b0228d2ecc4804de7b6764580ad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 13 Dec 2013 20:37:50 +0100 Subject: [PATCH 040/195] Upgrade in-tree acorn --- test/lint/acorn.js | 636 ++++++++++++++++++++++++++++++++++------------------- test/lint/walk.js | 203 ++++++++++++----- 2 files changed, 555 insertions(+), 284 deletions(-) diff --git a/test/lint/acorn.js b/test/lint/acorn.js index 6323b1fc6a..bbd32cfcea 100644 --- a/test/lint/acorn.js +++ b/test/lint/acorn.js @@ -12,13 +12,24 @@ // Please use the [github bug tracker][ghbt] to report issues. // // [ghbt]: https://github.com/marijnh/acorn/issues - -(function(exports) { +// +// This file defines the main parser interface. The library also comes +// with a [error-tolerant parser][dammit] and an +// [abstract syntax tree walker][walk], defined in other files. +// +// [dammit]: acorn_loose.js +// [walk]: util/walk.js + +(function(root, mod) { + if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS + if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD + mod(root.acorn || (root.acorn = {})); // Plain browser env +})(this, function(exports) { "use strict"; - exports.version = "0.0.1"; + exports.version = "0.4.1"; - // The main exported interface (under `window.acorn` when in the + // The main exported interface (under `self.acorn` when in the // browser) is a `parse` function that takes a code string and // returns an abstract syntax tree as specified by [Mozilla parser // API][api], with the caveat that the SpiderMonkey-specific syntax @@ -30,10 +41,8 @@ exports.parse = function(inpt, opts) { input = String(inpt); inputLen = input.length; - options = opts || {}; - for (var opt in defaultOptions) if (!options.hasOwnProperty(opt)) - options[opt] = defaultOptions[opt]; - sourceFile = options.sourceFile || null; + setOptions(opts); + initTokenState(); return parseTopLevel(options.program); }; @@ -55,18 +64,22 @@ // By default, reserved words are not enforced. Enable // `forbidReserved` to enforce them. forbidReserved: false, - // When `trackComments` is turned on, the parser will attach - // `commentsBefore` and `commentsAfter` properties to AST nodes - // holding arrays of strings. A single comment may appear in both - // a `commentsBefore` and `commentsAfter` array (of the nodes - // after and before it), but never twice in the before (or after) - // array of different nodes. - trackComments: false, // When `locations` is on, `loc` properties holding objects with // `start` and `end` properties in `{line, column}` form (with // line being 1-based and column 0-based) will be attached to the // nodes. locations: false, + // A function can be passed as `onComment` option, which will + // cause Acorn to call that function with `(block, text, start, + // end)` parameters whenever a comment is skipped. `block` is a + // boolean indicating whether this is a block (`/* */`) comment, + // `text` is the content of the comment, and `start` and `end` are + // character offsets that denote the start and end of the comment. + // When the `locations` option is on, two more parameters are + // passed, the full `{line, column}` locations of the start and + // end of the comments. Note that you are not allowed to call the + // parser from the callback—that will corrupt its internal state. + onComment: null, // Nodes have their start and end characters offsets recorded in // `start` and `end` properties (directly on the node, rather than // the `loc` object, which holds line/column data. To also add a @@ -82,11 +95,21 @@ // toplevel forms of the parsed file to the `Program` (top) node // of an existing parse tree. program: null, - // When `location` is on, you can pass this to record the source + // When `locations` is on, you can pass this to record the source // file in every node's `loc` object. - sourceFile: null + sourceFile: null, + // This value, if given, is stored in every node, whether + // `locations` is on or off. + directSourceFile: null }; + function setOptions(opts) { + options = opts || {}; + for (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt)) + options[opt] = defaultOptions[opt]; + sourceFile = options.sourceFile || null; + } + // The `getLineInfo` function is mostly useful when the // `locations` option is off (for performance reasons) and you // want to find the line/column position for a given character @@ -106,9 +129,44 @@ }; // Acorn is organized as a tokenizer and a recursive-descent parser. - // Both use (closure-)global variables to keep their state and - // communicate. We already saw the `options`, `input`, and - // `inputLen` variables above (set in `parse`). + // The `tokenize` export provides an interface to the tokenizer. + // Because the tokenizer is optimized for being efficiently used by + // the Acorn parser itself, this interface is somewhat crude and not + // very modular. Performing another parse or call to `tokenize` will + // reset the internal state, and invalidate existing tokenizers. + + exports.tokenize = function(inpt, opts) { + input = String(inpt); inputLen = input.length; + setOptions(opts); + initTokenState(); + + var t = {}; + function getToken(forceRegexp) { + readToken(forceRegexp); + t.start = tokStart; t.end = tokEnd; + t.startLoc = tokStartLoc; t.endLoc = tokEndLoc; + t.type = tokType; t.value = tokVal; + return t; + } + getToken.jumpTo = function(pos, reAllowed) { + tokPos = pos; + if (options.locations) { + tokCurLine = 1; + tokLineStart = lineBreak.lastIndex = 0; + var match; + while ((match = lineBreak.exec(input)) && match.index < pos) { + ++tokCurLine; + tokLineStart = match.index + match[0].length; + } + } + tokRegexpAllowed = reAllowed; + skipSpace(); + }; + return getToken; + }; + + // State is kept in (closure-)global variables. We already saw the + // `options`, `input`, and `inputLen` variables above. // The current position of the tokenizer in the input. @@ -133,11 +191,6 @@ var tokType, tokVal; - // These are used to hold arrays of comments when - // `options.trackComments` is true. - - var tokCommentsBefore, tokCommentsAfter; - // Interal state for the tokenizer. To distinguish between division // operators and regular expressions, it remembers whether the last // token was one that is allowed to be followed by an expression. @@ -145,13 +198,13 @@ // division operator. See the `parseStatement` function for a // caveat.) - var tokRegexpAllowed, tokComments; + var tokRegexpAllowed; // When `options.locations` is true, these are used to keep // track of the current line, and know when a new line has been - // entered. See the `curLineLoc` function. + // entered. - var tokCurLine, tokLineStart, tokLineStartNext; + var tokCurLine, tokLineStart; // These store the position of the previous token, which is useful // when finishing a node and assigning its `end` position. @@ -166,17 +219,23 @@ var inFunction, labels, strict; // This function is used to raise exceptions on parse errors. It - // takes either a `{line, column}` object or an offset integer (into - // the current `input`) as `pos` argument. It attaches the position - // to the end of the error message, and then raises a `SyntaxError` - // with that message. + // takes an offset integer (into the current `input`) to indicate + // the location of the error, attaches the position to the end + // of the error message, and then raises a `SyntaxError` with that + // message. function raise(pos, message) { - if (typeof pos == "number") pos = getLineInfo(input, pos); - message += " (" + pos.line + ":" + pos.column + ")"; - throw new SyntaxError(message); + var loc = getLineInfo(input, pos); + message += " (" + loc.line + ":" + loc.column + ")"; + var err = new SyntaxError(message); + err.pos = pos; err.loc = loc; err.raisedAt = tokPos; + throw err; } + // Reused empty array added for node fields that are always empty. + + var empty = []; + // ## Token types // The assignment of fine-grained, information-carrying type objects @@ -233,10 +292,10 @@ "function": _function, "if": _if, "return": _return, "switch": _switch, "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with, "null": _null, "true": _true, "false": _false, "new": _new, "in": _in, - "instanceof": {keyword: "instanceof", binop: 7}, "this": _this, - "typeof": {keyword: "typeof", prefix: true}, - "void": {keyword: "void", prefix: true}, - "delete": {keyword: "delete", prefix: true}}; + "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this, + "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, + "void": {keyword: "void", prefix: true, beforeExpr: true}, + "delete": {keyword: "delete", prefix: true, beforeExpr: true}}; // Punctuation token types. Again, the `type` property is purely for debugging. @@ -262,13 +321,27 @@ // in AssignmentExpression nodes. var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true}; - var _assign = {isAssign: true, beforeExpr: true}, _plusmin = {binop: 9, prefix: true, beforeExpr: true}; - var _incdec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true}; - var _bin1 = {binop: 1, beforeExpr: true}, _bin2 = {binop: 2, beforeExpr: true}; - var _bin3 = {binop: 3, beforeExpr: true}, _bin4 = {binop: 4, beforeExpr: true}; - var _bin5 = {binop: 5, beforeExpr: true}, _bin6 = {binop: 6, beforeExpr: true}; - var _bin7 = {binop: 7, beforeExpr: true}, _bin8 = {binop: 8, beforeExpr: true}; - var _bin10 = {binop: 10, beforeExpr: true}; + var _assign = {isAssign: true, beforeExpr: true}; + var _incDec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true}; + var _logicalOR = {binop: 1, beforeExpr: true}; + var _logicalAND = {binop: 2, beforeExpr: true}; + var _bitwiseOR = {binop: 3, beforeExpr: true}; + var _bitwiseXOR = {binop: 4, beforeExpr: true}; + var _bitwiseAND = {binop: 5, beforeExpr: true}; + var _equality = {binop: 6, beforeExpr: true}; + var _relational = {binop: 7, beforeExpr: true}; + var _bitShift = {binop: 8, beforeExpr: true}; + var _plusMin = {binop: 9, prefix: true, beforeExpr: true}; + var _multiplyModulo = {binop: 10, beforeExpr: true}; + + // Provide access to the token types for external users of the + // tokenizer. + + exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR, + parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon, + dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof, + num: _num, regexp: _regexp, string: _string}; + for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw]; // This is a trick taken from Esprima. It turns out that, on // non-Chrome browsers, to check whether a string is in a set, a @@ -345,9 +418,9 @@ // are only applied when a character is found to actually have a // code point above 128. - var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]/; + var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; - var nonASCIIidentifierChars = "\u0371-\u0374\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; + var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); @@ -362,17 +435,17 @@ // Test whether a given character code starts an identifier. - function isIdentifierStart(code) { + var isIdentifierStart = exports.isIdentifierStart = function(code) { if (code < 65) return code === 36; if (code < 91) return true; if (code < 97) return code === 95; if (code < 123)return true; return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); - } + }; // Test whether a given character is part of an identifier. - function isIdentifierChar(code) { + var isIdentifierChar = exports.isIdentifierChar = function(code) { if (code < 48) return code === 36; if (code < 58) return true; if (code < 65) return false; @@ -380,27 +453,16 @@ if (code < 97) return code === 95; if (code < 123)return true; return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); - } + }; // ## Tokenizer - // These are used when `options.locations` is on, in order to track - // the current line number and start of line offset, in order to set - // `tokStartLoc` and `tokEndLoc`. - - function nextLineStart() { - lineBreak.lastIndex = tokLineStart; - var match = lineBreak.exec(input); - return match ? match.index + match[0].length : input.length + 1; - } + // These are used when `options.locations` is on, for the + // `tokStartLoc` and `tokEndLoc` properties. - function curLineLoc() { - while (tokLineStartNext <= tokPos) { - ++tokCurLine; - tokLineStart = tokLineStartNext; - tokLineStartNext = nextLineStart(); - } - return {line: tokCurLine, column: tokPos - tokLineStart}; + function line_loc_t() { + this.line = tokCurLine; + this.column = tokPos - tokLineStart; } // Reset the token state. Used at the start of a parse. @@ -408,63 +470,88 @@ function initTokenState() { tokCurLine = 1; tokPos = tokLineStart = 0; - tokLineStartNext = nextLineStart(); tokRegexpAllowed = true; - tokComments = null; skipSpace(); } - // Called at the end of every token. Sets `tokEnd`, `tokVal`, - // `tokCommentsAfter`, and `tokRegexpAllowed`, and skips the space - // after the token, so that the next one's `tokStart` will point at - // the right position. + // Called at the end of every token. Sets `tokEnd`, `tokVal`, and + // `tokRegexpAllowed`, and skips the space after the token, so that + // the next one's `tokStart` will point at the right position. function finishToken(type, val) { tokEnd = tokPos; - if (options.locations) tokEndLoc = curLineLoc(); + if (options.locations) tokEndLoc = new line_loc_t; tokType = type; skipSpace(); tokVal = val; - tokCommentsAfter = tokComments; tokRegexpAllowed = type.beforeExpr; } function skipBlockComment() { - var end = input.indexOf("*/", tokPos += 2); + var startLoc = options.onComment && options.locations && new line_loc_t; + var start = tokPos, end = input.indexOf("*/", tokPos += 2); if (end === -1) raise(tokPos - 2, "Unterminated comment"); - if (options.trackComments) - (tokComments || (tokComments = [])).push(input.slice(tokPos, end)); tokPos = end + 2; + if (options.locations) { + lineBreak.lastIndex = start; + var match; + while ((match = lineBreak.exec(input)) && match.index < tokPos) { + ++tokCurLine; + tokLineStart = match.index + match[0].length; + } + } + if (options.onComment) + options.onComment(true, input.slice(start + 2, end), start, tokPos, + startLoc, options.locations && new line_loc_t); } function skipLineComment() { var start = tokPos; + var startLoc = options.onComment && options.locations && new line_loc_t; var ch = input.charCodeAt(tokPos+=2); - while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8329) { + while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { ++tokPos; ch = input.charCodeAt(tokPos); } - (tokComments || (tokComments = [])).push(input.slice(start, tokPos)); + if (options.onComment) + options.onComment(false, input.slice(start + 2, tokPos), start, tokPos, + startLoc, options.locations && new line_loc_t); } // Called at the start of the parse and after every token. Skips - // whitespace and comments, and, if `options.trackComments` is on, - // will store all skipped comments in `tokComments`. + // whitespace and comments, and. function skipSpace() { - tokComments = null; while (tokPos < inputLen) { var ch = input.charCodeAt(tokPos); - if (ch === 47) { // '/' - var next = input.charCodeAt(tokPos+1); + if (ch === 32) { // ' ' + ++tokPos; + } else if (ch === 13) { + ++tokPos; + var next = input.charCodeAt(tokPos); + if (next === 10) { + ++tokPos; + } + if (options.locations) { + ++tokCurLine; + tokLineStart = tokPos; + } + } else if (ch === 10 || ch === 8232 || ch === 8233) { + ++tokPos; + if (options.locations) { + ++tokCurLine; + tokLineStart = tokPos; + } + } else if (ch > 8 && ch < 14) { + ++tokPos; + } else if (ch === 47) { // '/' + var next = input.charCodeAt(tokPos + 1); if (next === 42) { // '*' skipBlockComment(); } else if (next === 47) { // '/' skipLineComment(); } else break; - } else if (ch < 14 && ch > 8) { - ++tokPos; - } else if (ch === 32 || ch === 160) { // ' ', '\xa0' + } else if (ch === 160) { // '\xa0' ++tokPos; } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { ++tokPos; @@ -486,26 +573,89 @@ // The `forceRegexp` parameter is used in the one case where the // `tokRegexpAllowed` trick does not work. See `parseStatement`. - function readToken(forceRegexp) { - tokStart = tokPos; - if (options.locations) tokStartLoc = curLineLoc(); - tokCommentsBefore = tokComments; - if (forceRegexp) return readRegexp(); - if (tokPos >= inputLen) return finishToken(_eof); + function readToken_dot() { + var next = input.charCodeAt(tokPos + 1); + if (next >= 48 && next <= 57) return readNumber(true); + ++tokPos; + return finishToken(_dot); + } - var code = input.charCodeAt(tokPos); - // Identifier or keyword. '\uXXXX' sequences are allowed in - // identifiers, so '\' also dispatches to that. - if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord(); - var next = input.charCodeAt(tokPos+1); + function readToken_slash() { // '/' + var next = input.charCodeAt(tokPos + 1); + if (tokRegexpAllowed) {++tokPos; return readRegexp();} + if (next === 61) return finishOp(_assign, 2); + return finishOp(_slash, 1); + } + + function readToken_mult_modulo() { // '%*' + var next = input.charCodeAt(tokPos + 1); + if (next === 61) return finishOp(_assign, 2); + return finishOp(_multiplyModulo, 1); + } + + function readToken_pipe_amp(code) { // '|&' + var next = input.charCodeAt(tokPos + 1); + if (next === code) return finishOp(code === 124 ? _logicalOR : _logicalAND, 2); + if (next === 61) return finishOp(_assign, 2); + return finishOp(code === 124 ? _bitwiseOR : _bitwiseAND, 1); + } + + function readToken_caret() { // '^' + var next = input.charCodeAt(tokPos + 1); + if (next === 61) return finishOp(_assign, 2); + return finishOp(_bitwiseXOR, 1); + } + + function readToken_plus_min(code) { // '+-' + var next = input.charCodeAt(tokPos + 1); + if (next === code) { + if (next == 45 && input.charCodeAt(tokPos + 2) == 62 && + newline.test(input.slice(lastEnd, tokPos))) { + // A `-->` line comment + tokPos += 3; + skipLineComment(); + skipSpace(); + return readToken(); + } + return finishOp(_incDec, 2); + } + if (next === 61) return finishOp(_assign, 2); + return finishOp(_plusMin, 1); + } + function readToken_lt_gt(code) { // '<>' + var next = input.charCodeAt(tokPos + 1); + var size = 1; + if (next === code) { + size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2; + if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1); + return finishOp(_bitShift, size); + } + if (next == 33 && code == 60 && input.charCodeAt(tokPos + 2) == 45 && + input.charCodeAt(tokPos + 3) == 45) { + // `