2 changes: 1 addition & 1 deletion mode/lua/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>CodeMirror: Lua mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="../../lib/codemirror.js"></script>
<script src="lua.js"></script>
<link rel="stylesheet" href="../../theme/neat.css">
Expand Down
2 changes: 1 addition & 1 deletion mode/markdown/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: Markdown mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/continuelist.js"></script>
<script src="../../addon/edit/continuelist.js"></script>
<script src="../xml/xml.js"></script>
<script src="markdown.js"></script>
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
Expand Down
15 changes: 8 additions & 7 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
"c++": "text/x-c++src",
java: "text/x-java",
csharp: "text/x-csharp",
"c#": "text/x-csharp"
"c#": "text/x-csharp",
scala: "text/x-scala"
};

var getMode = (function () {
Expand Down Expand Up @@ -246,8 +247,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return getType(state);
}

if (ch === '!' && stream.match(/\[.*\] ?(?:\(|\[)/, false)) {
stream.match(/\[.*\]/);
if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
stream.match(/\[[^\]]*\]/);
state.inline = state.f = linkHref;
return image;
}
Expand Down Expand Up @@ -456,10 +457,10 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
var difference = Math.floor((indentation - state.indentation) / 4) * 4;
if (difference > 4) difference = 4;
indentation = state.indentation + difference;
state.indentationDiff = indentation - state.indentation;
state.indentation = indentation;
if (indentation > 0) { return null; }
var adjustedIndentation = state.indentation + difference;
state.indentationDiff = adjustedIndentation - state.indentation;
state.indentation = adjustedIndentation;
if (indentation > 0) return null;
}
return state.f(stream, state);
},
Expand Down
1,845 changes: 579 additions & 1,266 deletions mode/markdown/test.js

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions mode/meta.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
CodeMirror.modeInfo = [
{name: 'APL', mime: 'text/apl', mode: 'apl'},
{name: 'Asterisk', mime: 'text/x-asterisk', mode: 'asterisk'},
{name: 'C', mime: 'text/x-csrc', mode: 'clike'},
{name: 'C++', mime: 'text/x-c++src', mode: 'clike'},
{name: 'Java', mime: 'text/x-java', mode: 'clike'},
{name: 'C#', mime: 'text/x-csharp', mode: 'clike'},
{name: 'Scala', mime: 'text/x-scala', mode: 'clike'},
{name: 'Clojure', mime: 'text/x-clojure', mode: 'clojure'},
{name: 'CoffeeScript', mime: 'text/x-coffeescript', mode: 'coffeescript'},
{name: 'Common Lisp', mime: 'text/x-common-lisp', mode: 'commonlisp'},
{name: 'CSS', mime: 'text/css', mode: 'css'},
{name: 'D', mime: 'text/x-d', mode: 'd'},
{name: 'diff', mime: 'text/x-diff', mode: 'diff'},
{name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
{name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
{name: 'GitHub Flavored Markdown', mode: 'gfm'},
{name: 'GO', mime: 'text/x-go', mode: 'go'},
{name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
{name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
{name: 'Haxe', mime: 'text/x-haxe', mode: 'haxe'},
{name: 'ASP.NET', mime: 'application/x-aspx', mode: 'htmlembedded'},
{name: 'Embedded Javascript', mime: 'application/x-ejs', mode: 'htmlembedded'},
{name: 'JavaServer Pages', mime: 'application/x-jsp', mode: 'htmlembedded'},
{name: 'HTML', mime: 'text/html', mode: 'htmlmixed'},
{name: 'HTTP', mime: 'message/http', mode: 'http'},
{name: 'JavaScript', mime: 'text/javascript', mode: 'javascript'},
{name: 'JSON', mime: 'application/json', mode: 'javascript'},
{name: 'TypeScript', mime: 'application/typescript', mode: 'javascript'},
{name: 'Jinja2', mime: 'jinja2', mode: 'jinja2'},
{name: 'LESS', mime: 'text/x-less', mode: 'less'},
{name: 'Lua', mime: 'text/x-lua', mode: 'lua'},
{name: 'Markdown (GitHub-flavour)', mime: 'text/x-markdown', mode: 'markdown'},
{name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'},
{name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'},
{name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'},
{name: 'Perl', mime: 'text/x-perl', mode: 'perl'},
{name: 'PHP', mime: 'text/x-php', mode: 'php'},
{name: 'PHP(HTML)', mime: 'application/x-httpd-php', mode: 'php'},
{name: 'Pig', mime: 'text/x-pig', mode: 'pig'},
{name: 'Properties files', mime: 'text/x-properties', mode: 'clike'},
{name: 'Python', mime: 'text/x-python', mode: 'python'},
{name: 'R', mime: 'text/x-rsrc', mode: 'r'},
{name: 'reStructuredText', mime: 'text/x-rst', mode: 'rst'},
{name: 'Ruby', mime: 'text/x-ruby', mode: 'ruby'},
{name: 'Rust', mime: 'text/x-rustsrc', mode: 'rust'},
{name: 'Sass', mime: 'text/x-sass', mode: 'sass'},
{name: 'Scheme', mime: 'text/x-scheme', mode: 'scheme'},
{name: 'Shell', mime: 'text/x-sh', mode: 'shell'},
{name: 'Sieve', mime: 'application/sieve', mode: 'sieve'},
{name: 'Smalltalk', mime: 'text/x-stsrc', mode: 'smalltalk'},
{name: 'Smarty', mime: 'text/x-smarty', mode: 'smarty'},
{name: 'SPARQL', mime: 'application/x-sparql-query', mode: 'sparql'},
{name: 'SQL', mime: 'text/x-sql', mode: 'sql'},
{name: 'MySQL', mime: 'text/x-mysql', mode: 'sql'},
{name: 'MariaDB', mime: 'text/x-mariadb', mode: 'sql'},
{name: 'PL/SQL', mime: 'text/x-plsql', mode: 'sql'},
{name: 'sTeX', mime: 'text/x-stex', mode: 'stex'},
{name: 'LaTeX', mime: 'text/x-latex', mode: 'stex'},
{name: 'TiddlyWiki ', mime: 'text/x-tiddlywiki', mode: 'tiddlwiki'},
{name: 'Tiki wiki', mime: 'text/tiki', mode: 'tiki'},
{name: 'VB.NET', mime: 'text/x-vb', mode: 'vb'},
{name: 'VBScript', mime: 'text/vbscript', mode: 'vbscript'},
{name: 'Velocity', mime: 'text/velocity', mode: 'velocity'},
{name: 'Verilog', mime: 'text/x-verilog', mode: 'verilog'},
{name: 'XML', mime: 'application/xml', mode: 'xml'},
{name: 'HTML', mime: 'text/html', mode: 'xml'},
{name: 'XQuery', mime: 'application/xquery', mode: 'xquery'},
{name: 'YAML', mime: 'text/x-yaml', mode: 'yaml'},
{name: 'Z80', mime: 'text/x-z80', mode: 'z80'}
];
2 changes: 2 additions & 0 deletions mode/mysql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ <h1>CodeMirror: MySQL mode</h1>
});
</script>

<p><strong><span style="color: red">!!</span> This mode is deprecated in favor of the <a href="../sql/index.html">generic SQL mode</a> (which can be configured to handle MySQL).</strong></p>

<p><strong>MIME types defined:</strong> <code>text/x-mysql</code>.</p>

</body>
Expand Down
2 changes: 1 addition & 1 deletion mode/ocaml/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</style>

<script src=../../lib/codemirror.js></script>
<script src=../../lib/util/matchbrackets.js></script>
<script src=../../addon/edit/matchbrackets.js></script>
<script src=ocaml.js></script>

<h1>CodeMirror: OCaml mode</h1>
Expand Down
2 changes: 1 addition & 1 deletion mode/php/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: PHP mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="../htmlmixed/htmlmixed.js"></script>
<script src="../xml/xml.js"></script>
<script src="../javascript/javascript.js"></script>
Expand Down
2 changes: 2 additions & 0 deletions mode/plsql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ <h1>CodeMirror: Oracle PL/SQL mode</h1>
});
</script>

<p><strong><span style="color: red">!!</span> This mode is deprecated in favor of the <a href="../sql/index.html">generic SQL mode</a> (which can be configured to handle PL/SQL).</strong></p>

<p>
Simple mode that handles Oracle PL/SQL language (and Oracle SQL, of course).
</p>
Expand Down
2 changes: 1 addition & 1 deletion mode/python/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: Python mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="python.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
Expand Down
2 changes: 1 addition & 1 deletion mode/ruby/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: Ruby mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="ruby.js"></script>
<style>
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
Expand Down
54 changes: 54 additions & 0 deletions mode/sass/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Sass mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="sass.js"></script>
<style>.CodeMirror {border: 1px solid #ddd; font-size:12px; height: 400px}</style>
<link rel="stylesheet" href="../../doc/docs.css">
</head>
<body>
<h1>CodeMirror: Sass mode</h1>
<form><textarea id="code" name="code">// Variable Definitions

$page-width: 800px
$sidebar-width: 200px
$primary-color: #eeeeee

// Global Attributes

body
font:
family: sans-serif
size: 30em
weight: bold

// Scoped Styles

#contents
width: $page-width
#sidebar
float: right
width: $sidebar-width
#main
width: $page-width - $sidebar-width
background: $primary-color
h2
color: blue

#footer
height: 200px
</textarea></form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers : true,
matchBrackets : true
});
</script>

<p><strong>MIME types defined:</strong> <code>text/x-sass</code>.</p>
</body>
</html>
349 changes: 349 additions & 0 deletions mode/sass/sass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,349 @@
CodeMirror.defineMode("sass", function(config) {
var tokenRegexp = function(words){
return new RegExp("^" + words.join("|"));
};

var tags = ["&", "a","abbr","acronym","address","applet","area","article","aside","audio","b","base","basefont","bdi","bdo","big","blockquote","body","br","button","canvas","caption","cite","code","col","colgroup","command","datalist","dd","del","details","dfn","dir","div","dl","dt","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","keygen","kbd","label","legend","li","link","map","mark","menu","meta","meter","nav","noframes","noscript","object","ol","optgroup","option","output","p","param","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strike","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","track","tt","u","ul","var","video","wbr"];
var keywords = ["true", "false", "null", "auto"];
var keywordsRegexp = new RegExp("^" + keywords.join("|"));

var operators = ["\\(", "\\)", "=", ">", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"];
var opRegexp = tokenRegexp(operators);

function htmlTag(val){
for(var i=0; i<tags.length; i++){
if(val === tags[i]){
return true;
}
}
}


var pseudoElements = [':first-line', ':hover', ':first-letter', ':active', ':visited', ':before', ':after', ':link', ':focus', ':first-child', ':lang'];
var pseudoElementsRegexp = new RegExp("^(" + pseudoElements.join("\\b|") + ")");

var urlTokens = function(stream, state){
var ch = stream.peek();

if (ch === ")"){
stream.next();
state.tokenizer = tokenBase;
return "operator";
}else if (ch === "("){
stream.next();
stream.eatSpace();

return "operator";
}else if (ch === "'" || ch === '"'){
state.tokenizer = buildStringTokenizer(stream.next());
return "string";
}else{
state.tokenizer = buildStringTokenizer(")", false);
return "string";
}
};
var multilineComment = function(stream, state) {
if (stream.skipTo("*/")){
stream.next();
stream.next();
state.tokenizer = tokenBase;
}else {
stream.next();
}

return "comment";
};

var buildStringTokenizer = function(quote, greedy){
if(greedy == null){ greedy = true; }

function stringTokenizer(stream, state){
var nextChar = stream.next();
var peekChar = stream.peek();
var previousChar = stream.string.charAt(stream.pos-2);

var endingString = ((nextChar !== "\\" && peekChar === quote) || (nextChar === quote && previousChar !== "\\"));

/*
console.log("previousChar: " + previousChar);
console.log("nextChar: " + nextChar);
console.log("peekChar: " + peekChar);
console.log("ending: " + endingString);
*/

if (endingString){
if (nextChar !== quote && greedy) { stream.next(); }
state.tokenizer = tokenBase;
return "string";
}else if (nextChar === "#" && peekChar === "{"){
state.tokenizer = buildInterpolationTokenizer(stringTokenizer);
stream.next();
return "operator";
}else {
return "string";
}
}

return stringTokenizer;
};

var buildInterpolationTokenizer = function(currentTokenizer){
return function(stream, state){
if (stream.peek() === "}"){
stream.next();
state.tokenizer = currentTokenizer;
return "operator";
}else{
return tokenBase(stream, state);
}
};
};

var indent = function(state){
if (state.indentCount == 0){
state.indentCount++;
var lastScopeOffset = state.scopes[0].offset;
var currentOffset = lastScopeOffset + config.indentUnit;
state.scopes.unshift({ offset:currentOffset });
}
};

var dedent = function(state){
if (state.scopes.length == 1) { return; }

state.scopes.shift();
};

var tokenBase = function(stream, state) {
var ch = stream.peek();

// Single line Comment
if (stream.match('//')) {
stream.skipToEnd();
return "comment";
}

// Multiline Comment
if (stream.match('/*')){
state.tokenizer = multilineComment;
return state.tokenizer(stream, state);
}

// Interpolation
if (stream.match('#{')){
state.tokenizer = buildInterpolationTokenizer(tokenBase);
return "operator";
}

if (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";
}
}

if (ch === "#"){
stream.next();

// Hex numbers
if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
return "number";
}

// ID selectors
if (stream.match(/^[\w-]+/)){
indent(state);
return "atom";
}

if (stream.peek() === "#"){
indent(state);
return "atom";
}
}

// Numbers
if (stream.match(/^-?[0-9\.]+/)){
return "number";
}

// Units
if (stream.match(/^(px|em|in)\b/)){
return "unit";
}

if (stream.match(keywordsRegexp)){
return "keyword";
}

if (stream.match(/^url/) && stream.peek() === "("){
state.tokenizer = urlTokens;
return "atom";
}

// Variables
if (ch === "$"){
stream.next();
stream.eatWhile(/[\w-]/);

if (stream.peek() === ":"){
stream.next();
return "variable-2";
}else{
return "variable-3";
}
}

if (ch === "!"){
stream.next();

if (stream.match(/^[\w]+/)){
return "keyword";
}

return "operator";
}

if (ch === "="){
stream.next();

// Match shortcut mixin definition
if (stream.match(/^[\w-]+/)){
indent(state);
return "meta";
}else {
return "operator";
}
}

if (ch === "+"){
stream.next();

// Match shortcut mixin definition
if (stream.match(/^[\w-]+/)){
return "variable-3";
}else {
return "operator";
}
}

// Indent Directives
if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)){
indent(state);
return "meta";
}

// Other Directives
if (ch === "@"){
stream.next();
stream.eatWhile(/[\w-]/);
return "meta";
}

// Strings
if (ch === '"' || ch === "'"){
stream.next();
state.tokenizer = buildStringTokenizer(ch);
return "string";
}

// Pseudo element selectors
if (stream.match(pseudoElementsRegexp)){
return "keyword";
}

// atoms
if (stream.eatWhile(/[\w-&]/)){

var current = stream.current();
// matches a property definition
if (stream.peek() === ":"){
// if this is an html tag and it has a pseudo selector, then it's an atom
if (htmlTag(current) && stream.match(pseudoElementsRegexp, false)){
return "atom";
}else{
stream.next();
return "property";
}
}
return "atom";
}

if (stream.match(opRegexp)){
return "operator";
}

// If we haven't returned by now, we move 1 character
// and return an error
stream.next();
return 'error';
};

var tokenLexer = function(stream, state) {
if (stream.sol()){
state.indentCount = 0;
}
var style = state.tokenizer(stream, state);
var current = stream.current();

if (current === "@return"){
dedent(state);
}

if (style === "atom" && htmlTag(current)){
indent(state);
}

if (style !== "error"){
var startOfToken = stream.pos - current.length;
var withCurrentIndent = startOfToken + (config.indentUnit * state.indentCount);

var newScopes = [];

for (var i = 0; i < state.scopes.length; i++){
var scope = state.scopes[i];

if (scope.offset <= withCurrentIndent){
newScopes.push(scope);
}
}

state.scopes = newScopes;
}


return style;
};

return {
startState: function() {
return {
tokenizer: tokenBase,
scopes: [{offset: 0, type: 'sass'}],
definedVars: [],
definedMixins: [],
};
},
token: function(stream, state) {
var style = tokenLexer(stream, state);

state.lastToken = { style: style, content: stream.current() };

return style;
},

indent: function(state) {
return state.scopes[0].offset;
}
};
});

