From 2fc14dbad06372e88e2ec663a45b82eb175be760 Mon Sep 17 00:00:00 2001 From: Ankit Date: Sun, 28 Dec 2014 01:19:01 +0530 Subject: [PATCH 01/30] [sass mode] Improve indentation Fixes bug #2156 for the most part indention was bugged because it was reaching a wrong state when any text that was not in the known set of words or patterns was incurred. for eg: head | <---- cursor should be here and lets take color : red as another example now 'red' is same to the current tokenizer as 'head' from the first example. So it indents for red automatically. It was fixed by not letting any text after colon to reach the state where it appends an indent after it. --- mode/sass/sass.js | 82 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 19 deletions(-) diff --git a/mode/sass/sass.js b/mode/sass/sass.js index b792a02aa3..3ac48018f0 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -22,7 +22,7 @@ CodeMirror.defineMode("sass", function(config) { var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"]; var opRegexp = tokenRegexp(operators); - var pseudoElementsRegexp = /^::?[\w\-]+/; + var pseudoElementsRegexp = /^::[\w\-]+/; function urlTokens(stream, state) { var ch = stream.peek(); @@ -64,7 +64,7 @@ CodeMirror.defineMode("sass", function(config) { } function buildStringTokenizer(quote, greedy) { - if(greedy == null) { greedy = true; } + if (greedy == null) { greedy = true; } function stringTokenizer(stream, state) { var nextChar = stream.next(); @@ -189,13 +189,11 @@ CodeMirror.defineMode("sass", function(config) { if (ch === "$") { stream.next(); stream.eatWhile(/[\w-]/); - - if (stream.peek() === ":") { - stream.next(); + stream.eatSpace(); + if (stream.peek() === ":") return "variable-2"; - } else { + else return "variable-3"; - } } if (ch === "!") { @@ -245,22 +243,71 @@ CodeMirror.defineMode("sass", function(config) { return "string"; } - // Pseudo element selectors - if (ch == ":" && stream.match(pseudoElementsRegexp)) - return "keyword"; + // Pseudo element selectors and values after colon + if (ch === ":"){ + if (stream.match(pseudoElementsRegexp)) + return "keyword"; + stream.next(); + stream.eatSpace(); + if (stream.peek() === null){ + // if there is no more space after it + indent(state); + return "atom"; + } - // atoms - if (stream.eatWhile(/[\w-&]/)) { - // matches a property definition - if (stream.peek() === ":" && !stream.match(pseudoElementsRegexp, false)) - return "property"; - else + // all posible tokens after colon + if (stream.match(/\$[\w-]+/)){ + // variables + return "variable-3"; + } else if (stream.match(/^url/) && stream.peek() === "(") { + //urls + state.tokenizer = urlTokens; + return "atom"; + } else if (stream.match(/#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}/)){ + //hexadecimal value + return "number"; + } else if (stream.match(/^-?[0-9\.]+/)){ + return "number"; + } else if (stream.match(keywordsRegexp)){ + return "keyword"; + } else if (stream.match(/[\w-,\s]+/)){ + // other text return "atom"; + } } if (stream.match(opRegexp)) return "operator"; + // atoms + if (stream.eatWhile(/[\w-&]/)) { + stream.eatSpace(); + // matches a property definition + if (stream.peek() === ":" ){ + if (!stream.match(/:[ ]*[\d\w-\$\+#!\("']/,false)){ + //for cases where line ends after colon + // eg + // font: + // | <-----cursor + indent(state); + return "property"; + } else if (!stream.match(pseudoElementsRegexp, false)){ + return "property"; + } + } else { + stream.eatSpace(); + if (stream.peek() !== "," ){ + //for cases where line ends after comma + //eg + //head, + //body, + //| <-----cursor + indent(state); + } + return "atom"; + } + } + // If we haven't returned by now, we move 1 character // and return an error stream.next(); @@ -275,9 +322,6 @@ CodeMirror.defineMode("sass", function(config) { if (current === "@return") dedent(state); - if (style === "atom") - indent(state); - if (style !== null) { var startOfToken = stream.pos - current.length; var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount); From 66f9224c656e8a9512f52c6be53645e8ec55cd48 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Wed, 31 Dec 2014 16:34:06 -0500 Subject: [PATCH 02/30] [vim] Support showing search matches on scrollbar https://github.com/codemirror/CodeMirror/issues/3012 --- keymap/vim.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/keymap/vim.js b/keymap/vim.js index 5743a0d9c7..78046f59f9 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -3469,6 +3469,12 @@ }, setReversed: function(reversed) { vimGlobalState.isReversed = reversed; + }, + getScrollbarAnnotate: function() { + return this.annotate; + }, + setScrollbarAnnotate: function(annotate) { + this.annotate = annotate; } }; function getSearchState(cm) { @@ -3747,14 +3753,21 @@ }; } function highlightSearchMatches(cm, query) { - var overlay = getSearchState(cm).getOverlay(); + var searchState = getSearchState(cm); + var overlay = searchState.getOverlay(); if (!overlay || query != overlay.query) { if (overlay) { cm.removeOverlay(overlay); } overlay = searchOverlay(query); cm.addOverlay(overlay); - getSearchState(cm).setOverlay(overlay); + if (cm.showMatchesOnScrollbar) { + if (searchState.getScrollbarAnnotate()) { + searchState.getScrollbarAnnotate().clear(); + } + searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query)); + } + searchState.setOverlay(overlay); } } function findNext(cm, prev, query, repeat) { From d0a2ddaa0454f6ea00d4c151ce9c1c15a3d84014 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Wed, 31 Dec 2014 23:21:33 -0500 Subject: [PATCH 03/30] [vim] Enable clearing search scrollbar annotation https://github.com/codemirror/CodeMirror/issues/3012 --- keymap/vim.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/keymap/vim.js b/keymap/vim.js index 78046f59f9..913e3a63fd 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -3792,8 +3792,13 @@ }); } function clearSearchHighlight(cm) { + var state = getSearchState(cm); cm.removeOverlay(getSearchState(cm).getOverlay()); - getSearchState(cm).setOverlay(null); + state.setOverlay(null); + if (state.getScrollbarAnnotate()) { + state.getScrollbarAnnotate().clear(); + state.setScrollbarAnnotate(null); + } } /** * Check if pos is in the specified range, INCLUSIVE. From 9e56052a7a902229bc996fc1c241ddec8a89966e Mon Sep 17 00:00:00 2001 From: Ankit Date: Thu, 1 Jan 2015 23:16:08 +0530 Subject: [PATCH 04/30] [sass mode] Fixed bug #2156 and syntax highlighting too To fix this I had to introduce new state parameter that tells if cursor is before colon or not. --- mode/sass/sass.js | 321 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 182 insertions(+), 139 deletions(-) diff --git a/mode/sass/sass.js b/mode/sass/sass.js index 3ac48018f0..52a6682915 100644 --- a/mode/sass/sass.js +++ b/mode/sass/sass.js @@ -19,10 +19,11 @@ CodeMirror.defineMode("sass", function(config) { var keywords = ["true", "false", "null", "auto"]; var keywordsRegexp = new RegExp("^" + keywords.join("|")); - var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"]; + var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", + "\\!=", "/", "\\*", "%", "and", "or", "not", ";","\\{","\\}",":"]; var opRegexp = tokenRegexp(operators); - var pseudoElementsRegexp = /^::[\w\-]+/; + var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/; function urlTokens(stream, state) { var ch = stream.peek(); @@ -56,7 +57,7 @@ CodeMirror.defineMode("sass", function(config) { stream.next(); state.tokenizer = tokenBase; } else { - stream.next(); + stream.skipToEnd(); } return "comment"; @@ -135,178 +136,216 @@ CodeMirror.defineMode("sass", function(config) { return "operator"; } - if (ch === ".") { + // Strings + if (ch === '"' || ch === "'") { stream.next(); - - // Match class selectors - if (stream.match(/^[\w-]+/)) { - indent(state); - return "atom"; - } else if (stream.peek() === "#") { - indent(state); - return "atom"; - } else { - return "operator"; - } + state.tokenizer = buildStringTokenizer(ch); + return "string"; } - if (ch === "#") { - stream.next(); + if(!state.cursorHalf){// state.cursorHalf === 0 + // first half i.e. before : for key-value pairs + // including selectors - // Hex numbers - if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)) - return "number"; + if (ch === ".") { + stream.next(); + if (stream.match(/^[\w-]+/)) { + indent(state); + return "atom"; + } else if (stream.peek() === "#") { + indent(state); + return "atom"; + } + } - // ID selectors - if (stream.match(/^[\w-]+/)) { - indent(state); - return "atom"; + if (ch === "#") { + stream.next(); + // ID selectors + if (stream.match(/^[\w-]+/)) { + indent(state); + return "atom"; + } + if (stream.peek() === "#") { + indent(state); + return "atom"; + } } - if (stream.peek() === "#") { - indent(state); - return "atom"; + // Variables + if (ch === "$") { + stream.next(); + stream.eatWhile(/[\w-]/); + return "variable-2"; } - } - // Numbers - if (stream.match(/^-?[0-9\.]+/)) - return "number"; + // Numbers + if (stream.match(/^-?[0-9\.]+/)) + return "number"; + + // Units + if (stream.match(/^(px|em|in)\b/)) + return "unit"; - // Units - if (stream.match(/^(px|em|in)\b/)) - return "unit"; + if (stream.match(keywordsRegexp)) + return "keyword"; - if (stream.match(keywordsRegexp)) - return "keyword"; + if (stream.match(/^url/) && stream.peek() === "(") { + state.tokenizer = urlTokens; + return "atom"; + } - if (stream.match(/^url/) && stream.peek() === "(") { - state.tokenizer = urlTokens; - return "atom"; - } + if (ch === "=") { + // Match shortcut mixin definition + if (stream.match(/^=[\w-]+/)) { + indent(state); + return "meta"; + } + } - // Variables - if (ch === "$") { - stream.next(); - stream.eatWhile(/[\w-]/); - stream.eatSpace(); - if (stream.peek() === ":") - return "variable-2"; - else - return "variable-3"; - } + if (ch === "+") { + // Match shortcut mixin definition + if (stream.match(/^\+[\w-]+/)){ + return "variable-3"; + } + } - if (ch === "!") { - stream.next(); - return stream.match(/^[\w]+/) ? "keyword": "operator"; - } + if(ch === "@"){ + if(stream.match(/@extend/)){ + if(!stream.match(/\s*[\w]/)) + dedent(state); + } + } - if (ch === "=") { - stream.next(); - // Match shortcut mixin definition - if (stream.match(/^[\w-]+/)) { + // Indent Directives + if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) { indent(state); return "meta"; - } else { - return "operator"; } - } - if (ch === "+") { - stream.next(); + // Other Directives + if (ch === "@") { + stream.next(); + stream.eatWhile(/[\w-]/); + return "meta"; + } - // Match shortcut mixin definition - if (stream.match(/^[\w-]+/)) - return "variable-3"; - else + if (stream.eatWhile(/[\w-]/)){ + if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){ + return "propery"; + } + else if(stream.match(/ *:/,false)){ + indent(state); + state.cursorHalf = 1; + return "atom"; + } + else if(stream.match(/ *,/,false)){ + return "atom"; + } + else{ + indent(state); + return "atom"; + } + } + + if(ch === ":"){ + if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element + return "keyword"; + } + stream.next(); + state.cursorHalf=1; return "operator"; - } + } - // Indent Directives - if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) { - indent(state); - return "meta"; - } + } // cursorHalf===0 ends here + else{ - // Other Directives - if (ch === "@") { - stream.next(); - stream.eatWhile(/[\w-]/); - return "meta"; - } + if (ch === "#") { + stream.next(); + // Hex numbers + if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){ + if(!stream.peek()){ + state.cursorHalf = 0; + } + return "number"; + } + } - // Strings - if (ch === '"' || ch === "'") { - stream.next(); - state.tokenizer = buildStringTokenizer(ch); - return "string"; - } + // Numbers + if (stream.match(/^-?[0-9\.]+/)){ + if(!stream.peek()){ + state.cursorHalf = 0; + } + return "number"; + } + + // Units + if (stream.match(/^(px|em|in)\b/)){ + if(!stream.peek()){ + state.cursorHalf = 0; + } + return "unit"; + } - // Pseudo element selectors and values after colon - if (ch === ":"){ - if (stream.match(pseudoElementsRegexp)) + if (stream.match(keywordsRegexp)){ + if(!stream.peek()){ + state.cursorHalf = 0; + } return "keyword"; - stream.next(); - stream.eatSpace(); - if (stream.peek() === null){ - // if there is no more space after it - indent(state); - return "atom"; } - // all posible tokens after colon - if (stream.match(/\$[\w-]+/)){ - // variables - return "variable-3"; - } else if (stream.match(/^url/) && stream.peek() === "(") { - //urls + if (stream.match(/^url/) && stream.peek() === "(") { state.tokenizer = urlTokens; - return "atom"; - } else if (stream.match(/#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}/)){ - //hexadecimal value - return "number"; - } else if (stream.match(/^-?[0-9\.]+/)){ - return "number"; - } else if (stream.match(keywordsRegexp)){ - return "keyword"; - } else if (stream.match(/[\w-,\s]+/)){ - // other text + if(!stream.peek()){ + state.cursorHalf = 0; + } return "atom"; } - } - if (stream.match(opRegexp)) - return "operator"; + // Variables + if (ch === "$") { + stream.next(); + stream.eatWhile(/[\w-]/); + if(!stream.peek()){ + state.cursorHalf = 0; + } + return "variable-3"; + } - // atoms - if (stream.eatWhile(/[\w-&]/)) { - stream.eatSpace(); - // matches a property definition - if (stream.peek() === ":" ){ - if (!stream.match(/:[ ]*[\d\w-\$\+#!\("']/,false)){ - //for cases where line ends after colon - // eg - // font: - // | <-----cursor - indent(state); - return "property"; - } else if (!stream.match(pseudoElementsRegexp, false)){ - return "property"; + // bang character for !important, !default, etc. + if (ch === "!") { + stream.next(); + if(!stream.peek()){ + state.cursorHalf = 0; } - } else { - stream.eatSpace(); - if (stream.peek() !== "," ){ - //for cases where line ends after comma - //eg - //head, - //body, - //| <-----cursor - indent(state); + return stream.match(/^[\w]+/) ? "keyword": "operator"; + } + + if (stream.match(opRegexp)){ + if(!stream.peek()){ + state.cursorHalf = 0; } - return "atom"; + return "operator"; + } + + // attributes + if (stream.eatWhile(/[\w-]/)) { + if(!stream.peek()){ + state.cursorHalf = 0; + } + return "attribute"; } - } + + //stream.eatSpace(); + if(!stream.peek()){ + state.cursorHalf = 0; + return null; + } + + } // else ends here + + if (stream.match(opRegexp)) + return "operator"; // If we haven't returned by now, we move 1 character // and return an error @@ -319,11 +358,13 @@ CodeMirror.defineMode("sass", function(config) { var style = state.tokenizer(stream, state); var current = stream.current(); - if (current === "@return") + if (current === "@return" || current === "}"){ dedent(state); + } if (style !== null) { var startOfToken = stream.pos - current.length; + var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount); var newScopes = []; @@ -348,6 +389,8 @@ CodeMirror.defineMode("sass", function(config) { tokenizer: tokenBase, scopes: [{offset: 0, type: "sass"}], indentCount: 0, + cursorHalf: 0, // cursor half tells us if cursor lies after (1) + // or before (0) colon (well... more or less) definedVars: [], definedMixins: [] }; From 1bfa666170100a4b40c14766f11460838a9c3ad8 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 2 Jan 2015 13:11:24 +0100 Subject: [PATCH 05/30] [merge addon] Keep stable scroll position when removing/adding alignment padding See http://discuss.codemirror.net/t/jittering-mousewheel-scrolling-with-merge-addon-align-mode/125 --- addon/merge/merge.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addon/merge/merge.js b/addon/merge/merge.js index ed22b60024..3e9df42fb7 100644 --- a/addon/merge/merge.js +++ b/addon/merge/merge.js @@ -259,11 +259,13 @@ function makeConnections(dv) { if (!dv.showDifferences) return; - var align = dv.mv.options.connect == "align"; + var align = dv.mv.options.connect == "align", oldScrollEdit, oldScrollOrig; if (align) { if (!dv.orig.curOp) return dv.orig.operation(function() { makeConnections(dv); }); + oldScrollEdit = dv.edit.getScrollInfo().top; + oldScrollOrig = dv.orig.getScrollInfo().top; for (var i = 0; i < dv.aligners.length; i++) dv.aligners[i].clear(); dv.aligners.length = 0; @@ -293,6 +295,8 @@ dv.aligners.push(padBelow(dv.edit, 0, extraSpaceAbove.edit)); if (extraSpaceAbove.orig) dv.aligners.push(padBelow(dv.orig, 0, extraSpaceAbove.orig)); + dv.edit.scrollTo(null, oldScrollEdit); + dv.orig.scrollTo(null, oldScrollOrig); } } From 0d8d8555529c279002115842b82f03109165d476 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 3 Jan 2015 22:48:50 +0100 Subject: [PATCH 06/30] [simpelscrollbars addon] Properly detect end-of-drag on non-Webkit browsers Issue #3017 --- addon/scroll/simplescrollbars.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addon/scroll/simplescrollbars.js b/addon/scroll/simplescrollbars.js index 739aa7beb4..bb06adb86a 100644 --- a/addon/scroll/simplescrollbars.js +++ b/addon/scroll/simplescrollbars.js @@ -27,14 +27,16 @@ CodeMirror.e_preventDefault(e); var axis = self.orientation == "horizontal" ? "pageX" : "pageY"; var start = e[axis], startpos = self.pos; + function done() { + CodeMirror.off(document, "mousemove", move); + CodeMirror.off(document, "mouseup", done); + } function move(e) { - if (e.which != 1) { - CodeMirror.off(document, "mousemove", move); - return; - } + if (e.which != 1) return done(); self.moveTo(startpos + (e[axis] - start) * (self.total / self.size)); } CodeMirror.on(document, "mousemove", move); + CodeMirror.on(document, "mouseup", done); }); CodeMirror.on(this.node, "click", function(e) { From 5619d837ef00e68ad2c15c2fb2fd49ee71f035e8 Mon Sep 17 00:00:00 2001 From: tel Date: Sun, 4 Jan 2015 03:22:08 +0900 Subject: [PATCH 07/30] [clike mode] Fix GLSL keywords --- mode/clike/clike.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mode/clike/clike.js b/mode/clike/clike.js index 710953b229..b04b22b167 100644 --- a/mode/clike/clike.js +++ b/mode/clike/clike.js @@ -408,7 +408,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " + "mat2 mat3 mat4 " + "sampler1D sampler2D sampler3D samplerCube " + - "sampler1DShadow sampler2DShadow" + + "sampler1DShadow sampler2DShadow " + "const attribute uniform varying " + "break continue discard return " + "for while do if else struct " + @@ -416,7 +416,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { blockKeywords: words("for while do if else struct"), builtin: words("radians degrees sin cos tan asin acos atan " + "pow exp log exp2 sqrt inversesqrt " + - "abs sign floor ceil fract mod min max clamp mix step smootstep " + + "abs sign floor ceil fract mod min max clamp mix step smoothstep " + "length distance dot cross normalize ftransform faceforward " + "reflect refract matrixCompMult " + "lessThan lessThanEqual greaterThan greaterThanEqual " + @@ -433,12 +433,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " + "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " + "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " + - "gl_FogCoord " + + "gl_FogCoord gl_PointCoord " + "gl_Position gl_PointSize gl_ClipVertex " + "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " + "gl_TexCoord gl_FogFragCoord " + "gl_FragCoord gl_FrontFacing " + - "gl_FragColor gl_FragData gl_FragDepth " + + "gl_FragData gl_FragDepth " + "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " + "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " + "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " + From f97df90d832fa523d26f26131e6646567f5a83d0 Mon Sep 17 00:00:00 2001 From: Yunchi Luo Date: Sat, 3 Jan 2015 19:40:09 -0500 Subject: [PATCH 08/30] [vim] Check for falsy return from CodeMirror.keyname --- keymap/vim.js | 1 + 1 file changed, 1 insertion(+) diff --git a/keymap/vim.js b/keymap/vim.js index 913e3a63fd..88a404a291 100644 --- a/keymap/vim.js +++ b/keymap/vim.js @@ -4807,6 +4807,7 @@ var macroModeState = vimGlobalState.macroModeState; var lastChange = macroModeState.lastInsertModeChanges; var keyName = CodeMirror.keyName(e); + if (!keyName) { return; } function onKeyFound() { lastChange.changes.push(new InsertModeKey(keyName)); return true; From 0cb74f85f78e910409cc036f49fb64083f179703 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Jan 2015 09:09:48 +0100 Subject: [PATCH 09/30] Make eventInWidget work with text nodes Issue #3019 --- lib/codemirror.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 68a08b9419..f7c3817d30 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -2759,7 +2759,9 @@ // Return true when the given mouse event happened in a widget function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { - if (!n || n.getAttribute("cm-ignore-events") == "true" || n.parentNode == display.sizer && n != display.mover) return true; + if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || + (n.parentNode == display.sizer && n != display.mover)) + return true; } } From deb87f471b1e63eb145cf0a387d659da38ed1fd2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Jan 2015 16:30:43 +0100 Subject: [PATCH 10/30] Properly document that cursorCoords can be given "window" as mode --- doc/manual.html | 9 ++++++--- lib/codemirror.js | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index dc3cfe065e..ba1f89352c 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -1763,9 +1763,12 @@

