From 0d83ad4dc08448e016c79ae739e84c3d71552b56 Mon Sep 17 00:00:00 2001 From: Ananya Sen Date: Tue, 27 Mar 2012 17:19:18 -0700 Subject: - Highlight Matching bracket - Highlight selection matched -Highlight current line Signed-off-by: Ananya Sen --- index.html | 12 ++++++++++++ js/stage/stage-view.reel/stage-view.js | 32 ++++++++++++++++++-------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/index.html b/index.html index d385e4ba..37daee4b 100755 --- a/index.html +++ b/index.html @@ -183,6 +183,8 @@ + + @@ -199,6 +201,16 @@ } } + + diff --git a/js/stage/stage-view.reel/stage-view.js b/js/stage/stage-view.reel/stage-view.js index ad67cada..66a18964 100755 --- a/js/stage/stage-view.reel/stage-view.js +++ b/js/stage/stage-view.reel/stage-view.js @@ -93,23 +93,27 @@ exports.StageView = Montage.create(Component, { var documentController = this.application.ninja.documentController; doc.editor = CodeMirror.fromTextArea(doc.textArea, { - lineNumbers: true, - mode: type, - onChange: function(){ - var historySize = doc.editor.historySize(); - if(historySize.undo>0){ - doc.needsSave = true; - }else if(historySize.undo===0 && historySize.redo>0){ - doc.needsSave = false; - } - }, - onCursorActivity: function() { - //documentController._codeEditor.editor.setLineClass(documentController._codeEditor.hline, null); - //documentController._codeEditor.hline = documentController._codeEditor.editor.setLineClass(documentController._codeEditor.editor.getCursor().line, "activeline"); + lineNumbers: true, + lineWrapping: true, + matchBrackets:true, + mode: type, + onChange: function(){ + var historySize = doc.editor.historySize(); + if(historySize.undo>0){ + doc.needsSave = true; + }else if(historySize.undo===0 && historySize.redo>0){ + doc.needsSave = false; } + }, + onCursorActivity: function() { + doc.editor.matchHighlight("CodeMirror-matchhighlight"); + doc.editor.setLineClass(doc.editor.hline, null); + doc.editor.hline = doc.editor.setLineClass(doc.editor.getCursor().line, "activeline"); + } }); - //this.application.ninja.documentController._codeEditor.hline = this.application.ninja.documentController._codeEditor.editor.setLineClass(0, "activeline"); + doc.editor.hline = doc.editor.setLineClass(0, "activeline"); + this.application.ninja.stage._scrollFlag = false; // TODO HACK to prevent type error on Hide/Show Iframe this.application.ninja.documentController.activeDocument = doc; this.application.ninja.stage.hideCanvas(true); -- cgit v1.2.3 From 4b1b72971567ea569a10c740e26aee33421e7bd5 Mon Sep 17 00:00:00 2001 From: Ananya Sen Date: Tue, 3 Apr 2012 15:07:33 -0700 Subject: adding basic autocomplete Signed-off-by: Ananya Sen --- index.html | 6 ++++-- js/stage/stage-view.reel/stage-view.js | 6 +++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 37daee4b..fe52167d 100755 --- a/index.html +++ b/index.html @@ -21,6 +21,7 @@ + diff --git a/js/stage/stage-view.reel/stage-view.js b/js/stage/stage-view.reel/stage-view.js index 28a66396..43751559 100755 --- a/js/stage/stage-view.reel/stage-view.js +++ b/js/stage/stage-view.reel/stage-view.js @@ -110,10 +110,19 @@ exports.StageView = Montage.create(Component, { doc.editor.setLineClass(doc.editor.hline, null); doc.editor.hline = doc.editor.setLineClass(doc.editor.getCursor().line, "activeline"); }, - extraKeys: {"Ctrl-Space": function(cm) { - CodeMirror.simpleHint(cm, CodeMirror.javascriptHint); - } - } + //extraKeys: {"Ctrl-Space": function(cm) {CodeMirror.simpleHint(cm, CodeMirror.javascriptHint);}} + onKeyEvent: function(cm, keyEvent) { + if((keyEvent.type === "keyup")//need seperate keycode set per mode + && ((keyEvent.keyCode > 47 && keyEvent.keyCode < 57)//numbers + || (keyEvent.keyCode > 64 && keyEvent.keyCode <91)//letters + || (keyEvent.keyCode === 190)//period + || (keyEvent.keyCode === 189)//underscore, dash + ) + ){ + + CodeMirror.simpleHint(cm, CodeMirror.javascriptHint); + } + } }); doc.editor.hline = doc.editor.setLineClass(0, "activeline"); -- cgit v1.2.3 From 671d2b94996ac71d56b2d05d5fe694ed7d84fa59 Mon Sep 17 00:00:00 2001 From: Ananya Sen Date: Wed, 4 Apr 2012 12:27:35 -0700 Subject: upgrading to CodeMirror 2.23, since it has an important fix to Not close Ninja, if backspace is pressed while autocomplete dropdown is open. Signed-off-by: Ananya Sen --- imports/codemirror/lib/codemirror.css | 2 + imports/codemirror/lib/codemirror.js | 247 ++++++++---- imports/codemirror/lib/util/closetag.js | 174 ++++++++ imports/codemirror/lib/util/foldcode.js | 2 +- imports/codemirror/lib/util/formatting.js | 2 +- imports/codemirror/lib/util/javascript-hint.js | 6 +- imports/codemirror/lib/util/simple-hint.js | 6 + imports/codemirror/mode/clike/index.html | 2 +- imports/codemirror/mode/clojure/clojure.js | 26 +- imports/codemirror/mode/css/css.js | 2 +- imports/codemirror/mode/htmlmixed/htmlmixed.js | 2 +- imports/codemirror/mode/javascript/javascript.js | 6 +- imports/codemirror/mode/less/index.html | 39 +- imports/codemirror/mode/less/less.js | 143 ++++--- imports/codemirror/mode/markdown/index.html | 1 - imports/codemirror/mode/markdown/markdown.js | 89 ++-- imports/codemirror/mode/properties/index.html | 4 +- imports/codemirror/mode/properties/properties.css | 3 - imports/codemirror/mode/properties/properties.js | 28 +- imports/codemirror/mode/scheme/scheme.js | 402 +++++++++--------- imports/codemirror/mode/smarty/index.html | 82 ++++ imports/codemirror/mode/smarty/smarty.js | 148 +++++++ imports/codemirror/mode/stex/stex.js | 17 +- imports/codemirror/mode/stex/test.html | 251 ++++++++++++ imports/codemirror/mode/tiddlywiki/index.html | 69 +--- imports/codemirror/mode/tiddlywiki/tiddlywiki.css | 2 +- imports/codemirror/mode/tiddlywiki/tiddlywiki.js | 72 ++-- imports/codemirror/mode/vbscript/index.html | 42 ++ imports/codemirror/mode/vbscript/vbscript.js | 26 ++ imports/codemirror/mode/xml/xml.js | 3 +- imports/codemirror/mode/xmlpure/index.html | 1 - imports/codemirror/mode/xquery/LICENSE | 20 + imports/codemirror/mode/xquery/index.html | 222 ++++++++++ imports/codemirror/mode/xquery/test/index.html | 27 ++ imports/codemirror/mode/xquery/test/testBase.js | 42 ++ .../mode/xquery/test/testEmptySequenceKeyword.js | 16 + .../codemirror/mode/xquery/test/testMultiAttr.js | 16 + .../codemirror/mode/xquery/test/testNamespaces.js | 91 +++++ .../mode/xquery/test/testProcessingInstructions.js | 16 + imports/codemirror/mode/xquery/test/testQuotes.js | 19 + imports/codemirror/mode/xquery/xquery.js | 448 +++++++++++++++++++++ imports/codemirror/theme/eclipse.css | 2 +- imports/codemirror/theme/elegant.css | 4 +- imports/codemirror/theme/lesser-dark.css | 45 +++ imports/codemirror/theme/neat.css | 6 +- imports/codemirror/theme/rubyblue.css | 2 +- imports/codemirror/theme/xq-dark.css | 46 +++ imports/codemirror/version.txt | 2 +- 48 files changed, 2370 insertions(+), 553 deletions(-) create mode 100644 imports/codemirror/lib/util/closetag.js delete mode 100755 imports/codemirror/mode/properties/properties.css create mode 100644 imports/codemirror/mode/smarty/index.html create mode 100644 imports/codemirror/mode/smarty/smarty.js create mode 100644 imports/codemirror/mode/stex/test.html create mode 100644 imports/codemirror/mode/vbscript/index.html create mode 100644 imports/codemirror/mode/vbscript/vbscript.js create mode 100644 imports/codemirror/mode/xquery/LICENSE create mode 100644 imports/codemirror/mode/xquery/index.html create mode 100644 imports/codemirror/mode/xquery/test/index.html create mode 100644 imports/codemirror/mode/xquery/test/testBase.js create mode 100644 imports/codemirror/mode/xquery/test/testEmptySequenceKeyword.js create mode 100644 imports/codemirror/mode/xquery/test/testMultiAttr.js create mode 100644 imports/codemirror/mode/xquery/test/testNamespaces.js create mode 100644 imports/codemirror/mode/xquery/test/testProcessingInstructions.js create mode 100644 imports/codemirror/mode/xquery/test/testQuotes.js create mode 100644 imports/codemirror/mode/xquery/xquery.js create mode 100644 imports/codemirror/theme/lesser-dark.css create mode 100644 imports/codemirror/theme/xq-dark.css diff --git a/imports/codemirror/lib/codemirror.css b/imports/codemirror/lib/codemirror.css index 5eadb247..2d79f4aa 100644 --- a/imports/codemirror/lib/codemirror.css +++ b/imports/codemirror/lib/codemirror.css @@ -9,6 +9,7 @@ /* This is needed to prevent an IE[67] bug where the scrolled content is visible outside of the scrolling box. */ position: relative; + outline: none; } .CodeMirror-gutter { @@ -27,6 +28,7 @@ } .CodeMirror-lines { padding: .4em; + white-space: pre; } .CodeMirror pre { diff --git a/imports/codemirror/lib/codemirror.js b/imports/codemirror/lib/codemirror.js index 9c6e65e4..5434a8dd 100644 --- a/imports/codemirror/lib/codemirror.js +++ b/imports/codemirror/lib/codemirror.js @@ -1,4 +1,4 @@ -// CodeMirror version 2.22 +// CodeMirror version 2.23 // // All functions that need access to the editor's state live inside // the CodeMirror function. Below that, at the bottom of the file, @@ -15,9 +15,8 @@ var CodeMirror = (function() { if (defaults.hasOwnProperty(opt)) options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; - var targetDocument = options["document"]; // The element in which the editor lives. - var wrapper = targetDocument.createElement("div"); + var wrapper = document.createElement("div"); wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""); // This mess creates the base DOM structure for the editor. wrapper.innerHTML = @@ -48,7 +47,10 @@ var CodeMirror = (function() { if (!webkit) lineSpace.draggable = true; lineSpace.style.outline = "none"; if (options.tabindex != null) input.tabIndex = options.tabindex; + if (options.autofocus) focusInput(); if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; + // Needed to handle Tab key in KHTML + if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; // Check for problem with IE innerHTML not working when we have a // P (or similar) parent node. @@ -81,12 +83,13 @@ var CodeMirror = (function() { gutterDirty, callbacks; // Current visible range (may be bigger than the view window). var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; - // bracketHighlighted is used to remember that a backet has been + // bracketHighlighted is used to remember that a bracket has been // marked. var bracketHighlighted; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. var maxLine = "", maxWidth; + var tabCache = {}; // Initialize the content. operation(function(){setValue(options.value || ""); updateInput = false;})(); @@ -124,10 +127,16 @@ var CodeMirror = (function() { if (!options.readOnly) replaceSelection(""); })); + // Needed to handle Tab key in KHTML + if (khtml) connect(code, "mouseup", function() { + if (document.activeElement == input) input.blur(); + focusInput(); + }); + // IE throws unspecified error in certain cases, when // trying to access activeElement before onload - var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { } - if (hasFocus) setTimeout(onFocus, 20); + var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } + if (hasFocus || options.autofocus) setTimeout(onFocus, 20); else onBlur(); function isLine(l) {return l >= 0 && l < doc.size;} @@ -178,17 +187,23 @@ var CodeMirror = (function() { line = clipLine(line == null ? doc.size - 1: line); return getStateBefore(line + 1); }, - cursorCoords: function(start){ + cursorCoords: function(start, mode) { if (start == null) start = sel.inverted; - return pageCoords(start ? sel.from : sel.to); + return this.charCoords(start ? sel.from : sel.to, mode); + }, + charCoords: function(pos, mode) { + pos = clipPos(pos); + if (mode == "local") return localCoords(pos, false); + if (mode == "div") return localCoords(pos, true); + return pageCoords(pos); }, - charCoords: function(pos){return pageCoords(clipPos(pos));}, coordsChar: function(coords) { var off = eltOffset(lineSpace); return coordsChar(coords.x - off.left, coords.y - off.top); }, markText: operation(markText), setBookmark: setBookmark, + findMarksAt: findMarksAt, setMarker: operation(addGutterMarker), clearMarker: operation(removeGutterMarker), setLineClass: operation(setLineClass), @@ -256,6 +271,7 @@ var CodeMirror = (function() { replaceRange: operation(replaceRange), getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));}, + triggerOnKeyDown: operation(onKeyDown), execCommand: function(cmd) {return commands[cmd](instance);}, // Stuff used by commands, probably not much use to outside code. moveH: operation(moveH), @@ -373,7 +389,7 @@ var CodeMirror = (function() { !posLess(start, sel.from) && !posLess(sel.to, start)) { // Let the drag handler handle this. if (webkit) lineSpace.draggable = true; - var up = connect(targetDocument, "mouseup", operation(function(e2) { + var up = connect(document, "mouseup", operation(function(e2) { if (webkit) lineSpace.draggable = false; draggingText = false; up(); @@ -384,6 +400,8 @@ var CodeMirror = (function() { } }), true); draggingText = true; + // IE's approach to draggable + if (lineSpace.dragDrop) lineSpace.dragDrop(); return; } e_preventDefault(e); @@ -402,12 +420,7 @@ var CodeMirror = (function() { } } - var move = connect(targetDocument, "mousemove", operation(function(e) { - clearTimeout(going); - e_preventDefault(e); - extend(e); - }), true); - var up = connect(targetDocument, "mouseup", operation(function(e) { + function done(e) { clearTimeout(going); var cur = posFromMouse(e); if (cur) setSelectionUser(start, cur); @@ -415,7 +428,14 @@ var CodeMirror = (function() { focusInput(); updateInput = true; move(); up(); + } + var move = connect(document, "mousemove", operation(function(e) { + clearTimeout(going); + e_preventDefault(e); + if (!ie && !e_button(e)) done(e); + else extend(e); }), true); + var up = connect(document, "mouseup", operation(done), true); } function onDoubleClick(e) { for (var n = e_target(e); n != wrapper; n = n.parentNode) @@ -464,11 +484,14 @@ var CodeMirror = (function() { } function onDragStart(e) { var txt = getSelection(); - // Disabled until further notice. Doesn't work on most browsers, - // and crashes Safari (issue #332). - //htmlEscape(txt); - //e.dataTransfer.setDragImage(escapeElement, 0, 0); e.dataTransfer.setData("Text", txt); + + // Use dummy image instead of default browsers image. + if (gecko || chrome) { + var img = document.createElement('img'); + img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image + e.dataTransfer.setDragImage(img, 0, 0); + } } function doHandleBinding(bound, dropShift) { @@ -506,13 +529,19 @@ var CodeMirror = (function() { if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; if (e_prop(e, "metaKey")) name = "Cmd-" + name; - if (e_prop(e, "shiftKey")) + if (e_prop(e, "shiftKey")) { handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap, - function(b) {return doHandleBinding(b, true);}); - if (!handled) + function(b) {return doHandleBinding(b, true);}) + || lookupKey(name, options.extraKeys, options.keyMap, function(b) { + if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b); + }); + } else { handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding); - - if (handled) e_preventDefault(e); + } + if (handled) { + e_preventDefault(e); + if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } + } return handled; } function handleCharBinding(e, ch) { @@ -545,7 +574,7 @@ var CodeMirror = (function() { if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} - if (window.opera && !e.which && handleKeyBinding(e)) return; + if (((window.opera && !e.which) || khtml) && handleKeyBinding(e)) return; var ch = String.fromCharCode(charCode == null ? keyCode : charCode); if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { if (mode.electricChars.indexOf(ch) > -1) @@ -837,11 +866,11 @@ var CodeMirror = (function() { return scrollIntoView(x, cursor.y, x, cursor.yBot); } function scrollIntoView(x1, y1, x2, y2) { - var pl = paddingLeft(), pt = paddingTop(), lh = textHeight(); + var pl = paddingLeft(), pt = paddingTop(); y1 += pt; y2 += pt; x1 += pl; x2 += pl; var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true; - if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;} - else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;} + if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1); scrolled = true;} + else if (y2 > screentop + screen) {scroller.scrollTop = y2 - screen; scrolled = true;} var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; var gutterw = options.fixedGutter ? gutter.clientWidth : 0; @@ -921,7 +950,7 @@ var CodeMirror = (function() { throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + " nodes=" + lineDiv.childNodes.length); - if (options.lineWrapping) { + function checkHeights() { maxWidth = scroller.clientWidth; var curNode = lineDiv.firstChild, heightChanged = false; doc.iter(showingFrom, showingTo, function(line) { @@ -936,6 +965,11 @@ var CodeMirror = (function() { }); if (heightChanged) code.style.height = (doc.height * th + 2 * paddingTop()) + "px"; + return heightChanged; + } + + if (options.lineWrapping) { + checkHeights(); } else { if (maxWidth == null) maxWidth = stringWidth(maxLine); if (maxWidth > scroller.clientWidth) { @@ -947,8 +981,12 @@ var CodeMirror = (function() { lineSpace.style.width = code.style.width = ""; } } + gutter.style.display = gutterDisplay; - if (different || gutterDirty) updateGutter(); + if (different || gutterDirty) { + // If the gutter grew in size, re-check heights. If those changed, re-draw gutter. + updateGutter() && options.lineWrapping && checkHeights() && updateGutter(); + } updateSelection(); if (!suppressCallback && options.onUpdate) options.onUpdate(instance); return true; @@ -996,16 +1034,17 @@ var CodeMirror = (function() { } // This pass fills in the lines that actually changed. var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; - var scratch = targetDocument.createElement("div"), newElt; + var scratch = document.createElement("div"); doc.iter(from, to, function(line) { if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); if (!nextIntact || nextIntact.from > j) { if (line.hidden) var html = scratch.innerHTML = "
";
           else {
-            var html = '
' + line.getHTML(makeTab) + '
'; + var html = '' + + line.getHTML(makeTab) + ''; // Kludge to make sure the styled element lies behind the selection (by z-index) - if (line.className) - html = '
 
' + html + "
"; } scratch.innerHTML = html; @@ -1021,7 +1060,7 @@ var CodeMirror = (function() { if (!options.gutter && !options.lineNumbers) return; var hText = mover.offsetHeight, hEditor = scroller.clientHeight; gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; - var html = [], i = showingFrom; + var html = [], i = showingFrom, normalNode; doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { if (line.hidden) { html.push("
");
@@ -1035,17 +1074,24 @@ var CodeMirror = (function() {
           html.push((marker && marker.style ? '
' : "
"), text);
           for (var j = 1; j < line.height; ++j) html.push("
 "); html.push("
"); + if (!marker) normalNode = i; } ++i; }); gutter.style.display = "none"; gutterText.innerHTML = html.join(""); - var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = ""; - while (val.length + pad.length < minwidth) pad += "\u00a0"; - if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild); + // Make sure scrolling doesn't cause number gutter size to pop + if (normalNode != null) { + var node = gutterText.childNodes[normalNode - showingFrom]; + var minwidth = String(doc.size).length, val = eltText(node), pad = ""; + while (val.length + pad.length < minwidth) pad += "\u00a0"; + if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild); + } gutter.style.display = ""; + var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2; lineSpace.style.marginLeft = gutter.offsetWidth + "px"; gutterDirty = false; + return resized; } function updateSelection() { var collapsed = posEq(sel.from, sel.to); @@ -1066,16 +1112,18 @@ var CodeMirror = (function() { html += '
'; } + var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; + var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; if (sel.from.ch && fromPos.y >= 0) { - var right = sameLine ? lineSpace.clientWidth - toPos.x : 0; + var right = sameLine ? clientWidth - toPos.x : 0; add(fromPos.x, fromPos.y, right, th); } var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); - var middleHeight = Math.min(toPos.y, lineSpace.clientHeight) - middleStart; + var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; if (middleHeight > 0.2 * th) add(0, middleStart, 0, middleHeight); - if ((!sameLine || !sel.from.ch) && toPos.y < lineSpace.clientHeight - .5 * th) - add(0, toPos.y, lineSpace.clientWidth - toPos.x, th); + if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) + add(0, toPos.y, clientWidth - toPos.x, th); selectionDiv.innerHTML = html; cursor.style.display = "none"; selectionDiv.style.display = ""; @@ -1105,7 +1153,12 @@ var CodeMirror = (function() { if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} // Skip over hidden lines. - if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch); + if (from.line != oldFrom) { + var from1 = skipHidden(from, oldFrom, sel.from.ch); + // If there is no non-hidden line left, force visibility on current line + if (!from1) setLineHidden(from.line, false); + else from = from1; + } if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); if (posEq(from, to)) sel.inverted = false; @@ -1114,7 +1167,7 @@ var CodeMirror = (function() { if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) { var head = sel.inverted ? from : to; - if (head.line != sel.from.line) { + if (head.line != sel.from.line && sel.from.line < doc.size) { var oldLine = getLine(sel.from.line); if (/^\s+$/.test(oldLine.text)) setTimeout(operation(function() { @@ -1209,6 +1262,7 @@ var CodeMirror = (function() { if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); else if (unit == "line") dist = textHeight(); var target = coordsChar(pos.x, pos.y + dist * dir + 2); + if (unit == "page") scroller.scrollTop += localCoords(target, true).y - pos.y; setCursor(target.line, target.ch, true); goalColumn = pos.x; } @@ -1295,9 +1349,10 @@ var CodeMirror = (function() { changes.push({from: 0, to: doc.size}); } function makeTab(col) { - var w = options.tabSize - col % options.tabSize; + var w = options.tabSize - col % options.tabSize, cached = tabCache[w]; + if (cached) return cached; for (var str = '', i = 0; i < w; ++i) str += " "; - return {html: str + "", width: w}; + return (tabCache[w] = {html: str + "", width: w}); } function themeChanged() { scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") + @@ -1313,7 +1368,7 @@ var CodeMirror = (function() { var lineN = lineNo(line); min = Math.min(min, lineN); max = Math.max(max, lineN); for (var j = 0; j < mk.length; ++j) - if (mk[j].set == this.set) mk.splice(j--, 1); + if (mk[j].marker == this) mk.splice(j--, 1); } if (min != Infinity) changes.push({from: min, to: max + 1}); @@ -1324,7 +1379,7 @@ var CodeMirror = (function() { var line = this.set[i], mk = line.marked; for (var j = 0; j < mk.length; ++j) { var mark = mk[j]; - if (mark.set == this.set) { + if (mark.marker == this) { if (mark.from != null || mark.to != null) { var found = lineNo(line); if (found != null) { @@ -1341,8 +1396,9 @@ var CodeMirror = (function() { function markText(from, to, className) { from = clipPos(from); to = clipPos(to); var tm = new TextMarker(); + if (!posLess(from, to)) return tm; function add(line, from, to, className) { - getLine(line).addMark(new MarkedText(from, to, className, tm.set)); + getLine(line).addMark(new MarkedText(from, to, className, tm)); } if (from.line == to.line) add(from.line, from.ch, to.ch, className); else { @@ -1362,6 +1418,19 @@ var CodeMirror = (function() { return bm; } + function findMarksAt(pos) { + pos = clipPos(pos); + var markers = [], marked = getLine(pos.line).marked; + if (!marked) return markers; + for (var i = 0, e = marked.length; i < e; ++i) { + var m = marked[i]; + if ((m.from == null || m.from <= pos.ch) && + (m.to == null || m.to >= pos.ch)) + markers.push(m.marker || m); + } + return markers; + } + function addGutterMarker(line, text, className) { if (typeof line == "number") line = getLine(clipLine(line)); line.gutterMarker = {text: text, style: className}; @@ -1383,10 +1452,11 @@ var CodeMirror = (function() { else return null; return line; } - function setLineClass(handle, className) { + function setLineClass(handle, className, bgClassName) { return changeLine(handle, function(line) { - if (line.className != className) { + if (line.className != className || line.bgClassName != bgClassName) { line.className = className; + line.bgClassName = bgClassName; return true; } }); @@ -1400,6 +1470,8 @@ var CodeMirror = (function() { if (hidden && (fline == no || tline == no)) { var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from; var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to; + // Can't hide the last visible line, we'd have no place to put the cursor + if (!to) return; setSelection(from, to); } return (gutterDirty = true); @@ -1420,7 +1492,7 @@ var CodeMirror = (function() { } var marker = line.gutterMarker; return {line: n, handle: line, text: line.text, markerText: marker && marker.text, - markerClass: marker && marker.style, lineClass: line.className}; + markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; } function stringWidth(str) { @@ -1464,7 +1536,7 @@ var CodeMirror = (function() { var extra = ""; // Include extra text at the end to make sure the measured line is wrapped in the right way. if (options.lineWrapping) { - var end = line.text.indexOf(" ", ch + 2); + var end = line.text.indexOf(" ", ch + 6); extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0))); } measure.innerHTML = "
" + line.getHTML(makeTab, ch) +
@@ -1823,7 +1895,7 @@ var CodeMirror = (function() {
     pollInterval: 100,
     undoDepth: 40,
     tabindex: null,
-    document: window.document
+    autofocus: null
   };
 
   var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
@@ -1831,7 +1903,7 @@ var CodeMirror = (function() {
   var win = /Win/.test(navigator.platform);
 
   // Known modes, by name and by MIME
-  var modes = {}, mimeModes = {};
+  var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
   CodeMirror.defineMode = function(name, mode) {
     if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name;
     modes[name] = mode;
@@ -1842,6 +1914,8 @@ var CodeMirror = (function() {
   CodeMirror.resolveMode = function(spec) {
     if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
       spec = mimeModes[spec];
+    else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
+      return CodeMirror.resolveMode("application/xml");
     if (typeof spec == "string") return {name: spec};
     else return spec || {name: "null"};
   };
@@ -1926,7 +2000,7 @@ var CodeMirror = (function() {
   keyMap.basic = {
     "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
     "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
-    "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess",
+    "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "insertTab", "Shift-Tab": "indentAuto",
     "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
   };
   // Note that the save and find-related commands aren't defined by
@@ -1937,6 +2011,7 @@ var CodeMirror = (function() {
     "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
     "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find",
     "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+    "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
     fallthrough: "basic"
   };
   keyMap.macDefault = {
@@ -1945,6 +2020,7 @@ var CodeMirror = (function() {
     "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft",
     "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find",
     "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+    "Cmd-[": "indentLess", "Cmd-]": "indentMore",
     fallthrough: ["basic", "emacsy"]
   };
   keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
@@ -1987,6 +2063,8 @@ var CodeMirror = (function() {
     options.value = textarea.value;
     if (!options.tabindex && textarea.tabindex)
       options.tabindex = textarea.tabindex;
+    if (options.autofocus == null && textarea.getAttribute("autofocus") != null)
+      options.autofocus = true;
 
     function save() {textarea.value = instance.getValue();}
     if (textarea.form) {
@@ -2098,34 +2176,34 @@ var CodeMirror = (function() {
   };
   CodeMirror.StringStream = StringStream;
 
-  function MarkedText(from, to, className, set) {
-    this.from = from; this.to = to; this.style = className; this.set = set;
+  function MarkedText(from, to, className, marker) {
+    this.from = from; this.to = to; this.style = className; this.marker = marker;
   }
   MarkedText.prototype = {
-    attach: function(line) { this.set.push(line); },
+    attach: function(line) { this.marker.set.push(line); },
     detach: function(line) {
-      var ix = indexOf(this.set, line);
-      if (ix > -1) this.set.splice(ix, 1);
+      var ix = indexOf(this.marker.set, line);
+      if (ix > -1) this.marker.set.splice(ix, 1);
     },
     split: function(pos, lenBefore) {
       if (this.to <= pos && this.to != null) return null;
       var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore;
       var to = this.to == null ? null : this.to - pos + lenBefore;
-      return new MarkedText(from, to, this.style, this.set);
+      return new MarkedText(from, to, this.style, this.marker);
     },
-    dup: function() { return new MarkedText(null, null, this.style, this.set); },
+    dup: function() { return new MarkedText(null, null, this.style, this.marker); },
     clipTo: function(fromOpen, from, toOpen, to, diff) {
-      if (this.from != null && this.from >= from)
-        this.from = Math.max(to, this.from) + diff;
-      if (this.to != null && this.to > from)
-        this.to = to < this.to ? this.to + diff : from;
       if (fromOpen && to > this.from && (to < this.to || this.to == null))
         this.from = null;
+      else if (this.from != null && this.from >= from)
+        this.from = Math.max(to, this.from) + diff;
       if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null))
         this.to = null;
+      else if (this.to != null && this.to > from)
+        this.to = to < this.to ? this.to + diff : from;
     },
     isDead: function() { return this.from != null && this.to != null && this.from >= this.to; },
-    sameSet: function(x) { return this.set == x.set; }
+    sameSet: function(x) { return this.marker == x.marker; }
   };
 
   function Bookmark(pos) {
@@ -2168,7 +2246,7 @@ var CodeMirror = (function() {
     this.styles = styles || [text, null];
     this.text = text;
     this.height = 1;
-    this.marked = this.gutterMarker = this.className = this.handlers = null;
+    this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null;
     this.stateAfter = this.parent = this.hidden = null;
   }
   Line.inheritMarks = function(text, orig) {
@@ -2214,6 +2292,7 @@ var CodeMirror = (function() {
           if (newmark) {
             if (!taken.marked) taken.marked = [];
             taken.marked.push(newmark); newmark.attach(taken);
+            if (newmark == mark) mk.splice(i--, 1);
           }
         }
       }
@@ -2637,10 +2716,10 @@ var CodeMirror = (function() {
         if (start < last.start) {
           for (var i = last.start - start - 1; i >= 0; --i)
             last.old.unshift(old[i]);
-          last.added += last.start - start;
+          oldoff = Math.min(0, added - old.length);
+          last.added += last.start - start + oldoff;
           last.start = start;
-        }
-        else if (last.start < start) {
+        } else if (last.start < start) {
           oldoff = start - last.start;
           added += oldoff;
         }
@@ -2707,19 +2786,21 @@ var CodeMirror = (function() {
 
   var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}};
 
-  // Detect drag-and-drop
-  var dragAndDrop = function() {
-    // IE8 has ondragstart and ondrop properties, but doesn't seem to
-    // actually support ondragstart the way it's supposed to work.
-    if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false;
-    var div = document.createElement('div');
-    return "draggable" in div;
-  }();
-
   var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
   var ie = /MSIE \d/.test(navigator.userAgent);
   var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
   var webkit = /WebKit\//.test(navigator.userAgent);
+  var chrome = /Chrome\//.test(navigator.userAgent);
+  var khtml = /KHTML\//.test(navigator.userAgent);
+
+  // Detect drag-and-drop
+  var dragAndDrop = function() {
+    // There is *some* kind of drag-and-drop support in IE6-8, but I
+    // couldn't get it to work yet.
+    if (ie_lt9) return false;
+    var div = document.createElement('div');
+    return "draggable" in div || "dragDrop" in div;
+  }();
 
   var lineSep = "\n";
   // Feature-detect whether newlines in textareas are converted to \r\n
@@ -2873,7 +2954,7 @@ var CodeMirror = (function() {
   var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
                   19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
                   36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
-                  46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",",
+                  46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 127: "Delete", 186: ";", 187: "=", 188: ",",
                   189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
                   63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
                   63233: "Down", 63302: "Insert", 63272: "Delete"};
diff --git a/imports/codemirror/lib/util/closetag.js b/imports/codemirror/lib/util/closetag.js
new file mode 100644
index 00000000..44c2b435
--- /dev/null
+++ b/imports/codemirror/lib/util/closetag.js
@@ -0,0 +1,174 @@
+/**
+ * Tag-closer extension for CodeMirror.
+ *
+ * This extension adds a "closeTag" utility function that can be used with key bindings to 
+ * insert a matching end tag after the ">" character of a start tag has been typed.  It can
+ * also complete "
+ * Contributed under the same license terms as CodeMirror.
+ */
+(function() {
+	/** Option that allows tag closing behavior to be toggled.  Default is true. */
+	CodeMirror.defaults['closeTagEnabled'] = true;
+	
+	/** Array of tag names to add indentation after the start tag for.  Default is the list of block-level html tags. */
+	CodeMirror.defaults['closeTagIndent'] = ['applet', 'blockquote', 'body', 'button', 'div', 'dl', 'fieldset', 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 'iframe', 'layer', 'legend', 'object', 'ol', 'p', 'select', 'table', 'ul'];
+
+	/**
+	 * Call during key processing to close tags.  Handles the key event if the tag is closed, otherwise throws CodeMirror.Pass.
+	 * - cm: The editor instance.
+	 * - ch: The character being processed.
+	 * - indent: Optional.  Omit or pass true to use the default indentation tag list defined in the 'closeTagIndent' option.
+	 *   Pass false to disable indentation.  Pass an array to override the default list of tag names.
+	 */
+	CodeMirror.defineExtension("closeTag", function(cm, ch, indent) {
+		if (!cm.getOption('closeTagEnabled')) {
+			throw CodeMirror.Pass;
+		}
+		
+		var mode = cm.getOption('mode');
+		
+		if (mode == 'text/html') {
+		
+			/*
+			 * Relevant structure of token:
+			 *
+			 * htmlmixed
+			 * 		className
+			 * 		state
+			 * 			htmlState
+			 * 				type
+			 * 				context
+			 * 					tagName
+			 * 			mode
+			 * 
+			 * xml
+			 * 		className
+			 * 		state
+			 * 			tagName
+			 * 			type
+			 */
+		
+			var pos = cm.getCursor();
+			var tok = cm.getTokenAt(pos);
+			var state = tok.state;
+			
+			if (state.mode && state.mode != 'html') {
+				throw CodeMirror.Pass; // With htmlmixed, we only care about the html sub-mode.
+			}
+			
+			if (ch == '>') {
+				var type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
+				
+				if (tok.className == 'tag' && type == 'closeTag') {
+					throw CodeMirror.Pass; // Don't process the '>' at the end of an end-tag.
+				}
+			
+				cm.replaceSelection('>'); // Mode state won't update until we finish the tag.
+				pos = {line: pos.line, ch: pos.ch + 1};
+				cm.setCursor(pos);
+		
+				tok = cm.getTokenAt(cm.getCursor());
+				state = tok.state;
+				type = state.htmlState ? state.htmlState.type : state.type; // htmlmixed : xml
+
+				if (tok.className == 'tag' && type != 'selfcloseTag') {
+					var tagName = state.htmlState ? state.htmlState.context.tagName : state.tagName; // htmlmixed : xml
+					if (tagName.length > 0) {
+						insertEndTag(cm, indent, pos, tagName);
+					}
+					return;
+				}
+				
+				// Undo the '>' insert and allow cm to handle the key instead.
+				cm.setSelection({line: pos.line, ch: pos.ch - 1}, pos);
+				cm.replaceSelection("");
+			
+			} else if (ch == '/') {
+				if (tok.className == 'tag' && tok.string == '<') {
+					var tagName = state.htmlState ? (state.htmlState.context ? state.htmlState.context.tagName : '') : state.context.tagName; // htmlmixed : xml # extra htmlmized check is for ' 0) {
+						completeEndTag(cm, pos, tagName);
+						return;
+					}
+				}
+			}
+		
+		} else if (mode == 'xmlpure') {
+
+			var pos = cm.getCursor();
+			var tok = cm.getTokenAt(pos);
+			var tagName = tok.state.context.tagName;
+
+			if (ch == '>') {
+				// 			tagName=foo, string=foo
+				// 			tagName=foo, string=/		# ignore
+				// 		tagName=foo, string=/foo	# ignore
+				if (tok.string == tagName) {
+					cm.replaceSelection('>'); // parity w/html modes
+					pos = {line: pos.line, ch: pos.ch + 1};
+					cm.setCursor(pos);
+					
+					insertEndTag(cm, indent, pos, tagName);
+					return;
+				}
+				
+			} else if (ch == '/') {
+				// ', 'end');
+			cm.indentLine(pos.line + 1);
+			cm.indentLine(pos.line + 2);
+			cm.setCursor({line: pos.line + 1, ch: cm.getLine(pos.line + 1).length});
+		} else {
+			cm.replaceSelection('');
+			cm.setCursor(pos);
+		}
+	}
+	
+	function shouldIndent(cm, indent, tagName) {
+		if (typeof indent == 'undefined' || indent == null || indent == true) {
+			indent = cm.getOption('closeTagIndent');
+		}
+		if (!indent) {
+			indent = [];
+		}
+		return indexOf(indent, tagName.toLowerCase()) != -1;
+	}
+	
+	// C&P from codemirror.js...would be nice if this were visible to utilities.
+	function indexOf(collection, elt) {
+		if (collection.indexOf) return collection.indexOf(elt);
+		for (var i = 0, e = collection.length; i < e; ++i)
+			if (collection[i] == elt) return i;
+		return -1;
+	}
+
+	function completeEndTag(cm, pos, tagName) {
+		cm.replaceSelection('/' + tagName + '>');
+		cm.setCursor({line: pos.line, ch: pos.ch + tagName.length + 2 });
+	}
+	
+})();
diff --git a/imports/codemirror/lib/util/foldcode.js b/imports/codemirror/lib/util/foldcode.js
index 50268a8b..b700d860 100644
--- a/imports/codemirror/lib/util/foldcode.js
+++ b/imports/codemirror/lib/util/foldcode.js
@@ -80,7 +80,7 @@ CodeMirror.tagRangeFinder = function(cm, line) {
   }
 
   if (found) {
-    var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\s)|(\\<" + tag + "$)";
+    var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\\s)|(\\<" + tag + "$)";
     var startTagRegExp = new RegExp(startTag, "g");
     var endTag = "";
     var depth = 1;
diff --git a/imports/codemirror/lib/util/formatting.js b/imports/codemirror/lib/util/formatting.js
index 15de0355..e1891191 100644
--- a/imports/codemirror/lib/util/formatting.js
+++ b/imports/codemirror/lib/util/formatting.js
@@ -1,4 +1,4 @@
-// ============== Formatting extensions ============================
+// ============== Formatting extensions ============================
 // A common storage for all mode-specific formatting features
 if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {};
 
diff --git a/imports/codemirror/lib/util/javascript-hint.js b/imports/codemirror/lib/util/javascript-hint.js
index 2b904a51..2117e5af 100644
--- a/imports/codemirror/lib/util/javascript-hint.js
+++ b/imports/codemirror/lib/util/javascript-hint.js
@@ -113,8 +113,10 @@
         base = 1;
       else if (obj.className == "function") {
         if (window.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
-            (typeof jQuery == 'function')) base = jQuery();
-        else if (window._ != null && (obj.string == '_') && (typeof _ == 'function')) base = _();
+            (typeof window.jQuery == 'function'))
+          base = window.jQuery();
+        else if (window._ != null && (obj.string == '_') && (typeof window._ == 'function'))
+          base = window._();
       }
       while (base != null && context.length)
         base = base[context.pop().string];
diff --git a/imports/codemirror/lib/util/simple-hint.js b/imports/codemirror/lib/util/simple-hint.js
index b38f3892..7decd587 100644
--- a/imports/codemirror/lib/util/simple-hint.js
+++ b/imports/codemirror/lib/util/simple-hint.js
@@ -29,6 +29,10 @@
     complete.style.left = pos.x + "px";
     complete.style.top = pos.yBot + "px";
     document.body.appendChild(complete);
+    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+    var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+    if(winW - pos.x < sel.clientWidth)
+      complete.style.left = (pos.x - sel.clientWidth) + "px";
     // Hack to hide the scrollbar.
     if (completions.length <= 10)
       complete.style.width = (sel.clientWidth - 1) + "px";
@@ -53,6 +57,8 @@
       else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();}
       else if (code != 38 && code != 40) {
         close(); editor.focus();
+        // Pass the event to the CodeMirror instance so that it can handle things like backspace properly.
+        editor.triggerOnKeyDown(event);
         setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50);
       }
     });
diff --git a/imports/codemirror/mode/clike/index.html b/imports/codemirror/mode/clike/index.html
index 5c327451..64d02f11 100644
--- a/imports/codemirror/mode/clike/index.html
+++ b/imports/codemirror/mode/clike/index.html
@@ -96,6 +96,6 @@ void* zmq_thread_init(void* zmq_context, int signal_fd) {
     

MIME types defined: text/x-csrc (C code), text/x-c++src (C++ code), text/x-java (Java - code).

+ code), text/x-csharp (C#).

diff --git a/imports/codemirror/mode/clojure/clojure.js b/imports/codemirror/mode/clojure/clojure.js index cce7ce37..c6836415 100644 --- a/imports/codemirror/mode/clojure/clojure.js +++ b/imports/codemirror/mode/clojure/clojure.js @@ -4,7 +4,7 @@ */ CodeMirror.defineMode("clojure", function (config, mode) { var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", TAG = "tag", - ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD="keyword"; + ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword"; var INDENT_WORD_SKIP = 2, KEYWORDS_SKIP = 1; function makeKeywords(str) { @@ -14,26 +14,25 @@ CodeMirror.defineMode("clojure", function (config, mode) { } var atoms = makeKeywords("true false nil"); - + var keywords = makeKeywords( - // Control structures - "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle" + + "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle"); - // Built-ins + var builtins = makeKeywords( "* *1 *2 *3 *agent* *allow-unresolved-vars* *assert *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *macro-meta* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *use-context-classloader* *warn-on-reflection* + - / < <= = == > >= accessor aclone agent agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defstruct delay delay? deliver deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq eval even? every? extend extend-protocol extend-type extends? extenders false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-doc print-dup print-method print-namespace-doc print-simple print-special-doc print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reify reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure release-pending-sends rem remove remove-method remove-ns repeat repeatedly replace replicate require reset! reset-meta! resolve rest resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str stream? string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision xml-seq"); var indentKeys = makeKeywords( // Built-ins - "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch" + + "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch " + // Binding forms - "let letfn binding loop for doseq dotimes when-let if-let" + + "let letfn binding loop for doseq dotimes when-let if-let " + // Data structures - "defstruct struct-map assoc" + + "defstruct struct-map assoc " + // clojure.test - "testing deftest" + + "testing deftest " + // contrib "handler-case handle dotrace deftrace"); @@ -154,12 +153,11 @@ CodeMirror.defineMode("clojure", function (config, mode) { (;something else, bracket, etc. */ - while ((letter = stream.eat(tests.keyword_char)) != null) { + if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) { keyWord += letter; } if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word - pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); } else { // non-indent word // we continue eating the spaces @@ -182,13 +180,15 @@ CodeMirror.defineMode("clojure", function (config, mode) { } } else if ( ch == ":" ) { stream.eatWhile(tests.lang_keyword); - return TAG; + return ATOM; } else { stream.eatWhile(tests.basic); if (keywords && keywords.propertyIsEnumerable(stream.current())) { + returnType = KEYWORD; + } else if (builtins && builtins.propertyIsEnumerable(stream.current())) { returnType = BUILTIN; - } else if ( atoms && atoms.propertyIsEnumerable(stream.current()) ) { + } else if (atoms && atoms.propertyIsEnumerable(stream.