CodeMirror.defineMIME("text/x-sass", "sass");
2 changes: 1 addition & 1 deletion mode/shell/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</style>

<script src=../../lib/codemirror.js></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src=shell.js></script>

<h1>CodeMirror: Shell mode</h1>
Expand Down
4 changes: 0 additions & 4 deletions mode/sieve/LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Please note that some subdirectories of the CodeMirror distribution
include their own LICENSE files, and are released under different
licences.
47 changes: 37 additions & 10 deletions mode/sieve/sieve.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,37 @@ CodeMirror.defineMode("sieve", function(config) {
state.tokenize = tokenString(ch);
return state.tokenize(stream, state);
}

if (ch === "{")
{
state._indent++;

if (ch == "(") {
state._indent.push("(");
// add virtual angel wings so that editor behaves...
// ...more sane incase of broken brackets
state._indent.push("{");
return null;
}

if (ch === "}")
{
state._indent--;
if (ch === "{") {
state._indent.push("{");
return null;
}

if (ch == ")") {
state._indent.pop();
state._indent.pop();
}

if (ch === "}") {
state._indent.pop();
return null;
}

if (ch == ",")
return null;

if (ch == ";")
return null;


if (/[{}\(\),;]/.test(ch))
return null;

Expand All @@ -62,7 +80,7 @@ CodeMirror.defineMode("sieve", function(config) {
return "operator";
}

stream.eatWhile(/[\w\$_]/);
stream.eatWhile(/\w/);
var cur = stream.current();

// "text:" *(SP / HTAB) (hash-comment / CRLF)
Expand All @@ -79,6 +97,8 @@ CodeMirror.defineMode("sieve", function(config) {

if (atoms.propertyIsEnumerable(cur))
return "atom";

return null;
}

function tokenMultiLineString(stream, state)
Expand Down Expand Up @@ -135,7 +155,7 @@ CodeMirror.defineMode("sieve", function(config) {
startState: function(base) {
return {tokenize: tokenBase,
baseIndent: base || 0,
_indent: 0};
_indent: []};
},

token: function(stream, state) {
Expand All @@ -146,7 +166,14 @@ CodeMirror.defineMode("sieve", function(config) {
},

indent: function(state, _textAfter) {
return state.baseIndent + state._indent * indentUnit;
var length = state._indent.length;
if (_textAfter && (_textAfter[0] == "}"))
length--;

if (length <0)
length = 0;

return length * indentUnit;
},

electricChars: "}"
Expand Down
2 changes: 1 addition & 1 deletion mode/smalltalk/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: Smalltalk mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="smalltalk.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style>
Expand Down
2 changes: 1 addition & 1 deletion mode/sparql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: SPARQL mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="sparql.js"></script>
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
<link rel="stylesheet" href="../../doc/docs.css">
Expand Down
68 changes: 68 additions & 0 deletions mode/sql/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>SQL Mode for CodeMirror</title>
<link rel="stylesheet" href="../../lib/codemirror.css" />
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/continuelist.js"></script>
<script src="sql.js"></script>
<style>
.CodeMirror {
border-top: 1px solid black;
border-bottom: 1px solid black;
}
</style>
<link rel="stylesheet" href="../../doc/docs.css">
<script>
var init = function() {
var mime = 'text/x-mariadb';

// get mime type
if (window.location.href.indexOf('mime=') > -1) {
mime = window.location.href.substr(window.location.href.indexOf('mime=') + 5);
}

window.editor = CodeMirror.fromTextArea(document.getElementById('code'), {
mode: mime,
indentWithTabs: true,
smartIndent: true,
lineNumbers: true,
matchBrackets : true,
autofocus: true
});
};
</script>
</head>
<body onload="init();">
<h1>SQL Mode for CodeMirror</h1>
<form>
<textarea id="code" name="code">-- SQL Mode for CodeMirror
SELECT SQL_NO_CACHE DISTINCT
@var1 AS `val1`, @'val2', @global.'sql_mode',
1.1 AS `float_val`, .14 AS `another_float`, 0.09e3 AS `int_with_esp`,
0xFA5 AS `hex`, x'fa5' AS `hex2`, 0b101 AS `bin`, b'101' AS `bin2`,
DATE '1994-01-01' AS `sql_date`, { T "1994-01-01" } AS `odbc_date`,
'myString', UNKNOWN
FROM DUAL
-- space needed after '--'
# 1 line comment
/* multiline
comment! */
LIMIT 1 OFFSET 0;
</textarea>
</form>
<p><strong>MIME types defined:</strong>
<code><a href="?mime=text/x-sql">text/x-sql</a></code>,
<code><a href="?mime=text/x-mysql">text/x-mysql</a></code>,
<code><a href="?mime=text/x-mariadb">text/x-mariadb</a></code>,
<code><a href="?mime=text/x-plsql">text/x-plsql</a></code>.
</p>
<p>
<strong>Tests:</strong>
<a href="../../test/index.html#sql_*">normal</a>,
<a href="../../test/index.html#verbose,sql_*">verbose</a>.
</p>
</body>
</html>
268 changes: 268 additions & 0 deletions mode/sql/sql.js

Large diffs are not rendered by default.

447 changes: 104 additions & 343 deletions mode/stex/test.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mode/tiddlywiki/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>CodeMirror: TiddlyWiki mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/util/matchbrackets.js"></script>
<script src="../../addon/edit/matchbrackets.js"></script>
<script src="tiddlywiki.js"></script>
<link rel="stylesheet" href="tiddlywiki.css">
<link rel="stylesheet" href="../../doc/docs.css">
Expand Down
2 changes: 1 addition & 1 deletion mode/vb/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
.CodeMirror-scroll { overflow-x: auto; overflow-y: hidden;}
.CodeMirror pre { font-family: Inconsolata; font-size: 14px}
</style>
<script type="text/javascript" src="../../lib/util/runmode.js"></script>
<script type="text/javascript" src="../../addon/runmode/runmode.js"></script>
</head>
<body onload="init()">
<h1>CodeMirror: VB.NET mode</h1>
Expand Down
121 changes: 54 additions & 67 deletions mode/xquery/test.js
Original file line number Diff line number Diff line change
@@ -1,77 +1,64 @@
// Initiate ModeTest and set defaults
var MT = ModeTest;
MT.modeName = "xquery";
MT.modeOptions = {};
// Don't take these too seriously -- the expected results appear to be
// based on the results of actual runs without any serious manual
// verification. If a change you made causes them to fail, the test is
// as likely to wrong as the code.

MT.testMode("eviltest",
'xquery version &quot;1.0-ml&quot;;\
(: this is\
: a \
"comment" :)\
let $let := &lt;x attr=&quot;value&quot;&gt;&quot;test&quot;&lt;func&gt;function() $var {function()} {$var}&lt;/func&gt;&lt;/x&gt;\
let $joe:=1\
return element element {\
attribute attribute { 1 },\
element test { &#39;a&#39; }, \
attribute foo { &quot;bar&quot; },\
fn:doc()[ foo/@bar eq $let ],\
//x } \
\
(: a more \'evil\' test :)\
(: Modified Blakeley example (: with nested comment :) ... :)\
declare private function local:declare() {()};\
declare private function local:private() {()};\
declare private function local:function() {()};\
declare private function local:local() {()};\
let $let := &lt;let&gt;let $let := &quot;let&quot;&lt;/let&gt;\
return element element {\
attribute attribute { try { xdmp:version() } catch($e) { xdmp:log($e) } },\
attribute fn:doc { &quot;bar&quot; castable as xs:string },\
element text { text { &quot;text&quot; } },\
fn:doc()[ child::eq/(@bar | attribute::attribute) eq $let ],\
//fn:doc\
}', ["keyword","xquery",null," ","keyword","version",null," ","variable","&quot;1","keyword",".","atom","0","keyword","-","variable","ml&quot;","def variable",";",null," ","comment","(: this is : a \"comment\" :)",null," ","keyword","let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","&lt;x",null," ","variable","attr","keyword","=","variable","&quot;value&quot;&gt;&quot;test&quot;&lt;func&gt","def variable",";function","","()",null," ","variable","$var",null," ","","{","keyword","function","","()}",null," ","","{","variable","$var","","}","variable","&lt;","keyword","/","variable","func&gt;&lt;","keyword","/","variable","x&gt;",null," ","keyword","let",null," ","variable","$joe","keyword",":=","atom","1",null," ","keyword","return",null," ","keyword","element",null," ","variable","element",null," ","","{",null," ","keyword","attribute",null," ","variable","attribute",null," ","","{",null," ","atom","1",null," ","","},",null," ","keyword","element",null," ","variable","test",null," ","","{",null," ","variable","&#39;a&#39;",null," ","","},",null," ","keyword","attribute",null," ","variable","foo",null," ","","{",null," ","variable","&quot;bar&quot;",null," ","","},",null," ","def variable","fn:doc","","()[",null," ","variable","foo","keyword","/","variable","@bar",null," ","keyword","eq",null," ","variable","$let",null," ","","],",null," ","keyword","//","variable","x",null," ","","}",null," ","comment","(: a more 'evil' test :)",null," ","comment","(: Modified Blakeley example (: with nested comment :) ... :)",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:declare","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:private","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:function","","()",null," ","","{()}","variable",";",null," ","keyword","declare",null," ","keyword","private",null," ","keyword","function",null," ","def variable","local:local","","()",null," ","","{()}","variable",";",null," ","keyword","let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","&lt;let&gt;let",null," ","variable","$let",null," ","keyword",":=",null," ","variable","&quot;let&quot;&lt;","keyword","/let","variable","&gt;",null," ","keyword","return",null," ","keyword","element",null," ","variable","element",null," ","","{",null," ","keyword","attribute",null," ","variable","attribute",null," ","","{",null," ","keyword","try",null," ","","{",null," ","def variable","xdmp:version","","()",null," ","","}",null," ","keyword","catch","","(","variable","$e","",")",null," ","","{",null," ","def variable","xdmp:log","","(","variable","$e","",")",null," ","","}",null," ","","},",null," ","keyword","attribute",null," ","variable","fn:doc",null," ","","{",null," ","variable","&quot;bar&quot;",null," ","variable","castable",null," ","keyword","as",null," ","atom","xs:string",null," ","","},",null," ","keyword","element",null," ","variable","text",null," ","","{",null," ","keyword","text",null," ","","{",null," ","variable","&quot;text&quot;",null," ","","}",null," ","","},",null," ","def variable","fn:doc","","()[",null," ","qualifier","child::","variable","eq","keyword","/","","(","variable","@bar",null," ","keyword","|",null," ","qualifier","attribute::","variable","attribute","",")",null," ","keyword","eq",null," ","variable","$let",null," ","","],",null," ","keyword","//","variable","fn:doc",null," ","","}"]);
(function() {
var mode = CodeMirror.getMode({tabSize: 4}, "xquery");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }

MT.testMode("testEmptySequenceKeyword",
'"foo" instance of empty-sequence()',
["string","\"foo\"",null," ","keyword","instance",null," ","keyword","of",null," ","keyword","empty-sequence","","()"]);
MT("eviltest",
"[keyword xquery] [keyword version] [variable &quot;1][keyword .][atom 0][keyword -][variable ml&quot;][def&variable ;] [comment (: this is : a \"comment\" :)]",
" [keyword let] [variable $let] [keyword :=] [variable &lt;x] [variable attr][keyword =][variable &quot;value&quot;&gt;&quot;test&quot;&lt;func&gt][def&variable ;function]() [variable $var] {[keyword function]()} {[variable $var]}[variable &lt;][keyword /][variable func&gt;&lt;][keyword /][variable x&gt;]",
" [keyword let] [variable $joe][keyword :=][atom 1]",
" [keyword return] [keyword element] [variable element] {",
" [keyword attribute] [variable attribute] { [atom 1] },",
" [keyword element] [variable test] { [variable &#39;a&#39;] }, [keyword attribute] [variable foo] { [variable &quot;bar&quot;] },",
" [def&variable fn:doc]()[[ [variable foo][keyword /][variable @bar] [keyword eq] [variable $let] ]],",
" [keyword //][variable x] } [comment (: a more 'evil' test :)]",
" [comment (: Modified Blakeley example (: with nested comment :) ... :)]",
" [keyword declare] [keyword private] [keyword function] [def&variable local:declare]() {()}[variable ;]",
" [keyword declare] [keyword private] [keyword function] [def&variable local:private]() {()}[variable ;]",
" [keyword declare] [keyword private] [keyword function] [def&variable local:function]() {()}[variable ;]",
" [keyword declare] [keyword private] [keyword function] [def&variable local:local]() {()}[variable ;]",
" [keyword let] [variable $let] [keyword :=] [variable &lt;let&gt;let] [variable $let] [keyword :=] [variable &quot;let&quot;&lt;][keyword /let][variable &gt;]",
" [keyword return] [keyword element] [variable element] {",
" [keyword attribute] [variable attribute] { [keyword try] { [def&variable xdmp:version]() } [keyword catch]([variable $e]) { [def&variable xdmp:log]([variable $e]) } },",
" [keyword attribute] [variable fn:doc] { [variable &quot;bar&quot;] [variable castable] [keyword as] [atom xs:string] },",
" [keyword element] [variable text] { [keyword text] { [variable &quot;text&quot;] } },",
" [def&variable fn:doc]()[[ [qualifier child::][variable eq][keyword /]([variable @bar] [keyword |] [qualifier attribute::][variable attribute]) [keyword eq] [variable $let] ]],",
" [keyword //][variable fn:doc]",
" }");

MT("testEmptySequenceKeyword",
"[string \"foo\"] [keyword instance] [keyword of] [keyword empty-sequence]()");

MT.testMode("testMultiAttr",
'<p a1="foo" a2="bar">hello world</p>',
["tag","<p ","attribute","a1","","=","string","\"foo\"",null," ","attribute","a2","","=","string","\"bar\"","tag",">","variable","hello",null," ","variable","world","tag","</p>"]);
MT("testMultiAttr",
"[tag <p ][attribute a1]=[string \"foo\"] [attribute a2]=[string \"bar\"][tag >][variable hello] [variable world][tag </p>]");

MT.testMode("test namespaced variable",
'declare namespace e = "http://example.com/ANamespace";\
declare variable $e:exampleComThisVarIsNotRecognized as element(*) external;',
["keyword","declare",null," ","keyword","namespace",null," ","variable","e",null," ","keyword","=",null," ","string","\"http://example.com/ANamespace\"","variable",";declare",null," ","keyword","variable",null," ","variable","$e:exampleComThisVarIsNotRecognized",null," ","keyword","as",null," ","keyword","element","","(","keyword","*","",")",null," ","variable","external;"]);
MT("test namespaced variable",
"[keyword declare] [keyword namespace] [variable e] [keyword =] [string \"http://example.com/ANamespace\"][variable ;declare] [keyword variable] [variable $e:exampleComThisVarIsNotRecognized] [keyword as] [keyword element]([keyword *]) [variable external;]");

MT.testMode("test EQName variable",
'declare variable $"http://www.example.com/ns/my":var := 12;\
<out>{$"http://www.example.com/ns/my":var}</out>',
["keyword","declare",null," ","keyword","variable",null," ","variable","$\"http://www.example.com/ns/my\":var",null," ","keyword",":=",null," ","atom","12","variable",";","tag","<out>","","{","variable","$\"http://www.example.com/ns/my\":var","","}","tag","</out>"]);
MT("test EQName variable",
"[keyword declare] [keyword variable] [variable $\"http://www.example.com/ns/my\":var] [keyword :=] [atom 12][variable ;]",
"[tag <out>]{[variable $\"http://www.example.com/ns/my\":var]}[tag </out>]");

MT.testMode("test EQName function",
'declare function "http://www.example.com/ns/my":fn ($a as xs:integer) as xs:integer {\
$a + 2\
};\
<out>{"http://www.example.com/ns/my":fn(12)}</out>',
["keyword","declare",null," ","keyword","function",null," ","def variable","\"http://www.example.com/ns/my\":fn",null," ","","(","variable","$a",null," ","keyword","as",null," ","atom","xs:integer","",")",null," ","keyword","as",null," ","atom","xs:integer",null," ","","{",null," ","variable","$a",null," ","keyword","+",null," ","atom","2","","}","variable",";","tag","<out>","","{","def variable","\"http://www.example.com/ns/my\":fn","","(","atom","12","",")}","tag","</out>"]);
MT("test EQName function",
"[keyword declare] [keyword function] [def&variable \"http://www.example.com/ns/my\":fn] ([variable $a] [keyword as] [atom xs:integer]) [keyword as] [atom xs:integer] {",
" [variable $a] [keyword +] [atom 2]",
"}[variable ;]",
"[tag <out>]{[def&variable \"http://www.example.com/ns/my\":fn]([atom 12])}[tag </out>]");

MT.testMode("test EQName function with single quotes",
'declare function \'http://www.example.com/ns/my\':fn ($a as xs:integer) as xs:integer {\
$a + 2\
};\
<out>{\'http://www.example.com/ns/my\':fn(12)}</out>',
["keyword","declare",null," ","keyword","function",null," ","def variable","'http://www.example.com/ns/my':fn",null," ","","(","variable","$a",null," ","keyword","as",null," ","atom","xs:integer","",")",null," ","keyword","as",null," ","atom","xs:integer",null," ","","{",null," ","variable","$a",null," ","keyword","+",null," ","atom","2","","}","variable",";","tag","<out>","","{","def variable","'http://www.example.com/ns/my':fn","","(","atom","12","",")}","tag","</out>"]);
MT("test EQName function with single quotes",
"[keyword declare] [keyword function] [def&variable 'http://www.example.com/ns/my':fn] ([variable $a] [keyword as] [atom xs:integer]) [keyword as] [atom xs:integer] {",
" [variable $a] [keyword +] [atom 2]",
"}[variable ;]",
"[tag <out>]{[def&variable 'http://www.example.com/ns/my':fn]([atom 12])}[tag </out>]");

MT.testMode("testProcessingInstructions",
'data(<?target content?>) instance of xs:string',
["def variable","data","","(","comment meta","<?target content?>","",")",null," ","keyword","instance",null," ","keyword","of",null," ","atom","xs:string"]);
MT("testProcessingInstructions",
"[def&variable data]([comment&meta <?target content?>]) [keyword instance] [keyword of] [atom xs:string]");

MT.testMode("testQuoteEscapeDouble",
'let $rootfolder := "c:\\builds\\winnt\\HEAD\\qa\\scripts\\"\
let $keysfolder := concat($rootfolder, "keys\\")\
return\
$keysfolder',
["keyword","let",null," ","variable","$rootfolder",null," ","keyword",":=",null," ","string","\"c:\\builds\\winnt\\HEAD\\qa\\scripts\\\"","keyword","let",null," ","variable","$keysfolder",null," ","keyword",":=",null," ","def variable","concat","","(","variable","$rootfolder","",",",null," ","string","\"keys\\\"","",")","variable","return$keysfolder"]);
MT("testQuoteEscapeDouble",
"[keyword let] [variable $rootfolder] [keyword :=] [string \"c:\\builds\\winnt\\HEAD\\qa\\scripts\\\"]",
"[keyword let] [variable $keysfolder] [keyword :=] [def&variable concat]([variable $rootfolder], [string \"keys\\\"])");
})();
16 changes: 8 additions & 8 deletions mode/xquery/xquery.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ CodeMirror.defineMode("xquery", function() {
, C = kw("keyword c")
, operator = kw("operator")
, atom = {type: "atom", style: "atom"}
, punctuation = {type: "punctuation", style: ""}
, punctuation = {type: "punctuation", style: null}
, qualifier = {type: "axis_specifier", style: "qualifier"};

// kwObj is what is return from this function at the end
Expand Down Expand Up @@ -121,12 +121,12 @@ CodeMirror.defineMode("xquery", function() {
// start code block
else if(ch == "{") {
pushStateStack(state,{ type: "codeblock"});
return ret("", "");
return ret("", null);
}
// end code block
else if(ch == "}") {
popStateStack(state);
return ret("", "");
return ret("", null);
}
// if we're in an XML block
else if(isInXmlBlock(state)) {
Expand Down Expand Up @@ -163,22 +163,22 @@ CodeMirror.defineMode("xquery", function() {
// open paren
else if(ch === "(") {
pushStateStack(state, { type: "paren"});
return ret("", "");
return ret("", null);
}
// close paren
else if(ch === ")") {
popStateStack(state);
return ret("", "");
return ret("", null);
}
// open paren
else if(ch === "[") {
pushStateStack(state, { type: "bracket"});
return ret("", "");
return ret("", null);
}
// close paren
else if(ch === "]") {
popStateStack(state);
return ret("", "");
return ret("", null);
}
else {
var known = keywords.propertyIsEnumerable(ch) && keywords[ch];
Expand Down Expand Up @@ -342,7 +342,7 @@ CodeMirror.defineMode("xquery", function() {
return ret("tag", "tag");
}
if(ch == "=")
return ret("", "");
return ret("", null);
// quoted string
if (ch == '"' || ch == "'")
return chain(stream, state, tokenString(ch, tokenAttribute));
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"3.0.2",
"version":"3.01.00",
"main": "codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
Expand Down
26 changes: 15 additions & 11 deletions test/driver.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function testCM(name, run, opts, expectedFail) {
successful = true;
} finally {
if ((debug && !successful) || verbose) {
place.style.visibility = "";
place.style.visibility = "visible";
} else {
place.removeChild(cm.getWrapperElement());
}
Expand All @@ -48,10 +48,6 @@ function runTests(callback) {
}
if (debug.length < 1) {
debug = null;
} else {
if (totalTests > debug.length) {
totalTests = debug.length;
}
}
}
var totalTime = 0;
Expand All @@ -67,19 +63,15 @@ function runTests(callback) {
// Remove from array for reporting incorrect tests later
debug.splice(debugIndex, 1);
} else {
var wildcardName = test.name.split("_").shift() + "_*";
var wildcardName = test.name.split("_")[0] + "_*";
debugIndex = indexOf(debug, wildcardName);
if (debugIndex !== -1) {
// Remove from array for reporting incorrect tests later
debug.splice(debugIndex, 1);
debugUsed.push(wildcardName);
} else {
debugIndex = indexOf(debugUsed, wildcardName);
if (debugIndex !== -1) {
totalTests++;
} else {
return step(i + 1);
}
if (debugIndex == -1) return step(i + 1);
}
}
}
Expand Down Expand Up @@ -132,3 +124,15 @@ function eqPos(a, b, msg) {
function is(a, msg) {
if (!a) throw new Failure(label("assertion failed", msg));
}

function countTests() {
if (!debug) return tests.length;
var sum = 0;
for (var i = 0; i < tests.length; ++i) {
var name = tests[i].name;
if (indexOf(debug, name) != -1 ||
indexOf(debug, name.split("_")[0] + "_*") != -1)
++sum;
}
return sum;
}
19 changes: 13 additions & 6 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<link rel="stylesheet" href="../doc/docs.css">
<link rel="stylesheet" href="mode_test.css">
<script src="../lib/codemirror.js"></script>
<script src="../lib/util/overlay.js"></script>
<script src="../lib/util/searchcursor.js"></script>
<script src="../addon/mode/overlay.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../keymap/vim.js"></script>
Expand All @@ -25,6 +25,15 @@
font-weight: bold;
white-space: pre;
}
#testground {
visibility: hidden;
}
#testground.offscreen {
visibility: visible;
position: absolute;
left: -10000px;
top: -10000px;
}
.CodeMirror { border: 1px solid black; }
</style>
</head>
Expand All @@ -39,7 +48,7 @@ <h1>CodeMirror: Test Suite</h1>
<p id=status>Please enable JavaScript...</p>
<div id=output></div>

<div style="visibility: hidden" id=testground></div>
<div id=testground></div>

<script src="driver.js"></script>
<script src="test.js"></script>
Expand Down Expand Up @@ -98,7 +107,7 @@ <h1>CodeMirror: Test Suite</h1>
bad = "";
verbose = false;
debugUsed = Array();
totalTests = tests.length;
totalTests = countTests();
progressTotal.nodeValue = " of " + totalTests;
progressRan.nodeValue = count;
output.innerHTML = '';
Expand Down Expand Up @@ -132,8 +141,6 @@ <h1>CodeMirror: Test Suite</h1>
var message = "???";
if (type != "done") ++count;
progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px";
progressTotal.nodeValue = " of " + totalTests +
(debugUsed.length && type != "done" ? "+" : "");
progressRan.nodeValue = count;
if (type == "ok") {
message = "Test '" + name + "' succeeded";
Expand Down
340 changes: 170 additions & 170 deletions test/mode_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,191 +2,191 @@
* Helper to test CodeMirror highlighting modes. It pretty prints output of the
* highlighter and can check against expected styles.
*
* See test.html in the stex mode for examples.
*/
ModeTest = {};

ModeTest.modeOptions = {};
ModeTest.modeName = CodeMirror.defaults.mode;

/* keep track of results for printSummary */
ModeTest.testCount = 0;
ModeTest.passes = 0;

/**
* Run a test; prettyprints the results using document.write().
*
* @param name Name of test
* @param text String to highlight.
* @param expected Expected styles and tokens: Array(style, token, [style, token,...])
* @param modeName
* @param modeOptions
* @param expectedFail
* Mode tests are registered by calling test.mode(testName, mode,
* tokens), where mode is a mode object as returned by
* CodeMirror.getMode, and tokens is an array of lines that make up
* the test.
*
* These lines are strings, in which styled stretches of code are
* enclosed in brackets `[]`, and prefixed by their style. For
* example, `[keyword if]`. Brackets in the code itself must be
* duplicated to prevent them from being interpreted as token
* boundaries. For example `a[[i]]` for `a[i]`. If a token has
* multiple styles, the styles must be separated by ampersands, for
* example `[tag&error </hmtl>]`.
*
* See the test.js files in the css, markdown, gfm, and stex mode
* directories for examples.
*/
ModeTest.testMode = function(name, text, expected, modeName, modeOptions, expectedFail) {
ModeTest.testCount += 1;

if (!modeName) modeName = ModeTest.modeName;

if (!modeOptions) modeOptions = ModeTest.modeOptions;

var mode = CodeMirror.getMode(modeOptions, modeName);

if (expected.length < 0) {
throw "must have text for test (" + name + ")";
}
if (expected.length % 2 != 0) {
throw "must have text for test (" + name + ") plus expected (style, token) pairs";
(function() {
function findSingle(str, pos, ch) {
for (;;) {
var found = str.indexOf(ch, pos);
if (found == -1) return null;
if (str.charAt(found + 1) != ch) return found;
pos = found + 2;
}
}
return test(
modeName + "_" + name,
function(){
return ModeTest.compare(text, expected, mode);
},
expectedFail
);

}

ModeTest.compare = function (text, expected, mode) {

var expectedOutput = [];
for (var i = 0; i < expected.length; i += 2) {
var sty = expected[i];
if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
expectedOutput.push(sty, expected[i + 1]);
var styleName = /[\w&-_]+/g;
function parseTokens(strs) {
var tokens = [], plain = "";
for (var i = 0; i < strs.length; ++i) {
if (i) plain += "\n";
var str = strs[i], pos = 0;
while (pos < str.length) {
var style = null, text;
if (str.charAt(pos) == "[" && str.charAt(pos+1) != "[") {
styleName.lastIndex = pos + 1;
var m = styleName.exec(str);
style = m[0].replace(/&/g, " ");
var textStart = pos + style.length + 2;
var end = findSingle(str, textStart, "]");
if (end == null) throw new Error("Unterminated token at " + pos + " in '" + str + "'" + style);
text = str.slice(textStart, end);
pos = end + 1;
} else {
var end = findSingle(str, pos, "[");
if (end == null) end = str.length;
text = str.slice(pos, end);
pos = end;
}
text = text.replace(/\[\[|\]\]/g, function(s) {return s.charAt(0);});
tokens.push(style, text);
plain += text;
}
}
return {tokens: tokens, plain: plain};
}

var observedOutput = ModeTest.highlight(text, mode);

var pass, passStyle = "";
pass = ModeTest.highlightOutputsEqual(expectedOutput, observedOutput);
passStyle = pass ? 'mt-pass' : 'mt-fail';
ModeTest.passes += pass ? 1 : 0;
test.mode = function(name, mode, tokens) {
var data = parseTokens(tokens);
return test(mode.name + "_" + name, function() {
return compare(data.plain, data.tokens, mode);
});
};

var s = '';
if (pass) {
s += '<div class="mt-test ' + passStyle + '">';
s += '<pre>' + ModeTest.htmlEscape(text) + '</pre>';
s += '<div class="cm-s-default">';
s += ModeTest.prettyPrintOutputTable(observedOutput);
s += '</div>';
s += '</div>';
return s;
} else {
s += '<div class="mt-test ' + passStyle + '">';
s += '<pre>' + ModeTest.htmlEscape(text) + '</pre>';
s += '<div class="cm-s-default">';
s += 'expected:';
s += ModeTest.prettyPrintOutputTable(expectedOutput);
s += 'observed:';
s += ModeTest.prettyPrintOutputTable(observedOutput);
s += '</div>';
s += '</div>';
throw s;
}
}
function compare(text, expected, mode) {

/**
* Emulation of CodeMirror's internal highlight routine for testing. Multi-line
* input is supported.
*
* @param string to highlight
*
* @param mode the mode that will do the actual highlighting
*
* @return array of [style, token] pairs
*/
ModeTest.highlight = function(string, mode) {
var state = mode.startState()
var expectedOutput = [];
for (var i = 0; i < expected.length; i += 2) {
var sty = expected[i];
if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
expectedOutput.push(sty, expected[i + 1]);
}

var lines = string.replace(/\r\n/g,'\n').split('\n');
var st = [], pos = 0;
for (var i = 0; i < lines.length; ++i) {
var line = lines[i], newLine = true;
var stream = new CodeMirror.StringStream(line);
if (line == "" && mode.blankLine) mode.blankLine(state);
/* Start copied code from CodeMirror.highlight */
while (!stream.eol()) {
var style = mode.token(stream, state), substr = stream.current();
if (style && style.indexOf(" ") > -1) style = style.split(' ').sort().join(' ');
var observedOutput = highlight(text, mode);

var pass, passStyle = "";
pass = highlightOutputsEqual(expectedOutput, observedOutput);
passStyle = pass ? 'mt-pass' : 'mt-fail';

var s = '';
if (pass) {
s += '<div class="mt-test ' + passStyle + '">';
s += '<pre>' + text + '</pre>';
s += '<div class="cm-s-default">';
s += prettyPrintOutputTable(observedOutput);
s += '</div>';
s += '</div>';
return s;
} else {
s += '<div class="mt-test ' + passStyle + '">';
s += '<pre>' + text + '</pre>';
s += '<div class="cm-s-default">';
s += 'expected:';
s += prettyPrintOutputTable(expectedOutput);
s += 'observed:';
s += prettyPrintOutputTable(observedOutput);
s += '</div>';
s += '</div>';
throw s;
}
}

stream.start = stream.pos;
if (pos && st[pos-2] == style && !newLine) {
st[pos-1] += substr;
} else if (substr) {
st[pos++] = style; st[pos++] = substr;
/**
* Emulation of CodeMirror's internal highlight routine for testing. Multi-line
* input is supported.
*
* @param string to highlight
*
* @param mode the mode that will do the actual highlighting
*
* @return array of [style, token] pairs
*/
function highlight(string, mode) {
var state = mode.startState()

var lines = string.replace(/\r\n/g,'\n').split('\n');
var st = [], pos = 0;
for (var i = 0; i < lines.length; ++i) {
var line = lines[i], newLine = true;
var stream = new CodeMirror.StringStream(line);
if (line == "" && mode.blankLine) mode.blankLine(state);
/* Start copied code from CodeMirror.highlight */
while (!stream.eol()) {
var style = mode.token(stream, state), substr = stream.current();
if (style && style.indexOf(" ") > -1) style = style.split(' ').sort().join(' ');

stream.start = stream.pos;
if (pos && st[pos-2] == style && !newLine) {
st[pos-1] += substr;
} else if (substr) {
st[pos++] = style; st[pos++] = substr;
}
// Give up when line is ridiculously long
if (stream.pos > 5000) {
st[pos++] = null; st[pos++] = this.text.slice(stream.pos);
break;
}
newLine = false;
}
// Give up when line is ridiculously long
if (stream.pos > 5000) {
st[pos++] = null; st[pos++] = this.text.slice(stream.pos);
break;
}
newLine = false;
}
}

return st;
}
return st;
}

/**
* Compare two arrays of output from ModeTest.highlight.
*
* @param o1 array of [style, token] pairs
*
* @param o2 array of [style, token] pairs
*
* @return boolean; true iff outputs equal
*/
ModeTest.highlightOutputsEqual = function(o1, o2) {
if (o1.length != o2.length) return false;
for (var i = 0; i < o1.length; ++i)
if (o1[i] != o2[i]) return false;
return true;
}
/**
* Compare two arrays of output from highlight.
*
* @param o1 array of [style, token] pairs
*
* @param o2 array of [style, token] pairs
*
* @return boolean; true iff outputs equal
*/
function highlightOutputsEqual(o1, o2) {
if (o1.length != o2.length) return false;
for (var i = 0; i < o1.length; ++i)
if (o1[i] != o2[i]) return false;
return true;
}

/**
* Print tokens and corresponding styles in a table. Spaces in the token are
* replaced with 'interpunct' dots (&middot;).
*
* @param output array of [style, token] pairs
*
* @return html string
*/
ModeTest.prettyPrintOutputTable = function(output) {
var s = '<table class="mt-output">';
s += '<tr>';
for (var i = 0; i < output.length; i += 2) {
var style = output[i], val = output[i+1];
s +=
/**
* Print tokens and corresponding styles in a table. Spaces in the token are
* replaced with 'interpunct' dots (&middot;).
*
* @param output array of [style, token] pairs
*
* @return html string
*/
function prettyPrintOutputTable(output) {
var s = '<table class="mt-output">';
s += '<tr>';
for (var i = 0; i < output.length; i += 2) {
var style = output[i], val = output[i+1];
s +=
'<td class="mt-token">' +
'<span class="cm-' + String(style).replace(/ +/g, " cm-") + '">' +
ModeTest.htmlEscape(val).replace(/ /g,'&middot;') +
val.replace(/ /g,'\xb7') +
'</span>' +
'</td>';
}
s += '</tr><tr>';
for (var i = 0; i < output.length; i += 2) {
s += '<td class="mt-style"><span>' + output[i] + '</span></td>';
'</td>';
}
s += '</tr><tr>';
for (var i = 0; i < output.length; i += 2) {
s += '<td class="mt-style"><span>' + output[i] + '</span></td>';
}
s += '</table>';
return s;
}
s += '</table>';
return s;
}

/**
* Print how many tests have run so far and how many of those passed.
*/
ModeTest.printSummary = function() {
ModeTest.runTests(ModeTest.displayTest);
document.write(ModeTest.passes + ' passes for ' + ModeTest.testCount + ' tests');
}

/**
* Basic HTML escaping.
*/
ModeTest.htmlEscape = function(str) {
str = str.toString();
return str.replace(/[<&]/g,
function(str) {return str == "&" ? "&amp;" : "&lt;";});
}

})();
1 change: 1 addition & 0 deletions test/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var lint = require("./lint/lint");

lint.checkDir("mode");
lint.checkDir("lib");
lint.checkDir("addon");

var ok = lint.success();

Expand Down
42 changes: 39 additions & 3 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ function byClassName(elt, cls) {
var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
var mac = /Mac/.test(navigator.platform);
var phantom = /PhantomJS/.test(navigator.userAgent);
var opera_lt10 = /Opera\/[1-9]\./.test(navigator.userAgent);
var opera = /Opera\/\./.test(navigator.userAgent);
var opera_version = opera && navigator.userAgent.match(/Version\/(\d+\.\d+)/);
if (opera_version) opera_version = Number(opera_version);
var opera_lt10 = opera && (!opera_version || opera_version < 10);

test("core_fromTextArea", function() {
var te = document.getElementById("code");
Expand Down Expand Up @@ -484,6 +487,7 @@ testCM("doubleScrollbar", function(cm) {
dummy.style.cssText = "height: 50px; overflow: scroll; width: 50px";
var scrollbarWidth = dummy.offsetWidth + 1 - dummy.clientWidth;
document.body.removeChild(dummy);
if (scrollbarWidth < 2) return;
cm.setSize(null, 100);
addDoc(cm, 1, 300);
var wrap = cm.getWrapperElement();
Expand Down Expand Up @@ -539,6 +543,21 @@ testCM("collapsedLines", function(cm) {
eq(cleared, 1);
});

testCM("collapsedRangeCoordsChar", function(cm) {
var pos_1_3 = cm.charCoords({line: 1, ch: 3});
pos_1_3.left += 2; pos_1_3.top += 2;
var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true};
var m1 = cm.markText({line: 0, ch: 0}, {line: 2, ch: 0}, opts);
eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3});
m1.clear();
var m1 = cm.markText({line: 0, ch: 0}, {line: 1, ch: 1}, opts);
var m2 = cm.markText({line: 1, ch: 1}, {line: 2, ch: 0}, opts);
eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3});
m1.clear(); m2.clear();
var m1 = cm.markText({line: 0, ch: 0}, {line: 1, ch: 6}, opts);
eqPos(cm.coordsChar(pos_1_3), {line: 3, ch: 3});
}, {value: "123456\nabcdef\nghijkl\nmnopqr\n"});

testCM("hiddenLinesAutoUnfold", function(cm) {
var range = foldLines(cm, 1, 3, true), cleared = 0;
CodeMirror.on(range, "clear", function() {cleared++;});
Expand Down Expand Up @@ -882,7 +901,8 @@ testCM("verticalMovementCommandsWrapping", function(cm) {
lineWrapping: true});

testCM("rtlMovement", function(cm) {
forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", "خحcd", "1خحcd", "abcdeح1ج"], function(line) {
forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج",
"خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!"], function(line) {
var inv = line.charAt(0) == "خ";
cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart");
var cursor = byClassName(cm.getWrapperElement(), "CodeMirror-cursor")[0];
Expand All @@ -901,7 +921,7 @@ testCM("rtlMovement", function(cm) {
prevX = cursor.offsetLeft;
}
});
});
}, {rtlMoveVisually: true});

// Verify that updating a line clears its bidi ordering
testCM("bidiUpdate", function(cm) {
Expand Down Expand Up @@ -967,6 +987,22 @@ testCM("lineWidgets", function(cm) {
eqPos(cm.getCursor(), {line: 1, ch: 1});
});

testCM("lineWidgetFocus", function(cm) {
var place = document.getElementById("testground");
place.className = "offscreen";
try {
addDoc(cm, 500, 10);
var node = document.createElement("input");
var widget = cm.addLineWidget(1, node);
node.focus();
eq(document.activeElement, node);
cm.replaceRange("new stuff", {line: 1, ch: 0});
eq(document.activeElement, node);
} finally {
place.className = "";
}
});

testCM("getLineNumber", function(cm) {
addDoc(cm, 2, 20);
var h1 = cm.getLineHandle(1);
Expand Down
280 changes: 232 additions & 48 deletions test/vim_test.js

Large diffs are not rendered by default.