diff --git a/ChangeLog b/ChangeLog index 62459cbf..fc8f5d87 100644 --- a/ChangeLog +++ b/ChangeLog @@ -85,6 +85,8 @@ dom.js and util.js. * Added ability to hide keys panel thru config option. +* CodeMirror upgraded to version 2.35.0. + 2012.10.01, Version 0.1.7 diff --git a/lib/client/editor/_codemirror.js b/lib/client/editor/_codemirror.js index 7146b6c5..16b6806e 100644 --- a/lib/client/editor/_codemirror.js +++ b/lib/client/editor/_codemirror.js @@ -19,7 +19,7 @@ var CloudCommander, Util, DOM, CloudFunc, CodeMirror; }; cloudcmd.Editor.dir = 'lib/client/editor/'; - CodeMirrorEditor.dir = cloudcmd.Editor.dir + 'codemirror/'; + CodeMirrorEditor.dir = cloudcmd.Editor.dir + 'codemirror_v2/'; /* private functions */ diff --git a/lib/client/editor/codemirror_v2/codemirror.css b/lib/client/editor/codemirror_v2/codemirror.css index 05ad0ed0..41b8d09e 100644 --- a/lib/client/editor/codemirror_v2/codemirror.css +++ b/lib/client/editor/codemirror_v2/codemirror.css @@ -80,6 +80,7 @@ word-wrap: normal; line-height: inherit; color: inherit; + overflow: visible; } .CodeMirror-wrap pre { diff --git a/lib/client/editor/codemirror_v2/codemirror.js b/lib/client/editor/codemirror_v2/codemirror.js index 6f58c0f0..e49481b5 100644 --- a/lib/client/editor/codemirror_v2/codemirror.js +++ b/lib/client/editor/codemirror_v2/codemirror.js @@ -1,3 +1,5 @@ +// CodeMirror version 2.35 +// // All functions that need access to the editor's state live inside // the CodeMirror function. Below that, at the bottom of the file, // some utilities are defined. @@ -75,7 +77,7 @@ window.CodeMirror = (function() { // Selection-related flags. shiftSelecting obviously tracks // whether the user is holding shift. var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, draggingText, - overwrite = false, suppressEdits = false; + overwrite = false, suppressEdits = false, pasteIncoming = false; // Variables used by startOperation/endOperation to track what // happened during the operation. var updateInput, userSelChange, changes, textChanged, selectionChanged, @@ -128,7 +130,7 @@ window.CodeMirror = (function() { connect(scroller, "drop", operation(onDrop)); } connect(scroller, "paste", function(){focusInput(); fastPoll();}); - connect(input, "paste", fastPoll); + connect(input, "paste", function(){pasteIncoming = true; fastPoll();}); connect(input, "cut", operation(function(){ if (!options.readOnly) replaceSelection(""); })); @@ -167,6 +169,7 @@ window.CodeMirror = (function() { else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); else if (option == "tabSize") updateDisplay(true); else if (option == "keyMap") keyMapChanged(); + else if (option == "tabindex") input.tabIndex = value; if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme" || option == "lineNumberFormatter") { gutterChanged(); @@ -954,12 +957,13 @@ window.CodeMirror = (function() { while (same < l && prevInput[same] == text[same]) ++same; if (same < prevInput.length) sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; - else if (overwrite && posEq(sel.from, sel.to)) + else if (overwrite && posEq(sel.from, sel.to) && !pasteIncoming) sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; replaceSelection(text.slice(same), "end"); if (text.length > 1000) { input.value = prevInput = ""; } else prevInput = text; if (!nestedOperation) endOperation(); + pasteIncoming = false; return true; } function resetInput(user) { @@ -1416,7 +1420,7 @@ window.CodeMirror = (function() { var startChar = line.charAt(start); var check = isWordChar(startChar) ? isWordChar : /\s/.test(startChar) ? function(ch) {return /\s/.test(ch);} : - function(ch) {return !/\s/.test(ch) && !isWordChar(ch);}; + function(ch) {return !/\s/.test(ch) && isWordChar(ch);}; while (start > 0 && check(line.charAt(start - 1))) --start; while (end < line.length && check(line.charAt(end))) ++end; } @@ -1460,6 +1464,7 @@ window.CodeMirror = (function() { if (indentString != curSpaceString) replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); + line.stateAfter = null; } function loadMode() { @@ -1505,18 +1510,17 @@ window.CodeMirror = (function() { function TextMarker(type, style) { this.lines = []; this.type = type; if (style) this.style = style; } TextMarker.prototype.clear = operation(function() { - var min = Infinity, max = -Infinity; + var min, max; for (var i = 0; i < this.lines.length; ++i) { var line = this.lines[i]; - var span = getMarkedSpanFor(line.markedSpans, this, true); - if (span.from != null || span.to != null) { - var lineN = lineNo(line); - min = Math.min(min, lineN); max = Math.max(max, lineN); - } + var span = getMarkedSpanFor(line.markedSpans, this); + if (span.from != null) min = lineNo(line); + if (span.to != null) max = lineNo(line); + line.markedSpans = removeMarkedSpan(line.markedSpans, span); } - if (min != Infinity) - changes.push({from: min, to: max + 1}); + if (min != null) changes.push({from: min, to: max + 1}); this.lines.length = 0; + this.explicitlyCleared = true; }); TextMarker.prototype.find = function() { var from, to; @@ -1543,7 +1547,7 @@ window.CodeMirror = (function() { var span = {from: curLine == from.line ? from.ch : null, to: curLine == to.line ? to.ch : null, marker: marker}; - (line.markedSpans || (line.markedSpans = [])).push(span); + line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); ++curLine; }); @@ -1554,8 +1558,9 @@ window.CodeMirror = (function() { function setBookmark(pos) { pos = clipPos(pos); var marker = new TextMarker("bookmark"), line = getLine(pos.line); + history.addChange(pos.line, 1, [newHL(line.text, line.markedSpans)], true); var span = {from: pos.ch, to: pos.ch, marker: marker}; - (line.markedSpans || (line.markedSpans = [])).push(span); + line.markedSpans = (line.markedSpans || []).concat([span]); marker.lines.push(line); return marker; } @@ -1644,8 +1649,6 @@ window.CodeMirror = (function() { function measureLine(line, ch) { if (ch == 0) return {top: 0, left: 0}; - var wbr = options.lineWrapping && ch < line.text.length && - spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1)); var pre = lineContent(line, ch); removeChildrenAndAdd(measure, pre); var anchor = pre.anchor; @@ -1978,6 +1981,7 @@ window.CodeMirror = (function() { if (extensions.propertyIsEnumerable(ext) && !instance.propertyIsEnumerable(ext)) instance[ext] = extensions[ext]; + for (var i = 0; i < initHooks.length; ++i) initHooks[i](instance); return instance; } // (end of function CodeMirror) @@ -2075,6 +2079,9 @@ window.CodeMirror = (function() { extensions[name] = func; }; + var initHooks = []; + CodeMirror.defineInitHook = function(f) {initHooks.push(f);}; + var modeExtensions = CodeMirror.modeExtensions = {}; CodeMirror.extendMode = function(mode, properties) { var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); @@ -2204,6 +2211,7 @@ window.CodeMirror = (function() { var name = keyNames[e_prop(event, "keyCode")]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; } + CodeMirror.isModifierKey = isModifierKey; CodeMirror.fromTextArea = function(textarea, options) { if (!options) options = {}; @@ -2353,16 +2361,20 @@ window.CodeMirror = (function() { this.from = from; this.to = to; this.marker = marker; } - function getMarkedSpanFor(spans, marker, del) { + function getMarkedSpanFor(spans, marker) { if (spans) for (var i = 0; i < spans.length; ++i) { var span = spans[i]; - if (span.marker == marker) { - if (del) spans.splice(i, 1); - return span; - } + if (span.marker == marker) return span; } } + function removeMarkedSpan(spans, span) { + var r; + for (var i = 0; i < spans.length; ++i) + if (spans[i] != span) (r || (r = [])).push(spans[i]); + return r; + } + function markedSpansBefore(old, startCh, endCh) { if (old) for (var i = 0, nw; i < old.length; ++i) { var span = old[i], marker = span.marker; @@ -2446,7 +2458,15 @@ window.CodeMirror = (function() { // hl stands for history-line, a data structure that can be either a // string (line without markers) or a {text, markedSpans} object. function hlText(val) { return typeof val == "string" ? val : val.text; } - function hlSpans(val) { return typeof val == "string" ? null : val.markedSpans; } + function hlSpans(val) { + if (typeof val == "string") return null; + var spans = val.markedSpans, out = null; + for (var i = 0; i < spans.length; ++i) { + if (spans[i].marker.explicitlyCleared) { if (!out) out = spans.slice(0, i); } + else if (out) out.push(spans[i]); + } + return !out ? spans : out.length ? out : null; + } function newHL(text, spans) { return spans ? {text: text, markedSpans: spans} : text; } function detachMarkedSpans(line) { @@ -2582,13 +2602,17 @@ window.CodeMirror = (function() { span = function(html, text, style) { var l = text.length; if (wrapAt >= outPos && wrapAt < outPos + l) { - if (wrapAt > outPos) { - span_(html, text.slice(0, wrapAt - outPos), style); + var cut = wrapAt - outPos; + if (cut) { + span_(html, text.slice(0, cut), style); // See comment at the definition of spanAffectsWrapping - if (compensateForWrapping) html.appendChild(elt("wbr")); + if (compensateForWrapping) { + var view = text.slice(cut - 1, cut + 1); + if (spanAffectsWrapping.test(view)) html.appendChild(elt("wbr")); + else if (!ie_lt8 && /\w\w/.test(view)) html.appendChild(document.createTextNode("\u200d")); + } } html.appendChild(anchor); - var cut = wrapAt - outPos; span_(anchor, opera ? text.slice(cut, cut + 1) : text.slice(cut), style); if (opera) span_(html, text.slice(cut + 1), style); wrapAt--; @@ -2872,7 +2896,7 @@ window.CodeMirror = (function() { var time = +new Date, cur = lst(this.done), last = cur && lst(cur); var dtime = time - this.time; - if (this.compound && cur && !this.closed) { + if (cur && !this.closed && this.compound) { cur.push({start: start, added: added, old: old}); } else if (dtime > 400 || !last || this.closed || last.start > start + old.length || last.start + last.added < start) { @@ -3079,7 +3103,7 @@ window.CodeMirror = (function() { return -1; } function isWordChar(ch) { - return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); + return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase() || /[\u4E00-\u9FA5]/.test(ch); } // See if "".split is the broken IE version, if so, provide an @@ -3135,7 +3159,7 @@ window.CodeMirror = (function() { for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; })(); - CodeMirror.version = "2.34 +"; + CodeMirror.version = "2.35"; return CodeMirror; })(); diff --git a/lib/client/editor/codemirror_v2/mode/javascript.js b/lib/client/editor/codemirror_v2/mode/javascript.js index e754a047..37f6f873 100644 --- a/lib/client/editor/codemirror_v2/mode/javascript.js +++ b/lib/client/editor/codemirror_v2/mode/javascript.js @@ -1,6 +1,9 @@ +// TODO actually recognize syntax of TypeScript constructs + CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var jsonMode = parserConfig.json; + var isTS = parserConfig.typescript; // Tokenizer @@ -8,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - return { + + var jsKeywords = { "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), @@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom }; + + // Extend the 'normal' keywords with the TypeScript language extensions + if (isTS) { + var type = {type: "variable", style: "variable-3"}; + var tsKeywords = { + // object-like things + "interface": kw("interface"), + "class": kw("class"), + "extends": kw("extends"), + "constructor": kw("constructor"), + + // scope modifiers + "public": kw("public"), + "private": kw("private"), + "protected": kw("protected"), + "static": kw("static"), + + "super": kw("super"), + + // types + "string": type, "number": type, "bool": type, "any": type + }; + + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } + } + + return jsKeywords; }(); var isOperatorChar = /[+\-*&%=<>!?|]/; @@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { stream.skipToEnd(); return ret("comment", "comment"); } - else if (state.reAllowed) { + else if (state.lastType == "operator" || state.lastType == "keyword c" || + /^[\[{}\(,;:]$/.test(state.lastType)) { nextUntilUnescaped(stream, "/"); stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla return ret("regexp", "string-2"); @@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else { stream.eatWhile(/[\w\$_]/); var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; - return (known && state.kwAllowed) ? ret(known.type, known.style, word) : + return (known && state.lastType != ".") ? ret(known.type, known.style, word) : ret("variable", "variable", word); } } @@ -275,19 +309,30 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "}") return cont(); return pass(statement, block); } + function maybetype(type) { + if (type == ":") return cont(typedef); + return pass(); + } + function typedef(type) { + if (type == "variable"){cx.marked = "variable-3"; return cont();} + return pass(); + } function vardef1(type, value) { - if (type == "variable"){register(value); return cont(vardef2);} - return cont(); + if (type == "variable") { + register(value); + return isTS ? cont(maybetype, vardef2) : cont(vardef2); + } + return pass(); } function vardef2(type, value) { if (value == "=") return cont(expression, vardef2); if (type == ",") return cont(vardef1); } function forspec1(type) { - if (type == "var") return cont(vardef1, forspec2); - if (type == ";") return pass(forspec2); + if (type == "var") return cont(vardef1, expect(";"), forspec2); + if (type == ";") return cont(forspec2); if (type == "variable") return cont(formaybein); - return pass(forspec2); + return cont(forspec2); } function formaybein(type, value) { if (value == "in") return cont(expression); @@ -306,7 +351,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); } function funarg(type, value) { - if (type == "variable") {register(value); return cont();} + if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} } // Interface @@ -315,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { startState: function(basecolumn) { return { tokenize: jsTokenBase, - reAllowed: true, - kwAllowed: true, + lastType: null, cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, @@ -334,19 +378,21 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; - state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/)); - state.kwAllowed = type != '.'; + state.lastType = type; return parseJS(state, style, type, content, stream); }, indent: function(state, textAfter) { + if (state.tokenize == jsTokenComment) return CodeMirror.Pass; if (state.tokenize != jsTokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; - if (type == "vardef") return lexical.indented + 4; + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; - else if (type == "stat" || type == "form") return lexical.indented + indentUnit; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0); else if (lexical.info == "switch" && !closing) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); @@ -359,3 +405,5 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { CodeMirror.defineMIME("text/javascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/lib/client/editor/codemirror_v2/package.json b/lib/client/editor/codemirror_v2/package.json index 1ce054f3..8b959767 100644 --- a/lib/client/editor/codemirror_v2/package.json +++ b/lib/client/editor/codemirror_v2/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"2.34.1", + "version":"2.35.0", "main": "codemirror.js", "description": "In-browser code editing made bearable", "licenses": [{"type": "MIT",