Sizing, scrolling and positioning methods

If mode is "local", they will be relative to the top-left corner of the editable document. If it is "page" or not given, they are relative to the - top-left corner of the page. where can be a boolean - indicating whether you want the start (true) or the - end (false) of the selection, or, if a {line, + top-left corner of the page. If mode + is "window", the coordinates are relative to the + top-left corner of the currently visible (scrolled) + window. where can be a boolean indicating whether + you want the start (true) or the end + (false) of the selection, or, if a {line, ch} object is given, it specifies the precise position at which you want to measure.
cm.charCoords(pos: {line, ch}, ?mode: string) → {left, right, top, bottom}
diff --git a/lib/codemirror.js b/lib/codemirror.js index f7c3817d30..542fb41732 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -1847,7 +1847,8 @@ // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of - // "line", "div" (display.lineDiv), "local"/null (editor), or "page". + // "line", "div" (display.lineDiv), "local"/null (editor), "window", + // or "page". function intoCoordSystem(cm, lineObj, rect, context) { if (lineObj.widgets) for (var i = 0; i < lineObj.widgets.length; ++i) if (lineObj.widgets[i].above) { var size = widgetHeight(lineObj.widgets[i]); From 04dbeafb4dc224041670045bce08d607f2b2bafa Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 5 Jan 2015 17:56:46 +0100 Subject: [PATCH 11/30] [javascript mode] Also look at following text to determine continued statement Issue #3015 --- mode/javascript/javascript.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js index fac8d57769..93df06d152 100644 --- a/mode/javascript/javascript.js +++ b/mode/javascript/javascript.js @@ -600,6 +600,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "if") return cont(expression, comprehension); } + function isContinuedStatement(state, textAfter) { + return state.lastType == "operator" || state.lastType == "," || + isOperatorChar.test(textAfter.charAt(0)) || + /[,.]/.test(textAfter.charAt(0)); + } + // Interface return { @@ -651,7 +657,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form") return lexical.indented + indentUnit; else if (type == "stat") - return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0); + return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); From 9ee2cefa7f7eded5aba5cb297236038429e5036d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 7 Jan 2015 12:27:23 +0100 Subject: [PATCH 12/30] Don't fall back to native implemtation of map/indexOf They aren't faster, and don't work on pseudo-arrays. --- lib/codemirror.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 542fb41732..20c7329c7e 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -7467,13 +7467,11 @@ if (array[i] == elt) return i; return -1; } - if ([].indexOf) indexOf = function(array, elt) { return array.indexOf(elt); }; function map(array, f) { var out = []; for (var i = 0; i < array.length; i++) out[i] = f(array[i], i); return out; } - if ([].map) map = function(array, f) { return array.map(f); }; function createObj(base, props) { var inst; From f043c8db443a3f097b0ed7b2ac0fb5399f9312b3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 Jan 2015 18:06:39 +0100 Subject: [PATCH 13/30] Make setValue completely replace the document Issue #3023 --- lib/codemirror.js | 26 +++++++++++++++++--------- test/test.js | 8 ++++++++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/lib/codemirror.js b/lib/codemirror.js index 20c7329c7e..ed337635c7 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -3791,7 +3791,9 @@ var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display - if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) + if (change.full) + regChange(cm); + else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) regLineChange(cm, from.line, "text"); else regChange(cm, from.line, to.line + 1, lendiff); @@ -5573,6 +5575,7 @@ // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. function stretchSpansOverChange(doc, change) { + if (change.full) return null; var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) return null; @@ -6345,17 +6348,24 @@ updateLine(line, text, spans, estimateHeight); signalLater(line, "change", line, change); } + function linesFor(start, end) { + for (var i = start, result = []; i < end; ++i) + result.push(new Line(text[i], spansFor(i), estimateHeight)); + return result; + } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // Adjust the line structure - if (isWholeLineUpdate(doc, change)) { + if (change.full) { + doc.insert(0, linesFor(0, text.length)); + doc.remove(text.length, doc.size - text.length); + } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. - for (var i = 0, added = []; i < text.length - 1; ++i) - added.push(new Line(text[i], spansFor(i), estimateHeight)); + var added = linesFor(0, text.length - 1); update(lastLine, lastLine.text, lastSpans); if (nlines) doc.remove(from.line, nlines); if (added.length) doc.insert(from.line, added); @@ -6363,8 +6373,7 @@ if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { - for (var added = [], i = 1; i < text.length - 1; ++i) - added.push(new Line(text[i], spansFor(i), estimateHeight)); + var added = linesFor(1, text.length - 1); added.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added); @@ -6375,8 +6384,7 @@ } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); - for (var i = 1, added = []; i < text.length - 1; ++i) - added.push(new Line(text[i], spansFor(i), estimateHeight)); + var added = linesFor(1, text.length - 1); if (nlines > 1) doc.remove(from.line + 1, nlines - 1); doc.insert(from.line + 1, added); } @@ -6587,7 +6595,7 @@ setValue: docMethodOp(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"}, true); + text: splitLines(code), origin: "setValue", full: true}, true); setSelection(this, simpleSelection(top)); }), replaceRange: function(code, from, to, origin) { diff --git a/test/test.js b/test/test.js index 08d03810bc..6314598235 100644 --- a/test/test.js +++ b/test/test.js @@ -611,6 +611,14 @@ testCM("getAllMarks", function(cm) { eq(cm.getAllMarks().length, 2); }); +testCM("setValueClears", function(cm) { + cm.addLineClass(0, "wrap", "foo"); + var mark = cm.markText(Pos(0, 0), Pos(1, 1), {inclusiveLeft: true, inclusiveRight: true}); + cm.setValue("foo"); + is(!cm.lineInfo(0).wrapClass); + is(!mark.find()); +}, {value: "a\nb"}); + testCM("bug577", function(cm) { cm.setValue("a\nb"); cm.clearHistory(); From 5dd07d60b8b142ca41d336adfccbd7cdd7acdb49 Mon Sep 17 00:00:00 2001 From: Marek Rudnicki Date: Fri, 9 Jan 2015 15:43:43 +0100 Subject: [PATCH 14/30] [emacs keymap] Add C-X H for selectAll --- keymap/emacs.js | 1 + 1 file changed, 1 insertion(+) diff --git a/keymap/emacs.js b/keymap/emacs.js index 6a8bb4a2f7..c4135237e1 100644 --- a/keymap/emacs.js +++ b/keymap/emacs.js @@ -395,6 +395,7 @@ "Ctrl-X U": repeated("undo"), "Ctrl-X K": "close", "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), true); }, + "Ctrl-X H": "selectAll", "Ctrl-Q Tab": repeated("insertTab"), "Ctrl-U": addPrefixMap From 3cf2e251a3037089a626f53facfe91bfe65d790b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 9 Jan 2015 18:15:00 +0100 Subject: [PATCH 15/30] Bump version number post-4.11 --- bower.json | 2 +- doc/manual.html | 2 +- lib/codemirror.js | 2 +- package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bower.json b/bower.json index 318a2454f1..16aa2e37dd 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"4.11.0", + "version":"4.11.1", "main": ["lib/codemirror.js", "lib/codemirror.css"], "ignore": [ "**/.*", diff --git a/doc/manual.html b/doc/manual.html index 906646e986..4298a17d67 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -63,7 +63,7 @@

User manual and reference guide - version 4.11.0 + version 4.11.1

CodeMirror is a code-editor component that can be embedded in diff --git a/lib/codemirror.js b/lib/codemirror.js index 893a5f54d6..c8612d0b5a 100644 --- a/lib/codemirror.js +++ b/lib/codemirror.js @@ -8032,7 +8032,7 @@ // THE END - CodeMirror.version = "4.11.0"; + CodeMirror.version = "4.11.1"; return CodeMirror; }); diff --git a/package.json b/package.json index c011d782cf..57f12bba7b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "codemirror", - "version":"4.11.0", + "version":"4.11.1", "main": "lib/codemirror.js", "description": "In-browser code editing made bearable", "licenses": [{"type": "MIT", From 6d8de27e7cf987015b52c44c6b5df97993ef3307 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 12 Jan 2015 10:56:34 +0100 Subject: [PATCH 16/30] [foldgutter addon] Use same option defaulting strategy as foldcode addon --- addon/fold/foldcode.js | 4 ++++ addon/fold/foldgutter.js | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js index 3abeb83e76..62911f9352 100644 --- a/addon/fold/foldcode.js +++ b/addon/fold/foldcode.js @@ -142,4 +142,8 @@ return editorOptions[name]; return defaultOptions[name]; } + + CodeMirror.defineExtension("foldOption", function(options, name) { + return getOption(this, options, name); + }); }); diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js index bd31ec4d9b..3359476796 100644 --- a/addon/fold/foldgutter.js +++ b/addon/fold/foldgutter.js @@ -67,14 +67,16 @@ function updateFoldInfo(cm, from, to) { var opts = cm.state.foldGutter.options, cur = from; + var minSize = cm.foldOption(opts, "minFoldSize"); + var func = cm.foldOption(opts, "rangeFinder"); cm.eachLine(from, to, function(line) { var mark = null; if (isFolded(cm, cur)) { mark = marker(opts.indicatorFolded); } else { - var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto; + var pos = Pos(cur, 0); var range = func && func(cm, pos); - if (range && range.from.line + 1 < range.to.line) + if (range && range.to.line - range.from.line >= minSize) mark = marker(opts.indicatorOpen); } cm.setGutterMarker(line, opts.gutter, mark); From f12f5733d57d6ced95f2487bfcddf757245c1ea2 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 13 Jan 2015 10:35:34 +0100 Subject: [PATCH 17/30] [closetag addon] Define a closeTag command that closes the nearest open tag Closes #3028 --- addon/edit/closetag.js | 21 ++++++++++++++------- doc/manual.html | 8 ++++++-- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js index a0bec7dd43..369bea30c4 100644 --- a/addon/edit/closetag.js +++ b/addon/edit/closetag.js @@ -94,15 +94,15 @@ } } - function autoCloseSlash(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; + function autoCloseCurrent(cm, typingSlash) { var ranges = cm.listSelections(), replacements = []; + var head = typingSlash ? "/" : ""; + replacements[i] = head + "script>"; else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css") - replacements[i] = "/style>"; + replacements[i] = head + "style>"; else return CodeMirror.Pass; } else { if (!state.context || !state.context.tagName || closingTagExists(cm, state.context.tagName, pos, state)) return CodeMirror.Pass; - replacements[i] = "/" + state.context.tagName + ">"; + replacements[i] = head + state.context.tagName + ">"; } } cm.replaceSelections(replacements); @@ -129,6 +129,13 @@ cm.indentLine(ranges[i].head.line); } + function autoCloseSlash(cm) { + if (cm.getOption("disableInput")) return CodeMirror.Pass; + autoCloseCurrent(cm, true); + } + + CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); }; + function indexOf(collection, elt) { if (collection.indexOf) return collection.indexOf(elt); for (var i = 0, e = collection.length; i < e; ++i) diff --git a/doc/manual.html b/doc/manual.html index 4298a17d67..a70ab6b02f 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2232,8 +2232,12 @@

Addons

squiggly underline style for this class.
edit/closetag.js
-
Provides utility functions for adding automatic tag closing - to XML modes. See +
Defines an autoCloseTags option that will + auto-close XML tags when '>' or '/' + is typed, and + a closeTag command that + closes the nearest open tag. Depends on + the fold/xml-fold.js addon. See the demo.
edit/continuelist.js
From 78e06acf0d9a04b81bd9aa667a9a98c7413c5e70 Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 12 Jan 2015 17:27:04 +0100 Subject: [PATCH 18/30] [meta] Add findModeByFileName, several meta improvements --- doc/manual.html | 9 +++++---- mode/meta.js | 41 ++++++++++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/doc/manual.html b/doc/manual.html index a70ab6b02f..051039bd03 100644 --- a/doc/manual.html +++ b/doc/manual.html @@ -2658,12 +2658,13 @@

Addons

properties mimes, which holds an array of MIME types for modes with multiple MIMEs associated, and ext, which holds an array of file extensions - associated with this mode. Three convenience + associated with this mode. Four convenience functions, CodeMirror.findModeByMIME, - and CodeMirror.findModeByExtension, + CodeMirror.findModeByExtension, + CodeMirror.findModeByFileName and CodeMirror.findModeByName are provided, which - return such an object given a MIME or extension string. Note - that, for historical reasons, this file resides in the + return such an object given a MIME, extension, file name or mode name + string. Note that, for historical reasons, this file resides in the top-level mode directory, not under addon. Demo. diff --git a/mode/meta.js b/mode/meta.js index fcaed98e2d..8d91df7d77 100644 --- a/mode/meta.js +++ b/mode/meta.js @@ -13,15 +13,15 @@ CodeMirror.modeInfo = [ {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]}, - {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk"}, + {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i}, {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]}, - {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "hpp", "h++"], alias: ["cpp"]}, + {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]}, {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]}, {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]}, {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, - {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher"}, + {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]}, {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]}, {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]}, {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]}, @@ -29,7 +29,7 @@ {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]}, {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]}, {name: "Django", mime: "text/x-django", mode: "django"}, - {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile"}, + {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile", file: /^Dockerfile$/}, {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]}, {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]}, {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"}, @@ -42,7 +42,7 @@ {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]}, {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]}, {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]}, - {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm"}, + {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i}, {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]}, {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]}, {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]}, @@ -59,7 +59,7 @@ {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"], mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]}, {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]}, - {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", alias: ["jsonld"]}, + {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]}, {name: "Jinja2", mime: "null", mode: "jinja2"}, {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, {name: "Kotlin", mime: "text/x-kotlin", mode: "kotlin", ext: ["kt"]}, @@ -72,18 +72,18 @@ {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]}, {name: "MS SQL", mime: "text/x-mssql", mode: "sql"}, {name: "MySQL", mime: "text/x-mysql", mode: "sql"}, - {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx"}, + {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i}, {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]}, {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]}, {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]}, {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]}, {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]}, - {name: "PEG.js", mime: "null", mode: "pegjs"}, + {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]}, {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]}, {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]}, - {name: "Pig", mime: "text/x-pig", mode: "pig"}, + {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]}, {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]}, - {name: "PLSQL", mime: "text/x-plsql", mode: "sql"}, + {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]}, {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]}, {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]}, {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]}, @@ -99,8 +99,8 @@ {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]}, {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]}, {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"]}, - {name: "Sieve", mime: "application/sieve", mode: "sieve"}, - {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim"}, + {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]}, + {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]}, {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]}, {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]}, {name: "SmartyMixed", mime: "text/x-smarty", mode: "smartymixed"}, @@ -114,15 +114,15 @@ {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]}, {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]}, {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]}, - {name: "Textile", mime: "text/x-textile", mode: "textile"}, + {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]}, {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"}, - {name: "TOML", mime: "text/x-toml", mode: "toml"}, + {name: "TOML", mime: "text/x-toml", mode: "toml", ext: ["toml"]}, {name: "Tornado", mime: "text/x-tornado", mode: "tornado"}, {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]}, {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]}, {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]}, - {name: "VBScript", mime: "text/vbscript", mode: "vbscript"}, + {name: "VBScript", mime: "text/vbscript", mode: "vbscript", ext: ["vbs"]}, {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]}, {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]}, {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]}, @@ -137,6 +137,7 @@ } CodeMirror.findModeByMIME = function(mime) { + mime = mime.toLowerCase(); for (var i = 0; i < CodeMirror.modeInfo.length; i++) { var info = CodeMirror.modeInfo[i]; if (info.mime == mime) return info; @@ -153,6 +154,16 @@ } }; + CodeMirror.findModeByFileName = function(filename) { + for (var i = 0; i < CodeMirror.modeInfo.length; i++) { + var info = CodeMirror.modeInfo[i]; + if (info.file && info.file.test(filename)) return info; + } + var dot = filename.lastIndexOf("."); + var ext = dot > -1 && filename.substring(dot + 1, filename.length); + if (ext) return CodeMirror.findModeByExtension(ext); + }; + CodeMirror.findModeByName = function(name) { name = name.toLowerCase(); for (var i = 0; i < CodeMirror.modeInfo.length; i++) { From 643d9c05a301b765d1bc8c6877434f35fc2a582a Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 14 Jan 2015 14:14:06 +0100 Subject: [PATCH 19/30] Use blint for linting, rather than in-tree tool (Blint is the in-tree tool factored into its own npm package.) --- bin/lint | 15 +- package.json | 3 +- test/lint.js | 11 + test/lint/acorn.js | 1782 ---------------------------------------------------- test/lint/lint.js | 166 ----- test/lint/walk.js | 313 --------- test/run.js | 9 +- 7 files changed, 15 insertions(+), 2284 deletions(-) create mode 100644 test/lint.js delete mode 100644 test/lint/acorn.js delete mode 100644 test/lint/lint.js delete mode 100644 test/lint/walk.js diff --git a/bin/lint b/bin/lint index 4f70994c51..47f45f3676 100755 --- a/bin/lint +++ b/bin/lint @@ -1,16 +1,3 @@ #!/usr/bin/env node -var lint = require("../test/lint/lint"), - path = require("path"); - -if (process.argv.length > 2) { - lint.checkDir(process.argv[2]); -} else { - process.chdir(path.resolve(__dirname, "..")); - lint.checkDir("lib"); - lint.checkDir("mode"); - lint.checkDir("addon"); - lint.checkDir("keymap"); -} - -process.exit(lint.success() ? 0 : 1); +process.exit(require("../test/lint").ok ? 0 : 1); diff --git a/package.json b/package.json index 57f12bba7b..b8cb39d0c5 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "directories": {"lib": "./lib"}, "scripts": {"test": "node ./test/run.js"}, "devDependencies": {"node-static": "0.6.0", - "phantomjs": "1.9.2-5"}, + "phantomjs": "1.9.2-5", + "blint": "^0.1.0"}, "bugs": "http://github.com/codemirror/CodeMirror/issues", "keywords": ["JavaScript", "CodeMirror", "Editor"], "homepage": "http://codemirror.net", diff --git a/test/lint.js b/test/lint.js new file mode 100644 index 0000000000..35777562ef --- /dev/null +++ b/test/lint.js @@ -0,0 +1,11 @@ +var blint = require("blint"); + +["mode", "lib", "addon", "keymap"].forEach(function(dir) { + blint.checkDir(dir, { + browser: true, + allowedGlobals: ["CodeMirror", "define", "test", "requirejs"], + blob: "// CodeMirror, copyright (c) by Marijn Haverbeke and others\n// Distributed under an MIT license: http:\/\/codemirror.net\/LICENSE\n\n" + }); +}); + +module.exports = {ok: blint.success()}; diff --git a/test/lint/acorn.js b/test/lint/acorn.js deleted file mode 100644 index 0f11931752..0000000000 --- a/test/lint/acorn.js +++ /dev/null @@ -1,1782 +0,0 @@ -// Acorn is a tiny, fast JavaScript parser written in JavaScript. -// -// Acorn was written by Marijn Haverbeke and released under an MIT -// license. The Unicode regexps (for identifiers and whitespace) were -// taken from [Esprima](http://esprima.org) by Ariya Hidayat. -// -// Git repositories for Acorn are available at -// -// http://marijnhaverbeke.nl/git/acorn -// https://github.com/marijnh/acorn.git -// -// Please use the [github bug tracker][ghbt] to report issues. -// -// [ghbt]: https://github.com/marijnh/acorn/issues -// -// 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.4.1"; - - // 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 - // (`let`, `yield`, inline XML, etc) is not recognized. - // - // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API - - var options, input, inputLen, sourceFile; - - exports.parse = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - return parseTopLevel(options.program); - }; - - // A second optional argument can be given to further configure - // the parser process. These options are recognized: - - var defaultOptions = exports.defaultOptions = { - // `ecmaVersion` indicates the ECMAScript version to parse. Must - // be either 3 or 5. This - // influences support for strict mode, the set of reserved words, and - // support for getters and setter. - ecmaVersion: 5, - // Turn on `strictSemicolons` to prevent the parser from doing - // automatic semicolon insertion. - strictSemicolons: false, - // When `allowTrailingCommas` is false, the parser will not allow - // trailing commas in array and object literals. - allowTrailingCommas: true, - // By default, reserved words are not enforced. Enable - // `forbidReserved` to enforce them. When this option has the - // value "everywhere", reserved words and keywords can also not be - // used as property names. - forbidReserved: false, - // When enabled, a return at the top level is not considered an - // error. - allowReturnOutsideFunction: 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 - // [semi-standardized][range] `range` property holding a `[start, - // end]` array with the same numbers, set the `ranges` option to - // `true`. - // - // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 - ranges: false, - // It is possible to parse multiple files into a single AST by - // passing the tree produced by parsing the first file as - // `program` option in subsequent parses. This will add the - // toplevel forms of the parsed file to the `Program` (top) node - // of an existing parse tree. - program: null, - // When `locations` is on, you can pass this to record the source - // file in every node's `loc` object. - 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 - // offset. `input` should be the code string that the offset refers - // into. - - var getLineInfo = exports.getLineInfo = function(input, offset) { - for (var line = 1, cur = 0;;) { - lineBreak.lastIndex = cur; - var match = lineBreak.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else break; - } - return {line: line, column: offset - cur}; - }; - - // Acorn is organized as a tokenizer and a recursive-descent parser. - // 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) { - lastEnd = tokEnd; - 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. - - var tokPos; - - // The start and end offsets of the current token. - - var tokStart, tokEnd; - - // When `options.locations` is true, these hold objects - // containing the tokens start and end line/column pairs. - - var tokStartLoc, tokEndLoc; - - // The type and value of the current token. Token types are objects, - // named by variables against which they can be compared, and - // holding properties that describe them (indicating, for example, - // the precedence of an infix operator, and the original name of a - // keyword token). The kind of value that's held in `tokVal` depends - // on the type of the token. For literals, it is the literal value, - // for operators, the operator name, and so on. - - var tokType, tokVal; - - // 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. - // (If it is, a slash is probably a regexp, if it isn't it's a - // division operator. See the `parseStatement` function for a - // caveat.) - - 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. - - var tokCurLine, tokLineStart; - - // These store the position of the previous token, which is useful - // when finishing a node and assigning its `end` position. - - var lastStart, lastEnd, lastEndLoc; - - // This is the parser's state. `inFunction` is used to reject - // `return` statements outside of functions, `labels` to verify that - // `break` and `continue` have somewhere to jump to, and `strict` - // indicates whether strict mode is on. - - var inFunction, labels, strict; - - // This function is used to raise exceptions on parse errors. It - // 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) { - 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 - // allows the tokenizer to store the information it has about a - // token in a way that is very cheap for the parser to look up. - - // All token type variables start with an underscore, to make them - // easy to recognize. - - // These are the general types. The `type` property is only used to - // make them recognizeable when debugging. - - var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"}; - var _name = {type: "name"}, _eof = {type: "eof"}; - - // Keyword tokens. The `keyword` property (also used in keyword-like - // operators) indicates that the token originated from an - // identifier-like word, which is used when parsing property names. - // - // The `beforeExpr` property is used to disambiguate between regular - // expressions and divisions. It is set on all token types that can - // be followed by an expression (thus, a slash after them would be a - // regular expression). - // - // `isLoop` marks a keyword as starting a loop, which is important - // to know when parsing a label, in order to allow or disallow - // continue jumps to that label. - - var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"}; - var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"}; - var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true}; - var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"}; - var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"}; - var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"}; - var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true}; - var _this = {keyword: "this"}; - - // The keywords that denote values. - - var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true}; - var _false = {keyword: "false", atomValue: false}; - - // Some keywords are treated as regular operators. `in` sometimes - // (when parsing `for`) needs to be tested against specifically, so - // we assign a variable name to it for quick comparing. - - var _in = {keyword: "in", binop: 7, beforeExpr: true}; - - // Map keyword names to token types. - - var keywordTypes = {"break": _break, "case": _case, "catch": _catch, - "continue": _continue, "debugger": _debugger, "default": _default, - "do": _do, "else": _else, "finally": _finally, "for": _for, - "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, 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. - - var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true}; - var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"}; - var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true}; - var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true}; - - // Operators. These carry several kinds of properties to help the - // parser use them properly (the presence of these properties is - // what categorizes them as operators). - // - // `binop`, when present, specifies that this operator is a binary - // operator, and will refer to its precedence. - // - // `prefix` and `postfix` mark the operator as a prefix or postfix - // unary operator. `isUpdate` specifies that the node produced by - // the operator should be of type UpdateExpression rather than - // simply UnaryExpression (`++` and `--`). - // - // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as - // binary operators with a very low precedence, that should result - // in AssignmentExpression nodes. - - var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, 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 - // predicate containing a big ugly `switch` statement is faster than - // a regular expression, and on Chrome the two are about on par. - // This function uses `eval` (non-lexical) to produce such a - // predicate from a space-separated string of words. - // - // It starts by sorting the words by length. - - function makePredicate(words) { - words = words.split(" "); - var f = "", cats = []; - out: for (var i = 0; i < words.length; ++i) { - for (var j = 0; j < cats.length; ++j) - if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; - } - cats.push([words[i]]); - } - function compareTo(arr) { - if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; - f += "switch(str){"; - for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; - f += "return true}return false;"; - } - - // When there are more than three length categories, an outer - // switch first dispatches on the lengths, to save on comparisons. - - if (cats.length > 3) { - cats.sort(function(a, b) {return b.length - a.length;}); - f += "switch(str.length){"; - for (var i = 0; i < cats.length; ++i) { - var cat = cats[i]; - f += "case " + cat[0].length + ":"; - compareTo(cat); - } - f += "}"; - - // Otherwise, simply generate a flat `switch` statement. - - } else { - compareTo(words); - } - return new Function("str", f); - } - - // The ECMAScript 3 reserved word list. - - var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"); - - // ECMAScript 5 reserved words. - - var isReservedWord5 = makePredicate("class enum extends super const export import"); - - // The additional reserved words in strict mode. - - var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield"); - - // The forbidden variable names in strict mode. - - var isStrictBadIdWord = makePredicate("eval arguments"); - - // And the keywords. - - var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"); - - // ## Character categories - - // Big ugly regular expressions that match characters in the - // whitespace, identifier, and identifier-start categories. These - // are only applied when a character is found to actually have a - // code point above 128. - - 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 = "\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 + "]"); - - // Whether a single character denotes a newline. - - var newline = /[\n\r\u2028\u2029]/; - - // Matches a whole line break (where CRLF is considered a single - // line break). Used to count lines. - - var lineBreak = /\r\n|[\n\r\u2028\u2029]/g; - - // Test whether a given character code starts an identifier. - - 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. - - var isIdentifierChar = exports.isIdentifierChar = function(code) { - if (code < 48) return code === 36; - if (code < 58) return true; - if (code < 65) return false; - if (code < 91) return true; - 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, for the - // `tokStartLoc` and `tokEndLoc` properties. - - function line_loc_t() { - this.line = tokCurLine; - this.column = tokPos - tokLineStart; - } - - // Reset the token state. Used at the start of a parse. - - function initTokenState() { - tokCurLine = 1; - tokPos = tokLineStart = 0; - tokRegexpAllowed = true; - skipSpace(); - } - - // 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 = new line_loc_t; - tokType = type; - skipSpace(); - tokVal = val; - tokRegexpAllowed = type.beforeExpr; - } - - function skipBlockComment() { - 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"); - 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 !== 8233) { - ++tokPos; - ch = input.charCodeAt(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. - - function skipSpace() { - while (tokPos < inputLen) { - var ch = input.charCodeAt(tokPos); - 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 === 160) { // '\xa0' - ++tokPos; - } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++tokPos; - } else { - break; - } - } - } - - // ### Token reading - - // This is the function that is called to fetch the next token. It - // is somewhat obscure, because it works in character codes rather - // than characters, and because operator parsing has been inlined - // into it. - // - // All in the name of speed. - // - // The `forceRegexp` parameter is used in the one case where the - // `tokRegexpAllowed` trick does not work. See `parseStatement`. - - function readToken_dot() { - var next = input.charCodeAt(tokPos + 1); - if (next >= 48 && next <= 57) return readNumber(true); - ++tokPos; - return finishToken(_dot); - } - - 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) { - // `