codemirror
Advanced tools
Comparing version 3.20.0 to 3.21.0
@@ -99,4 +99,5 @@ (function() { | ||
var found = line.indexOf(lineString); | ||
if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; | ||
if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; | ||
if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment; | ||
if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; | ||
lines.push(line); | ||
@@ -128,3 +129,6 @@ } | ||
} | ||
if (open == -1 || close == -1) return false; | ||
if (open == -1 || close == -1 || | ||
!/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) || | ||
!/comment/.test(self.getTokenTypeAt(Pos(end, close + 1)))) | ||
return false; | ||
@@ -131,0 +135,0 @@ self.operation(function() { |
@@ -8,3 +8,3 @@ (function() { | ||
var pos = cm.getCursor(), token = cm.getTokenAt(pos); | ||
if (token.type != "comment") return CodeMirror.Pass; | ||
if (token.type != "comment" || cm.getOption("disableInput")) return CodeMirror.Pass; | ||
var mode = CodeMirror.innerMode(cm.getMode(), token.state).mode; | ||
@@ -11,0 +11,0 @@ |
@@ -38,2 +38,3 @@ // Open simple dialogs on top of an editor. Relies on dialog.css. | ||
if (inp) { | ||
if (options && options.value) inp.value = options.value; | ||
CodeMirror.on(inp, "keydown", function(e) { | ||
@@ -40,0 +41,0 @@ if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } |
@@ -31,3 +31,3 @@ (function() { | ||
Backspace: function(cm) { | ||
if (cm.somethingSelected()) return CodeMirror.Pass; | ||
if (cm.somethingSelected() || cm.getOption("disableInput")) return CodeMirror.Pass; | ||
var cur = cm.getCursor(), around = charsAround(cm, cur); | ||
@@ -53,3 +53,4 @@ if (around && pairs.indexOf(around) % 2 == 0) | ||
map["'" + left + "'"] = function(cm) { | ||
if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment") | ||
if (left == "'" && cm.getTokenAt(cm.getCursor()).type == "comment" || | ||
cm.getOption("disableInput")) | ||
return CodeMirror.Pass; | ||
@@ -75,3 +76,4 @@ if (cm.somethingSelected()) return surround(cm); | ||
var cur = cm.getCursor(), around = charsAround(cm, cur); | ||
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; | ||
if (!around || pairs.indexOf(around) % 2 != 0 || cm.getOption("disableInput")) | ||
return CodeMirror.Pass; | ||
cm.operation(function() { | ||
@@ -78,0 +80,0 @@ var newPos = CodeMirror.Pos(cur.line + 1, 0); |
@@ -46,3 +46,3 @@ /** | ||
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; | ||
if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; | ||
if (inner.mode.name != "xml" || !state.tagName || cm.getOption("disableInput")) return CodeMirror.Pass; | ||
@@ -57,6 +57,9 @@ var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; | ||
// Don't process the '>' at the end of an end-tag or self-closing tag | ||
if (tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || | ||
if (!tagName || | ||
tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || | ||
tok.type == "tag" && state.type == "closeTag" || | ||
tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName /> | ||
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) | ||
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || | ||
CodeMirror.scanForClosingTag && CodeMirror.scanForClosingTag(cm, pos, tagName, | ||
Math.min(cm.lastLine() + 1, pos.line + 50))) | ||
return CodeMirror.Pass; | ||
@@ -69,4 +72,4 @@ | ||
if (doIndent) { | ||
cm.indentLine(pos.line + 1); | ||
cm.indentLine(pos.line + 2); | ||
cm.indentLine(pos.line + 1, null, true); | ||
cm.indentLine(pos.line + 2, null); | ||
} | ||
@@ -79,3 +82,4 @@ } | ||
if (tok.type == "string" || tok.string.charAt(0) != "<" || | ||
tok.start != pos.ch - 1 || inner.mode.name != "xml") | ||
tok.start != pos.ch - 1 || inner.mode.name != "xml" || | ||
cm.getOption("disableInput")) | ||
return CodeMirror.Pass; | ||
@@ -82,0 +86,0 @@ |
@@ -8,2 +8,4 @@ (function() { | ||
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { | ||
if (cm.getOption("disableInput")) return CodeMirror.Pass; | ||
var pos = cm.getCursor(), | ||
@@ -10,0 +12,0 @@ inList = cm.getStateAfter(pos.line).list !== false, |
@@ -57,3 +57,3 @@ (function() { | ||
// Kludge to work around the IE bug from issue #1193, where text | ||
// input stops going to the textare whever this fires. | ||
// input stops going to the textarea whenever this fires. | ||
if (ie_lt8 && cm.state.focused) cm.display.input.focus(); | ||
@@ -60,0 +60,0 @@ var clear = function() { |
@@ -1,2 +0,4 @@ | ||
CodeMirror.registerHelper("fold", "comment", function(cm, start) { | ||
CodeMirror.registerGlobalHelper("fold", "comment", function(mode) { | ||
return mode.blockCommentStart && mode.blockCommentEnd; | ||
}, function(cm, start) { | ||
var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd; | ||
@@ -3,0 +5,0 @@ if (!startToken || !endToken) return; |
@@ -6,4 +6,3 @@ (function() { | ||
var finder = options && (options.call ? options : options.rangeFinder); | ||
if (!finder) finder = cm.getHelper(pos, "fold"); | ||
if (!finder) return; | ||
if (!finder) finder = CodeMirror.fold.auto; | ||
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0); | ||
@@ -67,2 +66,6 @@ var minSize = options && options.minFoldSize || 0; | ||
CodeMirror.commands.fold = function(cm) { | ||
cm.foldCode(cm.getCursor()); | ||
}; | ||
CodeMirror.registerHelper("fold", "combine", function() { | ||
@@ -77,2 +80,10 @@ var funcs = Array.prototype.slice.call(arguments, 0); | ||
}); | ||
CodeMirror.registerHelper("fold", "auto", function(cm, start) { | ||
var helpers = cm.getHelpers(start, "fold"); | ||
for (var i = 0; i < helpers.length; i++) { | ||
var cur = helpers[i](cm, start); | ||
if (cur) return cur; | ||
} | ||
}); | ||
})(); |
@@ -65,3 +65,3 @@ (function() { | ||
} else { | ||
var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold"); | ||
var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto; | ||
var range = func && func(cm, pos); | ||
@@ -68,0 +68,0 @@ if (range && range.from.line + 1 < range.to.line) |
CodeMirror.registerHelper("fold", "indent", function(cm, start) { | ||
var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); | ||
if (!/\S/.test(firstLine)) return; | ||
var getIndent = function(lineNum) { | ||
return CodeMirror.countColumn(lineNum, null, tabSize); | ||
var getIndent = function(line) { | ||
return CodeMirror.countColumn(line, null, tabSize); | ||
}; | ||
@@ -7,0 +7,0 @@ var myIndent = getIndent(firstLine); |
@@ -167,2 +167,8 @@ (function() { | ||
}; | ||
// Used by addon/edit/closetag.js | ||
CodeMirror.scanForClosingTag = function(cm, pos, name, end) { | ||
var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); | ||
return !!findMatchingClose(iter, name); | ||
}; | ||
})(); |
@@ -16,10 +16,10 @@ (function() { | ||
var list = [], seen = {}; | ||
function scan(dir) { | ||
var re = new RegExp(word.source, "g"); | ||
for (var dir = -1; dir <= 1; dir += 2) { | ||
var line = cur.line, end = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir; | ||
for (; line != end; line += dir) { | ||
var text = editor.getLine(line), m; | ||
var re = new RegExp(word.source, "g"); | ||
while (m = re.exec(text)) { | ||
if (line == cur.line && m[0] === curWord) continue; | ||
if ((!curWord || m[0].indexOf(curWord) == 0) && !seen.hasOwnProperty(m[0])) { | ||
if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) { | ||
seen[m[0]] = true; | ||
@@ -31,6 +31,4 @@ list.push(m[0]); | ||
} | ||
scan(-1); | ||
scan(1); | ||
return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)}; | ||
}); | ||
})(); |
(function () { | ||
"use strict"; | ||
function getHints(cm) { | ||
var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1, | ||
"first-letter": 1, "first-line": 1, "first-child": 1, | ||
before: 1, after: 1, lang: 1}; | ||
CodeMirror.registerHelper("hint", "css", function(cm) { | ||
var cur = cm.getCursor(), token = cm.getTokenAt(cur); | ||
@@ -9,43 +13,35 @@ var inner = CodeMirror.innerMode(cm.getMode(), token.state); | ||
// If it's not a 'word-style' token, ignore the token. | ||
if (!/^[\w$_-]*$/.test(token.string)) { | ||
token = { | ||
start: cur.ch, end: cur.ch, string: "", state: token.state, | ||
type: null | ||
}; | ||
var stack = token.state.stack; | ||
var lastToken = stack && stack.length > 0 ? stack[stack.length - 1] : ""; | ||
if (token.string == ":" || lastToken.indexOf("property") == 0) | ||
token.type = "variable"; | ||
else if (token.string == "{" || lastToken.indexOf("rule") == 0) | ||
token.type = "property"; | ||
var word = token.string, start = token.start, end = token.end; | ||
if (/[^\w$_-]/.test(word)) { | ||
word = ""; start = end = cur.ch; | ||
} | ||
if (!token.type) | ||
return; | ||
var spec = CodeMirror.resolveMode("text/css"); | ||
var keywords = null; | ||
if (token.type.indexOf("property") == 0) | ||
keywords = spec.propertyKeywords; | ||
else if (token.type.indexOf("variable") == 0) | ||
keywords = spec.valueKeywords; | ||
if (!keywords) | ||
return; | ||
var result = []; | ||
for (var name in keywords) { | ||
if (name.indexOf(token.string) == 0 /* > -1 */) | ||
result.push(name); | ||
function add(keywords) { | ||
for (var name in keywords) | ||
if (!word || name.lastIndexOf(word, 0) == 0) | ||
result.push(name); | ||
} | ||
return { | ||
var st = token.state.state; | ||
if (st == "pseudo" || token.type == "variable-3") { | ||
add(pseudoClasses); | ||
} else if (st == "block" || st == "maybeprop") { | ||
add(spec.propertyKeywords); | ||
} else if (st == "prop" || st == "parens" || st == "at" || st == "params") { | ||
add(spec.valueKeywords); | ||
add(spec.colorKeywords); | ||
} else if (st == "media" || st == "media_parens") { | ||
add(spec.mediaTypes); | ||
add(spec.mediaFeatures); | ||
} | ||
if (result.length) return { | ||
list: result, | ||
from: CodeMirror.Pos(cur.line, token.start), | ||
to: CodeMirror.Pos(cur.line, token.end) | ||
from: CodeMirror.Pos(cur.line, start), | ||
to: CodeMirror.Pos(cur.line, end) | ||
}; | ||
} | ||
CodeMirror.registerHelper("hint", "css", getHints); | ||
}); | ||
})(); |
@@ -90,3 +90,3 @@ (function () { | ||
function maybeAdd(str) { | ||
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); | ||
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); | ||
} | ||
@@ -93,0 +93,0 @@ function gatherCompletions(obj) { |
@@ -87,3 +87,3 @@ (function () { | ||
function maybeAdd(str) { | ||
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); | ||
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); | ||
} | ||
@@ -90,0 +90,0 @@ |
@@ -34,6 +34,2 @@ (function () { | ||
completionList = completionList.sort(); | ||
//prevent autocomplete for last word, instead show dropdown with one word | ||
if(completionList.length == 1) { | ||
completionList.push(" "); | ||
} | ||
@@ -70,3 +66,3 @@ return {list: completionList, | ||
function maybeAdd(str) { | ||
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); | ||
if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str); | ||
} | ||
@@ -73,0 +69,0 @@ |
@@ -10,4 +10,6 @@ (function() { | ||
if (cm.somethingSelected()) return; | ||
if (getHints == null) getHints = cm.getHelper(cm.getCursor(), "hint"); | ||
if (getHints == null) return; | ||
if (getHints == null) { | ||
if (options && options.async) return; | ||
else getHints = CodeMirror.hint.auto; | ||
} | ||
@@ -49,2 +51,3 @@ if (cm.state.completionActive) cm.state.completionActive.close(); | ||
else this.cm.replaceRange(getText(completion), data.from, data.to); | ||
CodeMirror.signal(data, "pick", completion); | ||
this.close(); | ||
@@ -66,6 +69,11 @@ }, | ||
var debounce = null, completion = this, finished; | ||
var debounce = 0, completion = this, finished; | ||
var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/; | ||
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length; | ||
var requestAnimationFrame = window.requestAnimationFrame || function(fn) { | ||
return setTimeout(fn, 1000/60); | ||
}; | ||
var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout; | ||
function done() { | ||
@@ -94,4 +102,11 @@ if (finished) return; | ||
function clearDebounce() { | ||
if (debounce) { | ||
cancelAnimationFrame(debounce); | ||
debounce = 0; | ||
} | ||
} | ||
function activity() { | ||
clearTimeout(debounce); | ||
clearDebounce(); | ||
var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line); | ||
@@ -103,3 +118,3 @@ if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch || | ||
} else { | ||
debounce = setTimeout(update, 170); | ||
debounce = requestAnimationFrame(update); | ||
if (completion.widget) completion.widget.close(); | ||
@@ -151,5 +166,5 @@ } | ||
function getHintElement(stopAt, el) { | ||
while (el && el != stopAt) { | ||
if (el.nodeName.toUpperCase() === "LI") return el; | ||
function getHintElement(hintsElement, el) { | ||
while (el && el != hintsElement) { | ||
if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el; | ||
el = el.parentNode; | ||
@@ -241,3 +256,6 @@ } | ||
var t = getHintElement(hints, e.target || e.srcElement); | ||
if (t && t.hintId != null) widget.changeActive(t.hintId); | ||
if (t && t.hintId != null) { | ||
widget.changeActive(t.hintId); | ||
if (options.completeOnSingleClick) widget.pick(); | ||
} | ||
}); | ||
@@ -293,2 +311,33 @@ | ||
}; | ||
CodeMirror.registerHelper("hint", "auto", function(cm, options) { | ||
var helpers = cm.getHelpers(cm.getCursor(), "hint"); | ||
if (helpers.length) { | ||
for (var i = 0; i < helpers.length; i++) { | ||
var cur = helpers[i](cm, options); | ||
if (cur && cur.list.length) return cur; | ||
} | ||
} else { | ||
var words = cm.getHelper(cm.getCursor(), "hintWords"); | ||
if (words) return CodeMirror.hint.fromList(cm, {words: words}); | ||
} | ||
}); | ||
CodeMirror.registerHelper("hint", "fromList", function(cm, options) { | ||
var cur = cm.getCursor(), token = cm.getTokenAt(cur); | ||
var found = []; | ||
for (var i = 0; i < options.words.length; i++) { | ||
var word = options.words[i]; | ||
if (word.slice(0, token.string.length) == token.string) | ||
found.push(word); | ||
} | ||
if (found.length) return { | ||
list: found, | ||
from: CodeMirror.Pos(cur.line, token.start), | ||
to: CodeMirror.Pos(cur.line, token.end) | ||
}; | ||
}); | ||
CodeMirror.commands.autocomplete = CodeMirror.showHint; | ||
})(); |
@@ -6,2 +6,6 @@ (function () { | ||
var keywords; | ||
var CONS = { | ||
QUERY_DIV: ";", | ||
ALIAS_KEYWORD: "AS" | ||
}; | ||
@@ -38,7 +42,6 @@ function getKeywords(editor) { | ||
var table = editor.getTokenAt(prevCur).string; | ||
var columns = tables[table]; | ||
if(!columns) { | ||
if( !tables.hasOwnProperty( table ) ){ | ||
table = findTableByAlias(table, editor); | ||
} | ||
columns = tables[table]; | ||
var columns = tables[table]; | ||
if(!columns) { | ||
@@ -51,28 +54,68 @@ return; | ||
function eachWord(line, f) { | ||
var words = line.text.split(" "); | ||
for(var i = 0; i < words.length; i++) { | ||
f(words[i]); | ||
function eachWord(lineText, f) { | ||
if( !lineText ){return;} | ||
var excepted = /[,;]/g; | ||
var words = lineText.split( " " ); | ||
for( var i = 0; i < words.length; i++ ){ | ||
f( words[i]?words[i].replace( excepted, '' ) : '' ); | ||
} | ||
} | ||
// Tries to find possible table name from alias. | ||
function convertCurToNumber( cur ){ | ||
// max characters of a line is 999,999. | ||
return cur.line + cur.ch / Math.pow( 10, 6 ); | ||
} | ||
function convertNumberToCur( num ){ | ||
return CodeMirror.Pos(Math.floor( num ), +num.toString().split( '.' ).pop()); | ||
} | ||
function findTableByAlias(alias, editor) { | ||
var doc = editor.doc; | ||
var fullQuery = doc.getValue(); | ||
var aliasUpperCase = alias.toUpperCase(); | ||
var previousWord = ""; | ||
var table = ""; | ||
var separator = []; | ||
var validRange = { | ||
start: CodeMirror.Pos( 0, 0 ), | ||
end: CodeMirror.Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length ) | ||
}; | ||
editor.eachLine(function(line) { | ||
eachWord(line, function(word) { | ||
//add separator | ||
var indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV ); | ||
while( indexOfSeparator != -1 ){ | ||
separator.push( doc.posFromIndex(indexOfSeparator)); | ||
indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1); | ||
} | ||
separator.unshift( CodeMirror.Pos( 0, 0 ) ); | ||
separator.push( CodeMirror.Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) ); | ||
//find valieRange | ||
var prevItem = 0; | ||
var current = convertCurToNumber( editor.getCursor() ); | ||
for( var i=0; i< separator.length; i++){ | ||
var _v = convertCurToNumber( separator[i] ); | ||
if( current > prevItem && current <= _v ){ | ||
validRange = { start: convertNumberToCur( prevItem ), end: convertNumberToCur( _v ) }; | ||
break; | ||
} | ||
prevItem = _v; | ||
} | ||
var query = doc.getRange(validRange.start, validRange.end, false); | ||
for(var i=0; i < query.length; i++){ | ||
var lineText = query[i]; | ||
eachWord( lineText, function( word ){ | ||
var wordUpperCase = word.toUpperCase(); | ||
if(wordUpperCase === aliasUpperCase) { | ||
if(tables.hasOwnProperty(previousWord)) { | ||
if( wordUpperCase === aliasUpperCase && tables.hasOwnProperty( previousWord ) ){ | ||
table = previousWord; | ||
} | ||
} | ||
if(wordUpperCase !== "AS") { | ||
if( wordUpperCase !== CONS.ALIAS_KEYWORD ){ | ||
previousWord = word; | ||
} | ||
}); | ||
}); | ||
if( table ){ break; } | ||
} | ||
return table; | ||
@@ -86,5 +129,3 @@ } | ||
var token = editor.getTokenAt(cur); | ||
var result = []; | ||
var search = token.string.trim(); | ||
@@ -91,0 +132,0 @@ |
@@ -23,9 +23,9 @@ (function() { | ||
if (childList) { | ||
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0) | ||
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].lastIndexOf(prefix, 0) == 0) | ||
result.push("<" + childList[i]); | ||
} else { | ||
for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0)) | ||
for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.lastIndexOf(prefix, 0) == 0)) | ||
result.push("<" + name); | ||
} | ||
if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0)) | ||
if (cx && (!prefix || ("/" + cx.tagName).lastIndexOf(prefix, 0) == 0)) | ||
result.push("</" + cx.tagName + ">"); | ||
@@ -50,3 +50,3 @@ } else { | ||
} | ||
for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0) | ||
for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].lastIndexOf(prefix, 0) == 0) | ||
result.push(quote + atValues[i] + quote); | ||
@@ -58,3 +58,3 @@ } else { // An attribute name | ||
} | ||
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0)) | ||
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.lastIndexOf(prefix, 0) == 0)) | ||
result.push(attr); | ||
@@ -61,0 +61,0 @@ } |
@@ -115,3 +115,3 @@ (function() { | ||
else | ||
updateLinting(cm, options.getAnnotations(cm.getValue(), options)); | ||
updateLinting(cm, options.getAnnotations(cm.getValue(), options.options)); | ||
} | ||
@@ -118,0 +118,0 @@ |
@@ -32,3 +32,3 @@ (function() { | ||
this.diff = getDiff(orig, options.value); | ||
this.diff = getDiff(asString(orig), asString(options.value)); | ||
this.diffOutOfDate = false; | ||
@@ -356,2 +356,7 @@ | ||
function asString(obj) { | ||
if (typeof obj == "string") return obj; | ||
else return obj.getValue(); | ||
} | ||
// Operations on diffs | ||
@@ -358,0 +363,0 @@ |
@@ -50,3 +50,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) { | ||
var curInner = state.innerActive, oldContent = stream.string; | ||
var found = indexOf(oldContent, curInner.close, stream.pos); | ||
if (!curInner.close && stream.sol()) { | ||
state.innerActive = state.inner = null; | ||
return this.token(stream, state); | ||
} | ||
var found = curInner.close ? indexOf(oldContent, curInner.close, stream.pos) : -1; | ||
if (found == stream.pos) { | ||
@@ -60,4 +64,2 @@ stream.match(curInner.close); | ||
if (found > -1) stream.string = oldContent; | ||
var cur = stream.current(), found = cur.indexOf(curInner.close); | ||
if (found > -1) stream.backUp(cur.length - found); | ||
@@ -64,0 +66,0 @@ if (curInner.innerStyle) { |
@@ -13,2 +13,3 @@ /* Just enough of CodeMirror to run runMode under node.js */ | ||
this.string = string; | ||
this.lineStart = 0; | ||
} | ||
@@ -45,3 +46,3 @@ StringStream.prototype = { | ||
backUp: function(n) {this.pos -= n;}, | ||
column: function() {return this.start;}, | ||
column: function() {return this.start - this.lineStart;}, | ||
indentation: function() {return 0;}, | ||
@@ -63,3 +64,8 @@ match: function(pattern, consume, caseInsensitive) { | ||
}, | ||
current: function(){return this.string.slice(this.start, this.pos);} | ||
current: function(){return this.string.slice(this.start, this.pos);}, | ||
hideFirstChars: function(n, inner) { | ||
this.lineStart += n; | ||
try { return inner(); } | ||
finally { this.lineStart -= n; } | ||
} | ||
}; | ||
@@ -75,13 +81,22 @@ CodeMirror.StringStream = StringStream; | ||
CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; }; | ||
CodeMirror.resolveMode = function(spec) { | ||
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { | ||
spec = mimeModes[spec]; | ||
} else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { | ||
spec = mimeModes[spec.name]; | ||
} | ||
if (typeof spec == "string") return {name: spec}; | ||
else return spec || {name: "null"}; | ||
}; | ||
CodeMirror.getMode = function (options, spec) { | ||
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) | ||
spec = mimeModes[spec]; | ||
if (typeof spec == "string") | ||
var mname = spec, config = {}; | ||
else if (spec != null) | ||
var mname = spec.name, config = spec; | ||
var mfactory = modes[mname]; | ||
spec = CodeMirror.resolveMode(spec); | ||
var mfactory = modes[spec.name]; | ||
if (!mfactory) throw new Error("Unknown mode: " + spec); | ||
return mfactory(options, config || {}); | ||
return mfactory(options, spec); | ||
}; | ||
CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min; | ||
CodeMirror.defineMode("null", function() { | ||
return {token: function(stream) {stream.skipToEnd();}}; | ||
}); | ||
CodeMirror.defineMIME("text/plain", "null"); | ||
@@ -129,3 +144,3 @@ CodeMirror.runMode = function (string, modespec, callback, options) { | ||
var lines = splitLines(string), state = CodeMirror.startState(mode); | ||
var lines = splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); | ||
for (var i = 0, e = lines.length; i < e; ++i) { | ||
@@ -132,0 +147,0 @@ if (i) callback("\n"); |
@@ -46,3 +46,3 @@ CodeMirror.runMode = function(string, modespec, callback, options) { | ||
var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); | ||
var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode); | ||
for (var i = 0, e = lines.length; i < e; ++i) { | ||
@@ -49,0 +49,0 @@ if (i) callback("\n"); |
@@ -8,2 +8,3 @@ /* Just enough of CodeMirror to run runMode under node.js */ | ||
this.string = string; | ||
this.lineStart = 0; | ||
} | ||
@@ -40,3 +41,3 @@ StringStream.prototype = { | ||
backUp: function(n) {this.pos -= n;}, | ||
column: function() {return this.start;}, | ||
column: function() {return this.start - this.lineStart;}, | ||
indentation: function() {return 0;}, | ||
@@ -58,3 +59,8 @@ match: function(pattern, consume, caseInsensitive) { | ||
}, | ||
current: function(){return this.string.slice(this.start, this.pos);} | ||
current: function(){return this.string.slice(this.start, this.pos);}, | ||
hideFirstChars: function(n, inner) { | ||
this.lineStart += n; | ||
try { return inner(); } | ||
finally { this.lineStart -= n; } | ||
} | ||
}; | ||
@@ -82,17 +88,22 @@ exports.StringStream = StringStream; | ||
exports.resolveMode = function(spec) { | ||
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { | ||
spec = mimeModes[spec]; | ||
} else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { | ||
spec = mimeModes[spec.name]; | ||
} | ||
if (typeof spec == "string") return {name: spec}; | ||
else return spec || {name: "null"}; | ||
}; | ||
exports.getMode = function(options, spec) { | ||
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) | ||
spec = mimeModes[spec]; | ||
if (typeof spec == "string") | ||
var mname = spec, config = {}; | ||
else if (spec != null) | ||
var mname = spec.name, config = spec; | ||
var mfactory = modes[mname]; | ||
spec = exports.resolveMode(mimeModes[spec]); | ||
var mfactory = modes[spec.name]; | ||
if (!mfactory) throw new Error("Unknown mode: " + spec); | ||
return mfactory(options, config || {}); | ||
return mfactory(options, spec); | ||
}; | ||
exports.registerHelper = exports.registerGlobalHelper = Math.min; | ||
exports.runMode = function(string, modespec, callback) { | ||
var mode = exports.getMode({indentUnit: 2}, modespec); | ||
var lines = splitLines(string), state = exports.startState(mode); | ||
var lines = splitLines(string), state = (options && options.state) || exports.startState(mode); | ||
for (var i = 0, e = lines.length; i < e; ++i) { | ||
@@ -99,0 +110,0 @@ if (i) callback("\n"); |
@@ -10,3 +10,11 @@ // Define search commands. Depends on dialog.js or another | ||
(function() { | ||
function searchOverlay(query) { | ||
function searchOverlay(query, caseInsensitive) { | ||
var startChar; | ||
if (typeof query == "string") { | ||
startChar = query.charAt(0); | ||
query = new RegExp("^" + query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), | ||
caseInsensitive ? "i" : ""); | ||
} else { | ||
query = new RegExp("^(?:" + query.source + ")", query.ignoreCase ? "i" : ""); | ||
} | ||
if (typeof query == "string") return {token: function(stream) { | ||
@@ -21,2 +29,4 @@ if (stream.match(query)) return "searching"; | ||
stream.next(); | ||
if (startChar) | ||
stream.skipTo(startChar) || stream.skipToEnd(); | ||
if (stream.match(query, false)) break; | ||
@@ -34,9 +44,12 @@ } | ||
} | ||
function queryCaseInsensitive(query) { | ||
return typeof query == "string" && query == query.toLowerCase(); | ||
} | ||
function getSearchCursor(cm, query, pos) { | ||
// Heuristic: if the query string is all lowercase, do a case insensitive search. | ||
return cm.getSearchCursor(query, pos, typeof query == "string" && query == query.toLowerCase()); | ||
return cm.getSearchCursor(query, pos, queryCaseInsensitive(query)); | ||
} | ||
function dialog(cm, text, shortText, f) { | ||
if (cm.openDialog) cm.openDialog(text, f); | ||
else f(prompt(shortText, "")); | ||
function dialog(cm, text, shortText, deflt, f) { | ||
if (cm.openDialog) cm.openDialog(text, f, {value: deflt}); | ||
else f(prompt(shortText, deflt)); | ||
} | ||
@@ -56,7 +69,7 @@ function confirmDialog(cm, text, shortText, fs) { | ||
if (state.query) return findNext(cm, rev); | ||
dialog(cm, queryDialog, "Search for:", function(query) { | ||
dialog(cm, queryDialog, "Search for:", cm.getSelection(), function(query) { | ||
cm.operation(function() { | ||
if (!query || state.query) return; | ||
state.query = parseQuery(query); | ||
cm.removeOverlay(state.overlay); | ||
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query)); | ||
state.overlay = searchOverlay(state.query); | ||
@@ -92,6 +105,6 @@ cm.addOverlay(state.overlay); | ||
function replace(cm, all) { | ||
dialog(cm, replaceQueryDialog, "Replace:", function(query) { | ||
dialog(cm, replaceQueryDialog, "Replace:", cm.getSelection(), function(query) { | ||
if (!query) return; | ||
query = parseQuery(query); | ||
dialog(cm, replacementQueryDialog, "Replace with:", function(text) { | ||
dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) { | ||
if (all) { | ||
@@ -98,0 +111,0 @@ cm.operation(function() { |
@@ -50,2 +50,3 @@ (function(){ | ||
} else { // String query | ||
var origQuery = query; | ||
if (caseFold) query = query.toLowerCase(); | ||
@@ -62,29 +63,41 @@ var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; | ||
this.matches = function(reverse, pos) { | ||
var line = fold(doc.getLine(pos.line)), len = query.length, match; | ||
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) | ||
: (match = line.indexOf(query, pos.ch)) != -1) | ||
return {from: Pos(pos.line, match), | ||
to: Pos(pos.line, match + len)}; | ||
if (reverse) { | ||
var orig = doc.getLine(pos.line).slice(0, pos.ch), line = fold(orig); | ||
var match = line.lastIndexOf(query); | ||
if (match > -1) { | ||
match = adjustPos(orig, line, match); | ||
return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; | ||
} | ||
} else { | ||
var orig = doc.getLine(pos.line).slice(pos.ch), line = fold(orig); | ||
var match = line.indexOf(query); | ||
if (match > -1) { | ||
match = adjustPos(orig, line, match) + pos.ch; | ||
return {from: Pos(pos.line, match), to: Pos(pos.line, match + origQuery.length)}; | ||
} | ||
} | ||
}; | ||
} | ||
} else { | ||
var origTarget = origQuery.split("\n"); | ||
this.matches = function(reverse, pos) { | ||
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(doc.getLine(ln)); | ||
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); | ||
if (reverse ? offsetA > pos.ch || offsetA != match.length | ||
: offsetA < pos.ch || offsetA != line.length - match.length) | ||
return; | ||
for (;;) { | ||
if (reverse ? !ln : ln == doc.lineCount() - 1) return; | ||
line = fold(doc.getLine(ln += reverse ? -1 : 1)); | ||
match = target[reverse ? --idx : ++idx]; | ||
if (idx > 0 && idx < target.length - 1) { | ||
if (line != match) return; | ||
else continue; | ||
} | ||
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); | ||
if (reverse ? offsetB != line.length - match.length : offsetB != match.length) | ||
return; | ||
var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); | ||
return {from: reverse ? end : start, to: reverse ? start : end}; | ||
var last = target.length - 1; | ||
if (reverse) { | ||
if (pos.line - (target.length - 1) < doc.firstLine()) return; | ||
if (fold(doc.getLine(pos.line).slice(0, origTarget[last].length)) != target[target.length - 1]) return; | ||
var to = Pos(pos.line, origTarget[last].length); | ||
for (var ln = pos.line - 1, i = last - 1; i >= 1; --i, --ln) | ||
if (target[i] != fold(doc.getLine(ln))) return; | ||
var line = doc.getLine(ln), cut = line.length - origTarget[0].length; | ||
if (fold(line.slice(cut)) != target[0]) return; | ||
return {from: Pos(ln, cut), to: to}; | ||
} else { | ||
if (pos.line + (target.length - 1) > doc.lastLine()) return; | ||
var line = doc.getLine(pos.line), cut = line.length - origTarget[0].length; | ||
if (fold(line.slice(cut)) != target[0]) return; | ||
var from = Pos(pos.line, cut); | ||
for (var ln = pos.line + 1, i = 1; i < last; ++i, ++ln) | ||
if (target[i] != fold(doc.getLine(ln))) return; | ||
if (doc.getLine(ln).slice(0, origTarget[last].length) != target[last]) return; | ||
return {from: from, to: Pos(ln, origTarget[last].length)}; | ||
} | ||
@@ -111,3 +124,2 @@ }; | ||
if (this.pos = this.matches(reverse, pos)) { | ||
if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); } | ||
this.atOccurrence = true; | ||
@@ -140,2 +152,14 @@ return this.pos.match || true; | ||
// Maps a position in a case-folded line back to a position in the original line | ||
// (compensating for codepoints increasing in number during folding) | ||
function adjustPos(orig, folded, pos) { | ||
if (orig.length == folded.length) return pos; | ||
for (var pos1 = Math.min(pos, orig.length);;) { | ||
var len1 = orig.slice(0, pos1).toLowerCase().length; | ||
if (len1 < pos) ++pos1; | ||
else if (len1 > pos) --pos1; | ||
else return pos1; | ||
} | ||
} | ||
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { | ||
@@ -142,0 +166,0 @@ return new SearchCursor(this.doc, query, pos, caseFold); |
@@ -15,6 +15,6 @@ // Because sometimes you need to style the cursor's line. | ||
if (val && !prev) { | ||
updateActiveLine(cm); | ||
cm.on("cursorActivity", updateActiveLine); | ||
updateActiveLine(cm, cm.getCursor().line); | ||
cm.on("beforeSelectionChange", selectionChange); | ||
} else if (!val && prev) { | ||
cm.off("cursorActivity", updateActiveLine); | ||
cm.off("beforeSelectionChange", selectionChange); | ||
clearActiveLine(cm); | ||
@@ -32,10 +32,16 @@ delete cm.state.activeLine; | ||
function updateActiveLine(cm) { | ||
var line = cm.getLineHandleVisualStart(cm.getCursor().line); | ||
function updateActiveLine(cm, selectedLine) { | ||
var line = cm.getLineHandleVisualStart(selectedLine); | ||
if (cm.state.activeLine == line) return; | ||
clearActiveLine(cm); | ||
cm.addLineClass(line, "wrap", WRAP_CLASS); | ||
cm.addLineClass(line, "background", BACK_CLASS); | ||
cm.state.activeLine = line; | ||
cm.operation(function() { | ||
clearActiveLine(cm); | ||
cm.addLineClass(line, "wrap", WRAP_CLASS); | ||
cm.addLineClass(line, "background", BACK_CLASS); | ||
cm.state.activeLine = line; | ||
}); | ||
} | ||
function selectionChange(cm, sel) { | ||
updateActiveLine(cm, sel.head.line); | ||
} | ||
})(); |
@@ -254,3 +254,3 @@ // Glue code between CodeMirror and Tern. | ||
var ch, pos = lex.pos || 0, tabSize = cm.getOption("tabSize"); | ||
var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize"); | ||
for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) { | ||
@@ -272,3 +272,3 @@ var str = cm.getLine(line), extra = 0; | ||
if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0) | ||
return showArgHints(ts, cm, pos); | ||
return showArgHints(ts, cm, argPos); | ||
@@ -284,3 +284,3 @@ ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) { | ||
}; | ||
showArgHints(ts, cm, pos); | ||
showArgHints(ts, cm, argPos); | ||
}); | ||
@@ -287,0 +287,0 @@ } |
@@ -39,2 +39,5 @@ (function() { | ||
var lines = cm.getRange(from, to, false); | ||
if (!lines.length) return null; | ||
var leadingSpace = lines[0].match(/^[ \t]*/)[0]; | ||
for (var i = 0; i < lines.length; ++i) { | ||
@@ -46,12 +49,18 @@ var text = lines[i], oldLen = curLine.length, spaceInserted = 0; | ||
} | ||
var spaceTrimmed = ""; | ||
if (i) { | ||
spaceTrimmed = text.match(/^\s*/)[0]; | ||
text = text.slice(spaceTrimmed.length); | ||
} | ||
curLine += text; | ||
if (i) { | ||
var firstBreak = curLine.length > column && findBreakPoint(curLine, column, wrapOn, killTrailing); | ||
var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed && | ||
findBreakPoint(curLine, column, wrapOn, killTrailing); | ||
// If this isn't broken, or is broken at a different point, remove old break | ||
if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) { | ||
changes.push({text: spaceInserted ? " " : "", | ||
changes.push({text: [spaceInserted ? " " : ""], | ||
from: Pos(curNo, oldLen), | ||
to: Pos(curNo + 1, 0)}); | ||
to: Pos(curNo + 1, spaceTrimmed.length)}); | ||
} else { | ||
curLine = text; | ||
curLine = leadingSpace + text; | ||
++curNo; | ||
@@ -62,6 +71,6 @@ } | ||
var bp = findBreakPoint(curLine, column, wrapOn, killTrailing); | ||
changes.push({text: "\n", | ||
changes.push({text: ["", leadingSpace], | ||
from: Pos(curNo, bp.from), | ||
to: Pos(curNo, bp.to)}); | ||
curLine = curLine.slice(bp.to); | ||
curLine = leadingSpace + curLine.slice(bp.to); | ||
++curNo; | ||
@@ -76,2 +85,3 @@ } | ||
}); | ||
return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null; | ||
} | ||
@@ -83,7 +93,7 @@ | ||
var para = findParagraph(this, pos, options); | ||
wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options); | ||
return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options); | ||
}); | ||
CodeMirror.defineExtension("wrapRange", function(from, to, options) { | ||
wrapRange(this, from, to, options || {}); | ||
return wrapRange(this, from, to, options || {}); | ||
}); | ||
@@ -99,7 +109,9 @@ | ||
} | ||
var madeChange = false; | ||
if (paras.length) cm.operation(function() { | ||
for (var i = paras.length - 1; i >= 0; --i) | ||
wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options); | ||
madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options); | ||
}); | ||
return madeChange; | ||
}); | ||
})(); |
@@ -0,1 +1,5 @@ | ||
// Kludge in HTML5 tag recognition in IE8 | ||
document.createElement("section"); | ||
document.createElement("article"); | ||
(function() { | ||
@@ -40,4 +44,6 @@ var pending = false, prevVal = null; | ||
window.addEventListener("scroll", updateSoon); | ||
window.addEventListener("load", updateSoon); | ||
if (window.addEventListener) { | ||
window.addEventListener("scroll", updateSoon); | ||
window.addEventListener("load", updateSoon); | ||
} | ||
})(); |
@@ -204,2 +204,7 @@ (function() { | ||
function clearMark(cm) { | ||
cm.setExtending(false); | ||
cm.setCursor(cm.getCursor()); | ||
} | ||
function getInput(cm, msg, f) { | ||
@@ -238,2 +243,7 @@ if (cm.openDialog) | ||
function quit(cm) { | ||
cm.execCommand("clearSearch"); | ||
clearMark(cm); | ||
} | ||
// Actual keymap | ||
@@ -254,2 +264,3 @@ | ||
addToRing(cm.getSelection()); | ||
clearMark(cm); | ||
}, | ||
@@ -340,3 +351,3 @@ "Ctrl-Y": function(cm) { | ||
"Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd", | ||
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace", | ||
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace", | ||
"Alt-/": "autocomplete", | ||
@@ -343,0 +354,0 @@ "Ctrl-J": "newlineAndIndent", "Enter": false, "Tab": "indentAuto", |
@@ -206,7 +206,21 @@ CodeMirror.defineMode("clike", function(config, parserConfig) { | ||
function mimes(ms, mode) { | ||
for (var i = 0; i < ms.length; ++i) CodeMirror.defineMIME(ms[i], mode); | ||
function def(mimes, mode) { | ||
var words = []; | ||
function add(obj) { | ||
if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) | ||
words.push(prop); | ||
} | ||
add(mode.keywords); | ||
add(mode.builtin); | ||
add(mode.atoms); | ||
if (words.length) { | ||
mode.helperType = mimes[0]; | ||
CodeMirror.registerHelper("hintWords", mimes[0], words); | ||
} | ||
for (var i = 0; i < mimes.length; ++i) | ||
CodeMirror.defineMIME(mimes[i], mode); | ||
} | ||
mimes(["text/x-csrc", "text/x-c", "text/x-chdr"], { | ||
def(["text/x-csrc", "text/x-c", "text/x-chdr"], { | ||
name: "clike", | ||
@@ -216,5 +230,7 @@ keywords: words(cKeywords), | ||
atoms: words("null"), | ||
hooks: {"#": cppHook} | ||
hooks: {"#": cppHook}, | ||
modeProps: {fold: ["brace", "include"]} | ||
}); | ||
mimes(["text/x-c++src", "text/x-c++hdr"], { | ||
def(["text/x-c++src", "text/x-c++hdr"], { | ||
name: "clike", | ||
@@ -227,3 +243,4 @@ keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " + | ||
atoms: words("true false null"), | ||
hooks: {"#": cppHook} | ||
hooks: {"#": cppHook}, | ||
modeProps: {fold: ["brace", "include"]} | ||
}); | ||
@@ -244,3 +261,4 @@ CodeMirror.defineMIME("text/x-java", { | ||
} | ||
} | ||
}, | ||
modeProps: {fold: ["brace", "import"]} | ||
}); | ||
@@ -310,3 +328,3 @@ CodeMirror.defineMIME("text/x-csharp", { | ||
}); | ||
mimes(["x-shader/x-vertex", "x-shader/x-fragment"], { | ||
def(["x-shader/x-vertex", "x-shader/x-fragment"], { | ||
name: "clike", | ||
@@ -365,4 +383,5 @@ keywords: words("float int bool void " + | ||
"gl_MaxDrawBuffers"), | ||
hooks: {"#": cppHook} | ||
hooks: {"#": cppHook}, | ||
modeProps: {fold: ["brace", "include"]} | ||
}); | ||
}()); |
@@ -122,3 +122,3 @@ /** | ||
if (stream.match(stringPrefixes)) { | ||
state.tokenize = tokenFactory(stream.current(), "string"); | ||
state.tokenize = tokenFactory(stream.current(), false, "string"); | ||
return state.tokenize(stream, state); | ||
@@ -129,3 +129,3 @@ } | ||
if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division | ||
state.tokenize = tokenFactory(stream.current(), "string-2"); | ||
state.tokenize = tokenFactory(stream.current(), true, "string-2"); | ||
return state.tokenize(stream, state); | ||
@@ -166,4 +166,3 @@ } else { | ||
function tokenFactory(delimiter, outclass) { | ||
var singleline = delimiter.length == 1; | ||
function tokenFactory(delimiter, singleline, outclass) { | ||
return function(stream, state) { | ||
@@ -170,0 +169,0 @@ while (!stream.eol()) { |
@@ -6,42 +6,42 @@ CodeMirror.defineMode("css", function(config, parserConfig) { | ||
var indentUnit = config.indentUnit || config.tabSize || 2, | ||
hooks = parserConfig.hooks || {}, | ||
atMediaTypes = parserConfig.atMediaTypes || {}, | ||
atMediaFeatures = parserConfig.atMediaFeatures || {}, | ||
var indentUnit = config.indentUnit, | ||
tokenHooks = parserConfig.tokenHooks, | ||
mediaTypes = parserConfig.mediaTypes || {}, | ||
mediaFeatures = parserConfig.mediaFeatures || {}, | ||
propertyKeywords = parserConfig.propertyKeywords || {}, | ||
colorKeywords = parserConfig.colorKeywords || {}, | ||
valueKeywords = parserConfig.valueKeywords || {}, | ||
allowNested = !!parserConfig.allowNested, | ||
type = null; | ||
fontProperties = parserConfig.fontProperties || {}, | ||
allowNested = parserConfig.allowNested; | ||
var type, override; | ||
function ret(style, tp) { type = tp; return style; } | ||
// Tokenizers | ||
function tokenBase(stream, state) { | ||
var ch = stream.next(); | ||
if (hooks[ch]) { | ||
// result[0] is style and result[1] is type | ||
var result = hooks[ch](stream, state); | ||
if (tokenHooks[ch]) { | ||
var result = tokenHooks[ch](stream, state); | ||
if (result !== false) return result; | ||
} | ||
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} | ||
else if (ch == "=") ret(null, "compare"); | ||
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); | ||
else if (ch == "\"" || ch == "'") { | ||
if (ch == "@") { | ||
stream.eatWhile(/[\w\\\-]/); | ||
return ret("def", stream.current()); | ||
} else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) { | ||
return ret(null, "compare"); | ||
} else if (ch == "\"" || ch == "'") { | ||
state.tokenize = tokenString(ch); | ||
return state.tokenize(stream, state); | ||
} | ||
else if (ch == "#") { | ||
} else if (ch == "#") { | ||
stream.eatWhile(/[\w\\\-]/); | ||
return ret("atom", "hash"); | ||
} | ||
else if (ch == "!") { | ||
} else if (ch == "!") { | ||
stream.match(/^\s*\w*/); | ||
return ret("keyword", "important"); | ||
} | ||
else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { | ||
} else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) { | ||
stream.eatWhile(/[\w.%]/); | ||
return ret("number", "unit"); | ||
} | ||
else if (ch === "-") { | ||
if (/\d/.test(stream.peek())) { | ||
} else if (ch === "-") { | ||
if (/[\d.]/.test(stream.peek())) { | ||
stream.eatWhile(/[\w.%]/); | ||
@@ -52,38 +52,31 @@ return ret("number", "unit"); | ||
} | ||
} | ||
else if (/[,+>*\/]/.test(ch)) { | ||
} else if (/[,+>*\/]/.test(ch)) { | ||
return ret(null, "select-op"); | ||
} | ||
else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { | ||
} else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { | ||
return ret("qualifier", "qualifier"); | ||
} | ||
else if (ch == ":") { | ||
return ret("operator", ch); | ||
} | ||
else if (/[;{}\[\]\(\)]/.test(ch)) { | ||
} else if (/[:;{}\[\]\(\)]/.test(ch)) { | ||
return ret(null, ch); | ||
} | ||
else if (ch == "u" && stream.match("rl(")) { | ||
} else if (ch == "u" && stream.match("rl(")) { | ||
stream.backUp(1); | ||
state.tokenize = tokenParenthesized; | ||
return ret("property", "variable"); | ||
} | ||
else { | ||
return ret("property", "word"); | ||
} else if (/[\w\\\-]/.test(ch)) { | ||
stream.eatWhile(/[\w\\\-]/); | ||
return ret("property", "variable"); | ||
return ret("property", "word"); | ||
} else { | ||
return ret(null, null); | ||
} | ||
} | ||
function tokenString(quote, nonInclusive) { | ||
function tokenString(quote) { | ||
return function(stream, state) { | ||
var escaped = false, ch; | ||
while ((ch = stream.next()) != null) { | ||
if (ch == quote && !escaped) | ||
if (ch == quote && !escaped) { | ||
if (quote == ")") stream.backUp(1); | ||
break; | ||
} | ||
escaped = !escaped && ch == "\\"; | ||
} | ||
if (!escaped) { | ||
if (nonInclusive) stream.backUp(1); | ||
state.tokenize = tokenBase; | ||
} | ||
if (ch == quote || !escaped && quote != ")") state.tokenize = null; | ||
return ret("string", "string"); | ||
@@ -96,214 +89,234 @@ }; | ||
if (!stream.match(/\s*[\"\']/, false)) | ||
state.tokenize = tokenString(")", true); | ||
state.tokenize = tokenString(")"); | ||
else | ||
state.tokenize = tokenBase; | ||
state.tokenize = null; | ||
return ret(null, "("); | ||
} | ||
return { | ||
startState: function(base) { | ||
return {tokenize: tokenBase, | ||
baseIndent: base || 0, | ||
stack: [], | ||
lastToken: null}; | ||
}, | ||
// Context management | ||
token: function(stream, state) { | ||
function Context(type, indent, prev) { | ||
this.type = type; | ||
this.indent = indent; | ||
this.prev = prev; | ||
} | ||
// Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) | ||
// | ||
// rule** or **ruleset: | ||
// A selector + braces combo, or an at-rule. | ||
// | ||
// declaration block: | ||
// A sequence of declarations. | ||
// | ||
// declaration: | ||
// A property + colon + value combo. | ||
// | ||
// property value: | ||
// The entire value of a property. | ||
// | ||
// component value: | ||
// A single piece of a property value. Like the 5px in | ||
// text-shadow: 0 0 5px blue;. Can also refer to things that are | ||
// multiple terms, like the 1-4 terms that make up the background-size | ||
// portion of the background shorthand. | ||
// | ||
// term: | ||
// The basic unit of author-facing CSS, like a single number (5), | ||
// dimension (5px), string ("foo"), or function. Officially defined | ||
// by the CSS 2.1 grammar (look for the 'term' production) | ||
// | ||
// | ||
// simple selector: | ||
// A single atomic selector, like a type selector, an attr selector, a | ||
// class selector, etc. | ||
// | ||
// compound selector: | ||
// One or more simple selectors without a combinator. div.example is | ||
// compound, div > .example is not. | ||
// | ||
// complex selector: | ||
// One or more compound selectors chained with combinators. | ||
// | ||
// combinator: | ||
// The parts of selectors that express relationships. There are four | ||
// currently - the space (descendant combinator), the greater-than | ||
// bracket (child combinator), the plus sign (next sibling combinator), | ||
// and the tilda (following sibling combinator). | ||
// | ||
// sequence of selectors: | ||
// One or more of the named type of selector chained with commas. | ||
function pushContext(state, stream, type) { | ||
state.context = new Context(type, stream.indentation() + indentUnit, state.context); | ||
return type; | ||
} | ||
state.tokenize = state.tokenize || tokenBase; | ||
if (state.tokenize == tokenBase && stream.eatSpace()) return null; | ||
var style = state.tokenize(stream, state); | ||
if (style && typeof style != "string") style = ret(style[0], style[1]); | ||
function popContext(state) { | ||
state.context = state.context.prev; | ||
return state.context.type; | ||
} | ||
// Changing style returned based on context | ||
var context = state.stack[state.stack.length-1]; | ||
if (style == "variable") { | ||
if (type == "variable-definition") state.stack.push("propertyValue"); | ||
return state.lastToken = "variable-2"; | ||
} else if (style == "property") { | ||
var word = stream.current().toLowerCase(); | ||
if (context == "propertyValue") { | ||
if (valueKeywords.hasOwnProperty(word)) { | ||
style = "string-2"; | ||
} else if (colorKeywords.hasOwnProperty(word)) { | ||
style = "keyword"; | ||
} else { | ||
style = "variable-2"; | ||
} | ||
} else if (context == "rule") { | ||
if (!propertyKeywords.hasOwnProperty(word)) { | ||
style += " error"; | ||
} | ||
} else if (context == "block") { | ||
// if a value is present in both property, value, or color, the order | ||
// of preference is property -> color -> value | ||
if (propertyKeywords.hasOwnProperty(word)) { | ||
style = "property"; | ||
} else if (colorKeywords.hasOwnProperty(word)) { | ||
style = "keyword"; | ||
} else if (valueKeywords.hasOwnProperty(word)) { | ||
style = "string-2"; | ||
} else { | ||
style = "tag"; | ||
} | ||
} else if (!context || context == "@media{") { | ||
style = "tag"; | ||
} else if (context == "@media") { | ||
if (atMediaTypes[stream.current()]) { | ||
style = "attribute"; // Known attribute | ||
} else if (/^(only|not)$/.test(word)) { | ||
style = "keyword"; | ||
} else if (word == "and") { | ||
style = "error"; // "and" is only allowed in @mediaType | ||
} else if (atMediaFeatures.hasOwnProperty(word)) { | ||
style = "error"; // Known property, should be in @mediaType( | ||
} else { | ||
// Unknown, expecting keyword or attribute, assuming attribute | ||
style = "attribute error"; | ||
} | ||
} else if (context == "@mediaType") { | ||
if (atMediaTypes.hasOwnProperty(word)) { | ||
style = "attribute"; | ||
} else if (word == "and") { | ||
style = "operator"; | ||
} else if (/^(only|not)$/.test(word)) { | ||
style = "error"; // Only allowed in @media | ||
} else { | ||
// Unknown attribute or property, but expecting property (preceded | ||
// by "and"). Should be in parentheses | ||
style = "error"; | ||
} | ||
} else if (context == "@mediaType(") { | ||
if (propertyKeywords.hasOwnProperty(word)) { | ||
// do nothing, remains "property" | ||
} else if (atMediaTypes.hasOwnProperty(word)) { | ||
style = "error"; // Known property, should be in parentheses | ||
} else if (word == "and") { | ||
style = "operator"; | ||
} else if (/^(only|not)$/.test(word)) { | ||
style = "error"; // Only allowed in @media | ||
} else { | ||
style += " error"; | ||
} | ||
} else if (context == "@import") { | ||
style = "tag"; | ||
} else { | ||
style = "error"; | ||
} | ||
} else if (style == "atom") { | ||
if(!context || context == "@media{" || context == "block") { | ||
style = "builtin"; | ||
} else if (context == "propertyValue") { | ||
if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { | ||
style += " error"; | ||
} | ||
} else { | ||
style = "error"; | ||
} | ||
} else if (context == "@media" && type == "{") { | ||
style = "error"; | ||
} | ||
function pass(type, stream, state) { | ||
return states[state.context.type](type, stream, state); | ||
} | ||
function popAndPass(type, stream, state, n) { | ||
for (var i = n || 1; i > 0; i--) | ||
state.context = state.context.prev; | ||
return pass(type, stream, state); | ||
} | ||
// Push/pop context stack | ||
if (type == "{") { | ||
if (context == "@media" || context == "@mediaType") { | ||
state.stack[state.stack.length-1] = "@media{"; | ||
} | ||
else { | ||
var newContext = allowNested ? "block" : "rule"; | ||
state.stack.push(newContext); | ||
} | ||
// Parser | ||
function wordAsValue(stream) { | ||
var word = stream.current().toLowerCase(); | ||
if (valueKeywords.hasOwnProperty(word)) | ||
override = "atom"; | ||
else if (colorKeywords.hasOwnProperty(word)) | ||
override = "keyword"; | ||
else | ||
override = "variable"; | ||
} | ||
var states = {}; | ||
states.top = function(type, stream, state) { | ||
if (type == "{") { | ||
return pushContext(state, stream, "block"); | ||
} else if (type == "}" && state.context.prev) { | ||
return popContext(state); | ||
} else if (type == "@media") { | ||
return pushContext(state, stream, "media"); | ||
} else if (type == "@font-face") { | ||
return "font_face_before"; | ||
} else if (type && type.charAt(0) == "@") { | ||
return pushContext(state, stream, "at"); | ||
} else if (type == "hash") { | ||
override = "builtin"; | ||
} else if (type == "word") { | ||
override = "tag"; | ||
} else if (type == "variable-definition") { | ||
return "maybeprop"; | ||
} else if (type == "interpolation") { | ||
return pushContext(state, stream, "interpolation"); | ||
} else if (type == ":") { | ||
return "pseudo"; | ||
} else if (allowNested && type == "(") { | ||
return pushContext(state, stream, "params"); | ||
} | ||
return state.context.type; | ||
}; | ||
states.block = function(type, stream, state) { | ||
if (type == "word") { | ||
if (propertyKeywords.hasOwnProperty(stream.current().toLowerCase())) { | ||
override = "property"; | ||
return "maybeprop"; | ||
} else if (allowNested) { | ||
override = stream.match(/^\s*:/, false) ? "property" : "tag"; | ||
return "block"; | ||
} else { | ||
override += " error"; | ||
return "maybeprop"; | ||
} | ||
else if (type == "}") { | ||
if (context == "interpolation") style = "operator"; | ||
// Pop off end of array until { is reached | ||
while(state.stack.length){ | ||
var removed = state.stack.pop(); | ||
if(removed.indexOf("{") > -1 || removed == "block" || removed == "rule"){ | ||
break; | ||
} | ||
} | ||
} | ||
else if (type == "interpolation") state.stack.push("interpolation"); | ||
else if (type == "@media") state.stack.push("@media"); | ||
else if (type == "@import") state.stack.push("@import"); | ||
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) | ||
state.stack[state.stack.length-1] = "@mediaType"; | ||
else if (context == "@mediaType" && stream.current() == ",") | ||
state.stack[state.stack.length-1] = "@media"; | ||
else if (type == "(") { | ||
if (context == "@media" || context == "@mediaType") { | ||
// Make sure @mediaType is used to avoid error on { | ||
state.stack[state.stack.length-1] = "@mediaType"; | ||
state.stack.push("@mediaType("); | ||
} | ||
else state.stack.push("("); | ||
} | ||
else if (type == ")") { | ||
// Pop off end of array until ( is reached | ||
while(state.stack.length){ | ||
var removed = state.stack.pop(); | ||
if(removed.indexOf("(") > -1){ | ||
break; | ||
} | ||
} | ||
} | ||
else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue"); | ||
else if (context == "propertyValue" && type == ";") state.stack.pop(); | ||
else if (context == "@import" && type == ";") state.stack.pop(); | ||
} else if (type == "meta") { | ||
return "block"; | ||
} else if (!allowNested && (type == "hash" || type == "qualifier")) { | ||
override = "error"; | ||
return "block"; | ||
} else { | ||
return states.top(type, stream, state); | ||
} | ||
}; | ||
return state.lastToken = style; | ||
states.maybeprop = function(type, stream, state) { | ||
if (type == ":") return pushContext(state, stream, "prop"); | ||
return pass(type, stream, state); | ||
}; | ||
states.prop = function(type, stream, state) { | ||
if (type == ";") return popContext(state); | ||
if (type == "{" && allowNested) return pushContext(state, stream, "propBlock"); | ||
if (type == "}" || type == "{") return popAndPass(type, stream, state); | ||
if (type == "(") return pushContext(state, stream, "parens"); | ||
if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { | ||
override += " error"; | ||
} else if (type == "word") { | ||
wordAsValue(stream); | ||
} else if (type == "interpolation") { | ||
return pushContext(state, stream, "interpolation"); | ||
} | ||
return "prop"; | ||
}; | ||
states.propBlock = function(type, _stream, state) { | ||
if (type == "}") return popContext(state); | ||
if (type == "word") { override = "property"; return "maybeprop"; } | ||
return state.context.type; | ||
}; | ||
states.parens = function(type, stream, state) { | ||
if (type == "{" || type == "}") return popAndPass(type, stream, state); | ||
if (type == ")") return popContext(state); | ||
return "parens"; | ||
}; | ||
states.pseudo = function(type, stream, state) { | ||
if (type == "word") { | ||
override = "variable-3"; | ||
return state.context.type; | ||
} | ||
return pass(type, stream, state); | ||
}; | ||
states.media = function(type, stream, state) { | ||
if (type == "(") return pushContext(state, stream, "media_parens"); | ||
if (type == "}") return popAndPass(type, stream, state); | ||
if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top"); | ||
if (type == "word") { | ||
var word = stream.current().toLowerCase(); | ||
if (word == "only" || word == "not" || word == "and") | ||
override = "keyword"; | ||
else if (mediaTypes.hasOwnProperty(word)) | ||
override = "attribute"; | ||
else if (mediaFeatures.hasOwnProperty(word)) | ||
override = "property"; | ||
else | ||
override = "error"; | ||
} | ||
return state.context.type; | ||
}; | ||
states.media_parens = function(type, stream, state) { | ||
if (type == ")") return popContext(state); | ||
if (type == "{" || type == "}") return popAndPass(type, stream, state, 2); | ||
return states.media(type, stream, state); | ||
}; | ||
states.font_face_before = function(type, stream, state) { | ||
if (type == "{") | ||
return pushContext(state, stream, "font_face"); | ||
return pass(type, stream, state); | ||
}; | ||
states.font_face = function(type, stream, state) { | ||
if (type == "}") return popContext(state); | ||
if (type == "word") { | ||
if (!fontProperties.hasOwnProperty(stream.current().toLowerCase())) | ||
override = "error"; | ||
else | ||
override = "property"; | ||
return "maybeprop"; | ||
} | ||
return "font_face"; | ||
}; | ||
states.at = function(type, stream, state) { | ||
if (type == ";") return popContext(state); | ||
if (type == "{" || type == "}") return popAndPass(type, stream, state); | ||
if (type == "word") override = "tag"; | ||
else if (type == "hash") override = "builtin"; | ||
return "at"; | ||
}; | ||
states.interpolation = function(type, stream, state) { | ||
if (type == "}") return popContext(state); | ||
if (type == "{" || type == ";") return popAndPass(type, stream, state); | ||
if (type != "variable") override = "error"; | ||
return "interpolation"; | ||
}; | ||
states.params = function(type, stream, state) { | ||
if (type == ")") return popContext(state); | ||
if (type == "{" || type == "}") return popAndPass(type, stream, state); | ||
if (type == "word") wordAsValue(stream); | ||
return "params"; | ||
}; | ||
return { | ||
startState: function(base) { | ||
return {tokenize: null, | ||
state: "top", | ||
context: new Context("top", base || 0, null)}; | ||
}, | ||
token: function(stream, state) { | ||
if (!state.tokenize && stream.eatSpace()) return null; | ||
var style = (state.tokenize || tokenBase)(stream, state); | ||
if (style && typeof style == "object") { | ||
type = style[1]; | ||
style = style[0]; | ||
} | ||
override = style; | ||
state.state = states[state.state](type, stream, state); | ||
return override; | ||
}, | ||
indent: function(state, textAfter) { | ||
var n = state.stack.length; | ||
if (/^\}/.test(textAfter)) | ||
n -= state.stack[n-1] == "propertyValue" ? 2 : 1; | ||
return state.baseIndent + n * indentUnit; | ||
var cx = state.context, ch = textAfter && textAfter.charAt(0); | ||
var indent = cx.indent; | ||
if (cx.prev && | ||
(ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") || | ||
ch == ")" && (cx.type == "parens" || cx.type == "params" || cx.type == "media_parens") || | ||
ch == "{" && (cx.type == "at" || cx.type == "media"))) { | ||
indent = cx.indent - indentUnit; | ||
cx = cx.prev; | ||
} | ||
return indent; | ||
}, | ||
@@ -327,8 +340,8 @@ | ||
var atMediaTypes = keySet([ | ||
var mediaTypes_ = [ | ||
"all", "aural", "braille", "handheld", "print", "projection", "screen", | ||
"tty", "tv", "embossed" | ||
]); | ||
], mediaTypes = keySet(mediaTypes_); | ||
var atMediaFeatures = keySet([ | ||
var mediaFeatures_ = [ | ||
"width", "min-width", "max-width", "height", "min-height", "max-height", | ||
@@ -342,5 +355,5 @@ "device-width", "min-device-width", "max-device-width", "device-height", | ||
"min-resolution", "max-resolution", "scan", "grid" | ||
]); | ||
], mediaFeatures = keySet(mediaFeatures_); | ||
var propertyKeywords = keySet([ | ||
var propertyKeywords_ = [ | ||
"align-content", "align-items", "align-self", "alignment-adjust", | ||
@@ -433,5 +446,5 @@ "alignment-baseline", "anchor-point", "animation", "animation-delay", | ||
"glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode" | ||
]); | ||
], propertyKeywords = keySet(propertyKeywords_); | ||
var colorKeywords = keySet([ | ||
var colorKeywords_ = [ | ||
"aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", | ||
@@ -463,5 +476,5 @@ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", | ||
"whitesmoke", "yellow", "yellowgreen" | ||
]); | ||
], colorKeywords = keySet(colorKeywords_); | ||
var valueKeywords = keySet([ | ||
var valueKeywords_ = [ | ||
"above", "absolute", "activeborder", "activecaption", "afar", | ||
@@ -549,4 +562,12 @@ "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", | ||
"xx-large", "xx-small" | ||
]); | ||
], valueKeywords = keySet(valueKeywords_); | ||
var fontProperties_ = [ | ||
"font-family", "src", "unicode-range", "font-variant", "font-feature-settings", | ||
"font-stretch", "font-weight", "font-style" | ||
], fontProperties = keySet(fontProperties_); | ||
var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_).concat(colorKeywords_).concat(valueKeywords_); | ||
CodeMirror.registerHelper("hintWords", "css", allWords); | ||
function tokenCComment(stream, state) { | ||
@@ -564,32 +585,29 @@ var maybeEnd = false, ch; | ||
function tokenSGMLComment(stream, state) { | ||
if (stream.skipTo("-->")) { | ||
stream.match("-->"); | ||
state.tokenize = null; | ||
} else { | ||
stream.skipToEnd(); | ||
} | ||
return ["comment", "comment"]; | ||
} | ||
CodeMirror.defineMIME("text/css", { | ||
atMediaTypes: atMediaTypes, | ||
atMediaFeatures: atMediaFeatures, | ||
mediaTypes: mediaTypes, | ||
mediaFeatures: mediaFeatures, | ||
propertyKeywords: propertyKeywords, | ||
colorKeywords: colorKeywords, | ||
valueKeywords: valueKeywords, | ||
hooks: { | ||
fontProperties: fontProperties, | ||
tokenHooks: { | ||
"<": function(stream, state) { | ||
function tokenSGMLComment(stream, state) { | ||
var dashes = 0, ch; | ||
while ((ch = stream.next()) != null) { | ||
if (dashes >= 2 && ch == ">") { | ||
state.tokenize = null; | ||
break; | ||
} | ||
dashes = (ch == "-") ? dashes + 1 : 0; | ||
} | ||
return ["comment", "comment"]; | ||
} | ||
if (stream.eat("!")) { | ||
state.tokenize = tokenSGMLComment; | ||
return tokenSGMLComment(stream, state); | ||
} | ||
if (!stream.match("!--")) return false; | ||
state.tokenize = tokenSGMLComment; | ||
return tokenSGMLComment(stream, state); | ||
}, | ||
"/": function(stream, state) { | ||
if (stream.eat("*")) { | ||
state.tokenize = tokenCComment; | ||
return tokenCComment(stream, state); | ||
} | ||
return false; | ||
if (!stream.eat("*")) return false; | ||
state.tokenize = tokenCComment; | ||
return tokenCComment(stream, state); | ||
} | ||
@@ -601,13 +619,24 @@ }, | ||
CodeMirror.defineMIME("text/x-scss", { | ||
atMediaTypes: atMediaTypes, | ||
atMediaFeatures: atMediaFeatures, | ||
mediaTypes: mediaTypes, | ||
mediaFeatures: mediaFeatures, | ||
propertyKeywords: propertyKeywords, | ||
colorKeywords: colorKeywords, | ||
valueKeywords: valueKeywords, | ||
fontProperties: fontProperties, | ||
allowNested: true, | ||
hooks: { | ||
tokenHooks: { | ||
"/": function(stream, state) { | ||
if (stream.eat("/")) { | ||
stream.skipToEnd(); | ||
return ["comment", "comment"]; | ||
} else if (stream.eat("*")) { | ||
state.tokenize = tokenCComment; | ||
return tokenCComment(stream, state); | ||
} else { | ||
return ["operator", "operator"]; | ||
} | ||
}, | ||
":": function(stream) { | ||
if (stream.match(/\s*{/)) { | ||
if (stream.match(/\s*{/)) | ||
return [null, "{"]; | ||
} | ||
return false; | ||
@@ -617,12 +646,24 @@ }, | ||
stream.match(/^[\w-]+/); | ||
if (stream.peek() == ":") { | ||
return ["variable", "variable-definition"]; | ||
} | ||
return ["variable", "variable"]; | ||
if (stream.match(/^\s*:/, false)) | ||
return ["variable-2", "variable-definition"]; | ||
return ["variable-2", "variable"]; | ||
}, | ||
",": function(stream, state) { | ||
if (state.stack[state.stack.length - 1] == "propertyValue" && stream.match(/^ *\$/, false)) { | ||
return ["operator", ";"]; | ||
} | ||
}, | ||
"#": function(stream) { | ||
if (!stream.eat("{")) return false; | ||
return [null, "interpolation"]; | ||
} | ||
}, | ||
name: "css", | ||
helperType: "scss" | ||
}); | ||
CodeMirror.defineMIME("text/x-less", { | ||
mediaTypes: mediaTypes, | ||
mediaFeatures: mediaFeatures, | ||
propertyKeywords: propertyKeywords, | ||
colorKeywords: colorKeywords, | ||
valueKeywords: valueKeywords, | ||
fontProperties: fontProperties, | ||
allowNested: true, | ||
tokenHooks: { | ||
"/": function(stream, state) { | ||
@@ -639,13 +680,16 @@ if (stream.eat("/")) { | ||
}, | ||
"#": function(stream) { | ||
if (stream.eat("{")) { | ||
return ["operator", "interpolation"]; | ||
} else { | ||
stream.eatWhile(/[\w\\\-]/); | ||
return ["atom", "hash"]; | ||
} | ||
"@": function(stream) { | ||
if (stream.match(/^(charset|document|font-face|import|keyframes|media|namespace|page|supports)\b/, false)) return false; | ||
stream.eatWhile(/[\w\\\-]/); | ||
if (stream.match(/^\s*:/, false)) | ||
return ["variable-2", "variable-definition"]; | ||
return ["variable-2", "variable"]; | ||
}, | ||
"&": function() { | ||
return ["atom", "atom"]; | ||
} | ||
}, | ||
name: "css" | ||
name: "css", | ||
helperType: "less" | ||
}); | ||
})(); |
(function() { | ||
var mode = CodeMirror.getMode({tabSize: 1}, "text/x-scss"); | ||
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss"); | ||
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } | ||
function IT(name) { test.indentation(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); } | ||
MT('url_with_quotation', | ||
"[tag foo] { [property background][operator :][string-2 url]([string test.jpg]) }"); | ||
"[tag foo] { [property background]:[atom url]([string test.jpg]) }"); | ||
MT('url_with_double_quotes', | ||
"[tag foo] { [property background][operator :][string-2 url]([string \"test.jpg\"]) }"); | ||
"[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }"); | ||
MT('url_with_single_quotes', | ||
"[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) }"); | ||
"[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }"); | ||
@@ -19,15 +18,15 @@ MT('string', | ||
MT('important_keyword', | ||
"[tag foo] { [property background][operator :][string-2 url]([string \'test.jpg\']) [keyword !important] }"); | ||
"[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }"); | ||
MT('variable', | ||
"[variable-2 $blue][operator :][atom #333]"); | ||
"[variable-2 $blue]:[atom #333]"); | ||
MT('variable_as_attribute', | ||
"[tag foo] { [property color][operator :][variable-2 $blue] }"); | ||
"[tag foo] { [property color]:[variable-2 $blue] }"); | ||
MT('numbers', | ||
"[tag foo] { [property padding][operator :][number 10px] [number 10] [number 10em] [number 8in] }"); | ||
"[tag foo] { [property padding]:[number 10px] [number 10] [number 10em] [number 8in] }"); | ||
MT('number_percentage', | ||
"[tag foo] { [property width][operator :][number 80%] }"); | ||
"[tag foo] { [property width]:[number 80%] }"); | ||
@@ -44,6 +43,6 @@ MT('selector', | ||
MT('attribute_with_hyphen', | ||
"[tag foo] { [property font-size][operator :][number 10px] }"); | ||
"[tag foo] { [property font-size]:[number 10px] }"); | ||
MT('string_after_attribute', | ||
"[tag foo] { [property content][operator :][string \"::\"] }"); | ||
"[tag foo] { [property content]:[string \"::\"] }"); | ||
@@ -54,6 +53,6 @@ MT('directives', | ||
MT('basic_structure', | ||
"[tag p] { [property background][operator :][keyword red]; }"); | ||
"[tag p] { [property background]:[keyword red]; }"); | ||
MT('nested_structure', | ||
"[tag p] { [tag a] { [property color][operator :][keyword red]; } }"); | ||
"[tag p] { [tag a] { [property color]:[keyword red]; } }"); | ||
@@ -64,34 +63,49 @@ MT('mixin', | ||
MT('number_without_semicolon', | ||
"[tag p] {[property width][operator :][number 12]}", | ||
"[tag a] {[property color][operator :][keyword red];}"); | ||
"[tag p] {[property width]:[number 12]}", | ||
"[tag a] {[property color]:[keyword red];}"); | ||
MT('atom_in_nested_block', | ||
"[tag p] { [tag a] { [property color][operator :][atom #000]; } }"); | ||
"[tag p] { [tag a] { [property color]:[atom #000]; } }"); | ||
MT('interpolation_in_property', | ||
"[tag foo] { [operator #{][variable-2 $hello][operator }:][number 2]; }"); | ||
"[tag foo] { #{[variable-2 $hello]}:[number 2]; }"); | ||
MT('interpolation_in_selector', | ||
"[tag foo][operator #{][variable-2 $hello][operator }] { [property color][operator :][atom #000]; }"); | ||
"[tag foo]#{[variable-2 $hello]} { [property color]:[atom #000]; }"); | ||
MT('interpolation_error', | ||
"[tag foo][operator #{][error foo][operator }] { [property color][operator :][atom #000]; }"); | ||
"[tag foo]#{[error foo]} { [property color]:[atom #000]; }"); | ||
MT("divide_operator", | ||
"[tag foo] { [property width][operator :][number 4] [operator /] [number 2] }"); | ||
"[tag foo] { [property width]:[number 4] [operator /] [number 2] }"); | ||
MT('nested_structure_with_id_selector', | ||
"[tag p] { [builtin #hello] { [property color][operator :][keyword red]; } }"); | ||
"[tag p] { [builtin #hello] { [property color]:[keyword red]; } }"); | ||
IT('mixin', | ||
"@mixin container[1 (][2 $a: 10][1 , ][2 $b: 10][1 , ][2 $c: 10]) [1 {]}"); | ||
MT('indent_mixin', | ||
"[def @mixin] [tag container] (", | ||
" [variable-2 $a]: [number 10],", | ||
" [variable-2 $b]: [number 10])", | ||
"{}"); | ||
IT('nested', | ||
"foo [1 { bar ][2 { ][1 } ]}"); | ||
MT('indent_nested', | ||
"[tag foo] {", | ||
" [tag bar] {", | ||
" }", | ||
"}"); | ||
IT('comma', | ||
"foo [1 { font-family][2 : verdana, sans-serif][1 ; ]}"); | ||
MT('indent_parentheses', | ||
"[tag foo] {", | ||
" [property color]: [variable darken]([variable-2 $blue],", | ||
" [number 9%]);", | ||
"}"); | ||
IT('parentheses', | ||
"foo [1 { color][2 : darken][3 ($blue, 9%][2 )][1 ; ]}"); | ||
MT('indent_vardef', | ||
"[variable-2 $name]:", | ||
" [string 'val'];", | ||
"[tag tag] {", | ||
" [tag inner] {", | ||
" [property margin]: [number 3px];", | ||
" }", | ||
"}"); | ||
})(); |
(function() { | ||
var mode = CodeMirror.getMode({tabSize: 1}, "css"); | ||
var mode = CodeMirror.getMode({indentUnit: 2}, "css"); | ||
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } | ||
function IT(name) { test.indentation(name, mode, Array.prototype.slice.call(arguments, 1)); } | ||
// Requires at least one media query | ||
MT("atMediaEmpty", | ||
"[def @media] [error {] }"); | ||
MT("atMediaMultiple", | ||
"[def @media] [keyword not] [attribute screen] [operator and] ([property color]), [keyword not] [attribute print] [operator and] ([property color]) { }"); | ||
MT("atMediaCheckStack", | ||
"[def @media] [attribute screen] { } [tag foo] { }"); | ||
MT("atMediaCheckStack", | ||
"[def @media] [attribute screen] ([property color]) { } [tag foo] { }"); | ||
MT("atMediaPropertyOnly", | ||
"[def @media] ([property color]) { } [tag foo] { }"); | ||
MT("atMediaCheckStackInvalidAttribute", | ||
"[def @media] [attribute&error foobarhello] { [tag foo] { } }"); | ||
MT("atMediaCheckStackInvalidAttribute", | ||
"[def @media] [attribute&error foobarhello] { } [tag foo] { }"); | ||
// Error, because "and" is only allowed immediately preceding a media expression | ||
MT("atMediaInvalidAttribute", | ||
"[def @media] [attribute&error foobarhello] { }"); | ||
// Error, because "and" is only allowed immediately preceding a media expression | ||
MT("atMediaInvalidAnd", | ||
"[def @media] [error and] [attribute screen] { }"); | ||
// Error, because "not" is only allowed as the first item in each media query | ||
MT("atMediaInvalidNot", | ||
"[def @media] [attribute screen] [error not] ([error not]) { }"); | ||
// Error, because "only" is only allowed as the first item in each media query | ||
MT("atMediaInvalidOnly", | ||
"[def @media] [attribute screen] [error only] ([error only]) { }"); | ||
// Error, because "foobarhello" is neither a known type or property, but | ||
// property was expected (after "and"), and it should be in parenthese. | ||
MT("atMediaUnknownType", | ||
"[def @media] [attribute screen] [operator and] [error foobarhello] { }"); | ||
"[def @media] [attribute screen] [keyword and] [error foobarhello] { }"); | ||
// Error, because "color" is not a known type, but is a known property, and | ||
// should be in parentheses. | ||
MT("atMediaInvalidType", | ||
"[def @media] [attribute screen] [operator and] [error color] { }"); | ||
// Error, because "print" is not a known property, but is a known type, | ||
// and should not be in parenthese. | ||
MT("atMediaInvalidProperty", | ||
"[def @media] [attribute screen] [operator and] ([error print]) { }"); | ||
// Soft error, because "foobarhello" is not a known property or type. | ||
MT("atMediaUnknownProperty", | ||
"[def @media] [attribute screen] [operator and] ([property&error foobarhello]) { }"); | ||
"[def @media] [attribute screen] [keyword and] ([error foobarhello]) { }"); | ||
// Make sure nesting works with media queries | ||
MT("atMediaMaxWidthNested", | ||
"[def @media] [attribute screen] [operator and] ([property max-width][operator :] [number 25px]) { [tag foo] { } }"); | ||
"[def @media] [attribute screen] [keyword and] ([property max-width]: [number 25px]) { [tag foo] { } }"); | ||
@@ -77,49 +28,49 @@ MT("tagSelector", | ||
MT("tagSelectorUnclosed", | ||
"[tag foo] { [property margin][operator :] [number 0] } [tag bar] { }"); | ||
"[tag foo] { [property margin]: [number 0] } [tag bar] { }"); | ||
MT("tagStringNoQuotes", | ||
"[tag foo] { [property font-family][operator :] [variable-2 hello] [variable-2 world]; }"); | ||
"[tag foo] { [property font-family]: [variable hello] [variable world]; }"); | ||
MT("tagStringDouble", | ||
"[tag foo] { [property font-family][operator :] [string \"hello world\"]; }"); | ||
"[tag foo] { [property font-family]: [string \"hello world\"]; }"); | ||
MT("tagStringSingle", | ||
"[tag foo] { [property font-family][operator :] [string 'hello world']; }"); | ||
"[tag foo] { [property font-family]: [string 'hello world']; }"); | ||
MT("tagColorKeyword", | ||
"[tag foo] {" + | ||
"[property color][operator :] [keyword black];" + | ||
"[property color][operator :] [keyword navy];" + | ||
"[property color][operator :] [keyword yellow];" + | ||
"}"); | ||
"[tag foo] {", | ||
" [property color]: [keyword black];", | ||
" [property color]: [keyword navy];", | ||
" [property color]: [keyword yellow];", | ||
"}"); | ||
MT("tagColorHex3", | ||
"[tag foo] { [property background][operator :] [atom #fff]; }"); | ||
"[tag foo] { [property background]: [atom #fff]; }"); | ||
MT("tagColorHex6", | ||
"[tag foo] { [property background][operator :] [atom #ffffff]; }"); | ||
"[tag foo] { [property background]: [atom #ffffff]; }"); | ||
MT("tagColorHex4", | ||
"[tag foo] { [property background][operator :] [atom&error #ffff]; }"); | ||
"[tag foo] { [property background]: [atom&error #ffff]; }"); | ||
MT("tagColorHexInvalid", | ||
"[tag foo] { [property background][operator :] [atom&error #ffg]; }"); | ||
"[tag foo] { [property background]: [atom&error #ffg]; }"); | ||
MT("tagNegativeNumber", | ||
"[tag foo] { [property margin][operator :] [number -5px]; }"); | ||
"[tag foo] { [property margin]: [number -5px]; }"); | ||
MT("tagPositiveNumber", | ||
"[tag foo] { [property padding][operator :] [number 5px]; }"); | ||
"[tag foo] { [property padding]: [number 5px]; }"); | ||
MT("tagVendor", | ||
"[tag foo] { [meta -foo-][property box-sizing][operator :] [meta -foo-][string-2 border-box]; }"); | ||
"[tag foo] { [meta -foo-][property box-sizing]: [meta -foo-][atom border-box]; }"); | ||
MT("tagBogusProperty", | ||
"[tag foo] { [property&error barhelloworld][operator :] [number 0]; }"); | ||
"[tag foo] { [property&error barhelloworld]: [number 0]; }"); | ||
MT("tagTwoProperties", | ||
"[tag foo] { [property margin][operator :] [number 0]; [property padding][operator :] [number 0]; }"); | ||
"[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }"); | ||
MT("tagTwoPropertiesURL", | ||
"[tag foo] { [property background][operator :] [string-2 url]([string //example.com/foo.png]); [property padding][operator :] [number 0]; }"); | ||
"[tag foo] { [property background]: [atom url]([string //example.com/foo.png]); [property padding]: [number 0]; }"); | ||
@@ -129,16 +80,42 @@ MT("commentSGML", | ||
IT("tagSelector", | ||
"strong, em [1 { background][2 : rgba][3 (255, 255, 0, .2][2 )][1 ;]}"); | ||
MT("commentSGML2", | ||
"[comment <!--comment]", | ||
"[comment -->] [tag div] {}"); | ||
IT("atMedia", | ||
"[1 @media { foo ][2 { ][1 } ]}"); | ||
MT("indent_tagSelector", | ||
"[tag strong], [tag em] {", | ||
" [property background]: [atom rgba](", | ||
" [number 255], [number 255], [number 0], [number .2]", | ||
" );", | ||
"}"); | ||
IT("comma", | ||
"foo [1 { font-family][2 : verdana, sans-serif][1 ; ]}"); | ||
MT("indent_atMedia", | ||
"[def @media] {", | ||
" [tag foo] {", | ||
" [property color]:", | ||
" [keyword yellow];", | ||
" }", | ||
"}"); | ||
IT("parentheses", | ||
"foo [1 { background][2 : url][3 (\"bar\"][2 )][1 ; ]}"); | ||
MT("indent_comma", | ||
"[tag foo] {", | ||
" [property font-family]: [variable verdana],", | ||
" [atom sans-serif];", | ||
"}"); | ||
IT("pseudo", | ||
"foo:before [1 { ]}"); | ||
MT("indent_parentheses", | ||
"[tag foo]:[variable-3 before] {", | ||
" [property background]: [atom url](", | ||
"[string blahblah]", | ||
"[string etc]", | ||
"[string ]) [keyword !important];", | ||
"}"); | ||
MT("font_face", | ||
"[def @font-face] {", | ||
" [property font-family]: [string 'myfont'];", | ||
" [error nonsense]: [string 'abc'];", | ||
" [property src]: [atom url]([string http://blah]),", | ||
" [atom url]([string http://foo]);", | ||
"}"); | ||
})(); |
@@ -1,44 +0,23 @@ | ||
// block; "begin", "case", "fun", "if", "receive", "try": closed by "end" | ||
// block internal; "after", "catch", "of" | ||
// guard; "when", closed by "->" | ||
// "->" opens a clause, closed by ";" or "." | ||
// "<<" opens a binary, closed by ">>" | ||
// "," appears in arglists, lists, tuples and terminates lines of code | ||
// "." resets indentation to 0 | ||
// obsolete; "cond", "let", "query" | ||
/*jshint unused:true, eqnull:true, curly:true, bitwise:true */ | ||
/*jshint undef:true, latedef:true, trailing:true */ | ||
/*global CodeMirror:true */ | ||
// erlang mode. | ||
// tokenizer -> token types -> CodeMirror styles | ||
// tokenizer maintains a parse stack | ||
// indenter uses the parse stack | ||
// TODO indenter: | ||
// bit syntax | ||
// old guard/bif/conversion clashes (e.g. "float/1") | ||
// type/spec/opaque | ||
CodeMirror.defineMIME("text/x-erlang", "erlang"); | ||
CodeMirror.defineMode("erlang", function(cmCfg) { | ||
"use strict"; | ||
function rval(state,_stream,type) { | ||
// distinguish between "." as terminator and record field operator | ||
state.in_record = (type == "record"); | ||
///////////////////////////////////////////////////////////////////////////// | ||
// constants | ||
// erlang -> CodeMirror tag | ||
switch (type) { | ||
case "atom": return "atom"; | ||
case "attribute": return "attribute"; | ||
case "boolean": return "special"; | ||
case "builtin": return "builtin"; | ||
case "comment": return "comment"; | ||
case "fun": return "meta"; | ||
case "function": return "tag"; | ||
case "guard": return "property"; | ||
case "keyword": return "keyword"; | ||
case "macro": return "variable-2"; | ||
case "number": return "number"; | ||
case "operator": return "operator"; | ||
case "record": return "bracket"; | ||
case "string": return "string"; | ||
case "type": return "def"; | ||
case "variable": return "variable"; | ||
case "error": return "error"; | ||
case "separator": return null; | ||
case "open_paren": return null; | ||
case "close_paren": return null; | ||
default: return null; | ||
} | ||
} | ||
var typeWords = [ | ||
@@ -51,19 +30,19 @@ "-type", "-spec", "-export_type", "-opaque"]; | ||
var separatorRE = /[\->\.,:;]/; | ||
var separatorRE = /[\->,;]/; | ||
var separatorWords = [ | ||
"->",";",":",".",","]; | ||
"->",";",","]; | ||
var operatorWords = [ | ||
var operatorAtomWords = [ | ||
"and","andalso","band","bnot","bor","bsl","bsr","bxor", | ||
"div","not","or","orelse","rem","xor"]; | ||
var symbolRE = /[\+\-\*\/<>=\|:!]/; | ||
var symbolWords = [ | ||
"+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"]; | ||
var operatorSymbolRE = /[\+\-\*\/<>=\|:!]/; | ||
var operatorSymbolWords = [ | ||
"=","+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-","!"]; | ||
var openParenRE = /[<\(\[\{]/; | ||
var openParenRE = /[<\(\[\{]/; | ||
var openParenWords = [ | ||
"<<","(","[","{"]; | ||
var closeParenRE = /[>\)\]\}]/; | ||
var closeParenRE = /[>\)\]\}]/; | ||
var closeParenWords = [ | ||
@@ -103,4 +82,4 @@ "}","]",")",">>"]; | ||
// [Ø-Þ] [À-Ö] | ||
// [ß-ö] [ø-ÿ] | ||
// upper case: [A-Z] [Ø-Þ] [À-Ö] | ||
// lower case: [a-z] [ß-ö] [ø-ÿ] | ||
var anumRE = /[\w@Ø-ÞÀ-Öß-öø-ÿ]/; | ||
@@ -110,7 +89,9 @@ var escapesRE = | ||
function tokenize(stream, state) { | ||
///////////////////////////////////////////////////////////////////////////// | ||
// tokenizer | ||
function tokenizer(stream,state) { | ||
// in multi-line string | ||
if (state.in_string) { | ||
state.in_string = (!doubleQuote(stream)); | ||
state.in_string = (!doubleQuote(stream)); | ||
return rval(state,stream,"string"); | ||
@@ -121,3 +102,3 @@ } | ||
if (state.in_atom) { | ||
state.in_atom = (!singleQuote(stream)); | ||
state.in_atom = (!singleQuote(stream)); | ||
return rval(state,stream,"atom"); | ||
@@ -132,5 +113,5 @@ } | ||
// attributes and type specs | ||
if ((peekToken(state).token == "") && | ||
if (!peekToken(state) && | ||
stream.match(/-\s*[a-zß-öø-ÿ][\wØ-ÞÀ-Öß-öø-ÿ]*/)) { | ||
if (isMember(stream.current(),typeWords)) { | ||
if (is_member(stream.current(),typeWords)) { | ||
return rval(state,stream,"type"); | ||
@@ -150,4 +131,10 @@ }else{ | ||
// colon | ||
if (ch == ":") { | ||
return rval(state,stream,"colon"); | ||
} | ||
// macro | ||
if (ch == '?') { | ||
stream.eatSpace(); | ||
stream.eatWhile(anumRE); | ||
@@ -159,2 +146,3 @@ return rval(state,stream,"macro"); | ||
if (ch == "#") { | ||
stream.eatSpace(); | ||
stream.eatWhile(anumRE); | ||
@@ -165,3 +153,3 @@ return rval(state,stream,"record"); | ||
// dollar escape | ||
if ( ch == "$" ) { | ||
if (ch == "$") { | ||
if (stream.next() == "\\" && !stream.match(escapesRE)) { | ||
@@ -173,2 +161,7 @@ return rval(state,stream,"error"); | ||
// dot | ||
if (ch == ".") { | ||
return rval(state,stream,"dot"); | ||
} | ||
// quoted atom | ||
@@ -179,3 +172,2 @@ if (ch == '\'') { | ||
stream.match(/\s*\/\s*[0-9]/,true); | ||
popToken(state); | ||
return rval(state,stream,"fun"); // 'f'/0 style fun | ||
@@ -208,3 +200,2 @@ } | ||
stream.match(/\s*\/\s*[0-9]/,true); | ||
popToken(state); | ||
return rval(state,stream,"fun"); // f/0 style fun | ||
@@ -215,11 +206,13 @@ } | ||
if (isMember(w,keywordWords)) { | ||
pushToken(state,stream); | ||
if (is_member(w,keywordWords)) { | ||
return rval(state,stream,"keyword"); | ||
}else if (is_member(w,operatorAtomWords)) { | ||
return rval(state,stream,"operator"); | ||
}else if (stream.match(/\s*\(/,false)) { | ||
// 'put' and 'erlang:put' are bifs, 'foo:put' is not | ||
if (isMember(w,bifWords) && | ||
(!isPrev(stream,":") || isPrev(stream,"erlang:"))) { | ||
if (is_member(w,bifWords) && | ||
((peekToken(state).token != ":") || | ||
(peekToken(state,2).token == "erlang"))) { | ||
return rval(state,stream,"builtin"); | ||
}else if (isMember(w,guardWords)) { | ||
}else if (is_member(w,guardWords)) { | ||
return rval(state,stream,"guard"); | ||
@@ -229,5 +222,5 @@ }else{ | ||
} | ||
}else if (isMember(w,operatorWords)) { | ||
}else if (is_member(w,operatorAtomWords)) { | ||
return rval(state,stream,"operator"); | ||
}else if (stream.match(/\s*:/,false)) { | ||
}else if (lookahead(stream) == ":") { | ||
if (w == "erlang") { | ||
@@ -238,4 +231,6 @@ return rval(state,stream,"builtin"); | ||
} | ||
}else if (isMember(w,["true","false"])) { | ||
}else if (is_member(w,["true","false"])) { | ||
return rval(state,stream,"boolean"); | ||
}else if (is_member(w,["true","false"])) { | ||
return rval(state,stream,"boolean"); | ||
}else{ | ||
@@ -251,11 +246,21 @@ return rval(state,stream,"atom"); | ||
stream.eatWhile(digitRE); | ||
if (stream.eat('#')) { | ||
stream.eatWhile(radixRE); // 36#aZ style integer | ||
} else { | ||
if (stream.eat('.')) { // float | ||
stream.eatWhile(digitRE); | ||
if (stream.eat('#')) { // 36#aZ style integer | ||
if (!stream.eatWhile(radixRE)) { | ||
stream.backUp(1); //"36#" - syntax error | ||
} | ||
if (stream.eat(/[eE]/)) { | ||
stream.eat(/[-+]/); // float with exponent | ||
stream.eatWhile(digitRE); | ||
} else if (stream.eat('.')) { // float | ||
if (!stream.eatWhile(digitRE)) { | ||
stream.backUp(1); // "3." - probably end of function | ||
} else { | ||
if (stream.eat(/[eE]/)) { // float with exponent | ||
if (stream.eat(/[-+]/)) { | ||
if (!stream.eatWhile(digitRE)) { | ||
stream.backUp(2); // "2e-" - syntax error | ||
} | ||
} else { | ||
if (!stream.eatWhile(digitRE)) { | ||
stream.backUp(1); // "2e" - syntax error | ||
} | ||
} | ||
} | ||
} | ||
@@ -268,3 +273,2 @@ } | ||
if (nongreedy(stream,openParenRE,openParenWords)) { | ||
pushToken(state,stream); | ||
return rval(state,stream,"open_paren"); | ||
@@ -275,3 +279,2 @@ } | ||
if (nongreedy(stream,closeParenRE,closeParenWords)) { | ||
pushToken(state,stream); | ||
return rval(state,stream,"close_paren"); | ||
@@ -282,6 +285,2 @@ } | ||
if (greedy(stream,separatorRE,separatorWords)) { | ||
// distinguish between "." as terminator and record field operator | ||
if (!state.in_record) { | ||
pushToken(state,stream); | ||
} | ||
return rval(state,stream,"separator"); | ||
@@ -291,3 +290,3 @@ } | ||
// operators | ||
if (greedy(stream,symbolRE,symbolWords)) { | ||
if (greedy(stream,operatorSymbolRE,operatorSymbolWords)) { | ||
return rval(state,stream,"operator"); | ||
@@ -299,13 +298,4 @@ } | ||
function isPrev(stream,string) { | ||
var start = stream.start; | ||
var len = string.length; | ||
if (len <= start) { | ||
var word = stream.string.slice(start-len,start); | ||
return word == string; | ||
}else{ | ||
return false; | ||
} | ||
} | ||
///////////////////////////////////////////////////////////////////////////// | ||
// utilities | ||
function nongreedy(stream,re,words) { | ||
@@ -316,3 +306,3 @@ if (stream.current().length == 1 && re.test(stream.current())) { | ||
stream.next(); | ||
if (isMember(stream.current(),words)) { | ||
if (is_member(stream.current(),words)) { | ||
return true; | ||
@@ -332,3 +322,3 @@ } | ||
while (0 < stream.current().length) { | ||
if (isMember(stream.current(),words)) { | ||
if (is_member(stream.current(),words)) { | ||
return true; | ||
@@ -364,52 +354,61 @@ }else{ | ||
function isMember(element,list) { | ||
function lookahead(stream) { | ||
var m = stream.match(/([\n\s]+|%[^\n]*\n)*(.)/,false); | ||
return m ? m.pop() : ""; | ||
} | ||
function is_member(element,list) { | ||
return (-1 < list.indexOf(element)); | ||
} | ||
///////////////////////////////////////////////////////////////////////////// | ||
function myIndent(state,textAfter) { | ||
var indent = cmCfg.indentUnit; | ||
var token = (peekToken(state)).token; | ||
var wordAfter = takewhile(textAfter,/[^a-z]/); | ||
function rval(state,stream,type) { | ||
if (state.in_string || state.in_atom) { | ||
return CodeMirror.Pass; | ||
}else if (token == "") { | ||
return 0; | ||
}else if (isMember(token,openParenWords)) { | ||
return (peekToken(state)).column+token.length; | ||
}else if (token == "when") { | ||
return (peekToken(state)).column+token.length+1; | ||
}else if (token == "fun" && wordAfter == "") { | ||
return (peekToken(state)).column+token.length; | ||
}else if (token == "->") { | ||
if (isMember(wordAfter,["end","after","catch"])) { | ||
return peekToken(state,2).column; | ||
}else if (peekToken(state,2).token == "fun") { | ||
return peekToken(state,2).column+indent; | ||
}else if (peekToken(state,2).token == "") { | ||
return indent; | ||
}else{ | ||
return (peekToken(state)).indent+indent; | ||
} | ||
}else if (isMember(wordAfter,["after","catch","of"])) { | ||
return (peekToken(state)).indent; | ||
}else{ | ||
return (peekToken(state)).column+indent; | ||
// parse stack | ||
pushToken(state,realToken(type,stream)); | ||
// map erlang token type to CodeMirror style class | ||
// erlang -> CodeMirror tag | ||
switch (type) { | ||
case "atom": return "atom"; | ||
case "attribute": return "attribute"; | ||
case "boolean": return "special"; | ||
case "builtin": return "builtin"; | ||
case "close_paren": return null; | ||
case "colon": return null; | ||
case "comment": return "comment"; | ||
case "dot": return null; | ||
case "error": return "error"; | ||
case "fun": return "meta"; | ||
case "function": return "tag"; | ||
case "guard": return "property"; | ||
case "keyword": return "keyword"; | ||
case "macro": return "variable-2"; | ||
case "number": return "number"; | ||
case "open_paren": return null; | ||
case "operator": return "operator"; | ||
case "record": return "bracket"; | ||
case "separator": return null; | ||
case "string": return "string"; | ||
case "type": return "def"; | ||
case "variable": return "variable"; | ||
default: return null; | ||
} | ||
} | ||
function takewhile(str,re) { | ||
var m = str.match(re); | ||
return m ? str.slice(0,m.index) : str; | ||
function aToken(tok,col,ind,typ) { | ||
return {token: tok, | ||
column: col, | ||
indent: ind, | ||
type: typ}; | ||
} | ||
function Token(stream) { | ||
this.token = stream ? stream.current() : ""; | ||
this.column = stream ? stream.column() : 0; | ||
this.indent = stream ? stream.indentation() : 0; | ||
function realToken(type,stream) { | ||
return aToken(stream.current(), | ||
stream.column(), | ||
stream.indentation(), | ||
type); | ||
} | ||
function popToken(state) { | ||
return state.tokenStack.pop(); | ||
function fakeToken(type) { | ||
return aToken(type,0,0,type); | ||
} | ||
@@ -420,4 +419,5 @@ | ||
var dep = (depth ? depth : 1); | ||
if (len < dep) { | ||
return new Token; | ||
return false; | ||
}else{ | ||
@@ -428,61 +428,185 @@ return state.tokenStack[len-dep]; | ||
function pushToken(state,stream) { | ||
var token = stream.current(); | ||
var prev_token = peekToken(state).token; | ||
function pushToken(state,token) { | ||
if (token == ".") { | ||
state.tokenStack = []; | ||
return false; | ||
}else if(isMember(token,[",", ":", "of", "cond", "let", "query"])) { | ||
return false; | ||
}else if (drop_last(prev_token,token)) { | ||
return false; | ||
}else if (drop_both(prev_token,token)) { | ||
popToken(state); | ||
return false; | ||
}else if (drop_first(prev_token,token)) { | ||
popToken(state); | ||
return pushToken(state,stream); | ||
}else if (isMember(token,["after","catch"])) { | ||
return false; | ||
if (!(token.type == "comment" || token.type == "whitespace")) { | ||
state.tokenStack = maybe_drop_pre(state.tokenStack,token); | ||
state.tokenStack = maybe_drop_post(state.tokenStack); | ||
} | ||
} | ||
function maybe_drop_pre(s,token) { | ||
var last = s.length-1; | ||
if (0 < last && s[last].type === "record" && token.type === "dot") { | ||
s.pop(); | ||
}else if (0 < last && s[last].type === "group") { | ||
s.pop(); | ||
s.push(token); | ||
}else{ | ||
state.tokenStack.push(new Token(stream)); | ||
return true; | ||
s.push(token); | ||
} | ||
return s; | ||
} | ||
function drop_last(open, close) { | ||
switch(open+" "+close) { | ||
case "when ;": return true; | ||
default: return false; | ||
function maybe_drop_post(s) { | ||
var last = s.length-1; | ||
if (s[last].type === "dot") { | ||
return []; | ||
} | ||
if (s[last].type === "fun" && s[last-1].token === "fun") { | ||
return s.slice(0,last-1); | ||
} | ||
switch (s[s.length-1].token) { | ||
case "}": return d(s,{g:["{"]}); | ||
case "]": return d(s,{i:["["]}); | ||
case ")": return d(s,{i:["("]}); | ||
case ">>": return d(s,{i:["<<"]}); | ||
case "end": return d(s,{i:["begin","case","fun","if","receive","try"]}); | ||
case ",": return d(s,{e:["begin","try","when","->", | ||
",","(","[","{","<<"]}); | ||
case "->": return d(s,{r:["when"], | ||
m:["try","if","case","receive"]}); | ||
case ";": return d(s,{E:["case","fun","if","receive","try","when"]}); | ||
case "catch":return d(s,{e:["try"]}); | ||
case "of": return d(s,{e:["case"]}); | ||
case "after":return d(s,{e:["receive","try"]}); | ||
default: return s; | ||
} | ||
} | ||
function drop_first(open, close) { | ||
switch (open+" "+close) { | ||
case "when ->": return true; | ||
case "-> end": return true; | ||
default: return false; | ||
function d(stack,tt) { | ||
// stack is a stack of Token objects. | ||
// tt is an object; {type:tokens} | ||
// type is a char, tokens is a list of token strings. | ||
// The function returns (possibly truncated) stack. | ||
// It will descend the stack, looking for a Token such that Token.token | ||
// is a member of tokens. If it does not find that, it will normally (but | ||
// see "E" below) return stack. If it does find a match, it will remove | ||
// all the Tokens between the top and the matched Token. | ||
// If type is "m", that is all it does. | ||
// If type is "i", it will also remove the matched Token and the top Token. | ||
// If type is "g", like "i", but add a fake "group" token at the top. | ||
// If type is "r", it will remove the matched Token, but not the top Token. | ||
// If type is "e", it will keep the matched Token but not the top Token. | ||
// If type is "E", it behaves as for type "e", except if there is no match, | ||
// in which case it will return an empty stack. | ||
for (var type in tt) { | ||
var len = stack.length-1; | ||
var tokens = tt[type]; | ||
for (var i = len-1; -1 < i ; i--) { | ||
if (is_member(stack[i].token,tokens)) { | ||
var ss = stack.slice(0,i); | ||
switch (type) { | ||
case "m": return ss.concat(stack[i]).concat(stack[len]); | ||
case "r": return ss.concat(stack[len]); | ||
case "i": return ss; | ||
case "g": return ss.concat(fakeToken("group")); | ||
case "E": return ss.concat(stack[i]); | ||
case "e": return ss.concat(stack[i]); | ||
} | ||
} | ||
} | ||
} | ||
return (type == "E" ? [] : stack); | ||
} | ||
function drop_both(open, close) { | ||
switch (open+" "+close) { | ||
case "( )": return true; | ||
case "[ ]": return true; | ||
case "{ }": return true; | ||
case "<< >>": return true; | ||
case "begin end": return true; | ||
case "case end": return true; | ||
case "fun end": return true; | ||
case "if end": return true; | ||
case "receive end": return true; | ||
case "try end": return true; | ||
case "-> catch": return true; | ||
case "-> after": return true; | ||
case "-> ;": return true; | ||
default: return false; | ||
///////////////////////////////////////////////////////////////////////////// | ||
// indenter | ||
function indenter(state,textAfter) { | ||
var t; | ||
var unit = cmCfg.indentUnit; | ||
var wordAfter = wordafter(textAfter); | ||
var currT = peekToken(state,1); | ||
var prevT = peekToken(state,2); | ||
if (state.in_string || state.in_atom) { | ||
return CodeMirror.Pass; | ||
}else if (!prevT) { | ||
return 0; | ||
}else if (currT.token == "when") { | ||
return currT.column+unit; | ||
}else if (wordAfter === "when" && prevT.type === "function") { | ||
return prevT.indent+unit; | ||
}else if (wordAfter === "(" && currT.token === "fun") { | ||
return currT.column+3; | ||
}else if (wordAfter === "catch" && (t = getToken(state,["try"]))) { | ||
return t.column; | ||
}else if (is_member(wordAfter,["end","after","of"])) { | ||
t = getToken(state,["begin","case","fun","if","receive","try"]); | ||
return t ? t.column : CodeMirror.Pass; | ||
}else if (is_member(wordAfter,closeParenWords)) { | ||
t = getToken(state,openParenWords); | ||
return t ? t.column : CodeMirror.Pass; | ||
}else if (is_member(currT.token,[",","|","||"]) || | ||
is_member(wordAfter,[",","|","||"])) { | ||
t = postcommaToken(state); | ||
return t ? t.column+t.token.length : unit; | ||
}else if (currT.token == "->") { | ||
if (is_member(prevT.token, ["receive","case","if","try"])) { | ||
return prevT.column+unit+unit; | ||
}else{ | ||
return prevT.column+unit; | ||
} | ||
}else if (is_member(currT.token,openParenWords)) { | ||
return currT.column+currT.token.length; | ||
}else{ | ||
t = defaultToken(state); | ||
return truthy(t) ? t.column+unit : 0; | ||
} | ||
} | ||
function wordafter(str) { | ||
var m = str.match(/,|[a-z]+|\}|\]|\)|>>|\|+|\(/); | ||
return truthy(m) && (m.index === 0) ? m[0] : ""; | ||
} | ||
function postcommaToken(state) { | ||
var objs = state.tokenStack.slice(0,-1); | ||
var i = getTokenIndex(objs,"type",["open_paren"]); | ||
return truthy(objs[i]) ? objs[i] : false; | ||
} | ||
function defaultToken(state) { | ||
var objs = state.tokenStack; | ||
var stop = getTokenIndex(objs,"type",["open_paren","separator","keyword"]); | ||
var oper = getTokenIndex(objs,"type",["operator"]); | ||
if (truthy(stop) && truthy(oper) && stop < oper) { | ||
return objs[stop+1]; | ||
} else if (truthy(stop)) { | ||
return objs[stop]; | ||
} else { | ||
return false; | ||
} | ||
} | ||
function getToken(state,tokens) { | ||
var objs = state.tokenStack; | ||
var i = getTokenIndex(objs,"token",tokens); | ||
return truthy(objs[i]) ? objs[i] : false; | ||
} | ||
function getTokenIndex(objs,propname,propvals) { | ||
for (var i = objs.length-1; -1 < i ; i--) { | ||
if (is_member(objs[i][propname],propvals)) { | ||
return i; | ||
} | ||
} | ||
return false; | ||
} | ||
function truthy(x) { | ||
return (x !== false) && (x != null); | ||
} | ||
///////////////////////////////////////////////////////////////////////////// | ||
// this object defines the mode | ||
return { | ||
@@ -492,3 +616,2 @@ startState: | ||
return {tokenStack: [], | ||
in_record: false, | ||
in_string: false, | ||
@@ -500,3 +623,3 @@ in_atom: false}; | ||
function(stream, state) { | ||
return tokenize(stream, state); | ||
return tokenizer(stream, state); | ||
}, | ||
@@ -506,3 +629,3 @@ | ||
function(state, textAfter) { | ||
return myIndent(state,textAfter); | ||
return indenter(state,textAfter); | ||
}, | ||
@@ -509,0 +632,0 @@ |
@@ -1,2 +0,2 @@ | ||
CodeMirror.defineMode("gfm", function(config) { | ||
CodeMirror.defineMode("gfm", function(config, modeConfig) { | ||
var codeDepth = 0; | ||
@@ -78,3 +78,3 @@ function blankLine(state) { | ||
} | ||
if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i) && | ||
if (stream.match(/^((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]|\([^\s()<>]*\))+(?:\([^\s()<>]*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/i) && | ||
stream.string.slice(stream.start - 2, stream.start) != "](") { | ||
@@ -91,9 +91,14 @@ // URLs | ||
}; | ||
CodeMirror.defineMIME("gfmBase", { | ||
name: "markdown", | ||
var markdownConfig = { | ||
underscoresBreakWords: false, | ||
taskLists: true, | ||
fencedCodeBlocks: true | ||
}); | ||
}; | ||
for (var attr in modeConfig) { | ||
markdownConfig[attr] = modeConfig[attr]; | ||
} | ||
markdownConfig.name = "markdown"; | ||
CodeMirror.defineMIME("gfmBase", markdownConfig); | ||
return CodeMirror.overlayMode(CodeMirror.getMode(config, "gfmBase"), gfmOverlay); | ||
}, "markdown"); |
(function() { | ||
var mode = CodeMirror.getMode({tabSize: 4}, "gfm"); | ||
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } | ||
var modeHighlightFormatting = CodeMirror.getMode({tabSize: 4}, {name: "gfm", highlightFormatting: true}); | ||
function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); } | ||
FT("codeBackticks", | ||
"[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]"); | ||
FT("doubleBackticks", | ||
"[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]"); | ||
FT("codeBlock", | ||
"[comment&formatting&formatting-code-block ```css]", | ||
"[tag foo]", | ||
"[comment&formatting&formatting-code-block ```]"); | ||
FT("taskList", | ||
"[variable-2&formatting&formatting-list&formatting-list-ul - ][meta&formatting&formatting-task [ ]]][variable-2 foo]", | ||
"[variable-2&formatting&formatting-list&formatting-list-ul - ][property&formatting&formatting-task [x]]][variable-2 foo]"); | ||
MT("emInWordAsterisk", | ||
@@ -100,3 +117,3 @@ "foo[em *bar*]hello"); | ||
"[comment ```css]", | ||
"[tag foo] {[property color][operator :][keyword black];}", | ||
"[tag foo] {[property color]:[keyword black];}", | ||
"[comment ```][link http://www.example.com/]"); | ||
@@ -103,0 +120,0 @@ |
@@ -21,3 +21,3 @@ /* | ||
lineNumber: 0, | ||
tableHeaderLine: null, | ||
tableHeaderLine: false, | ||
allowFeature: true, | ||
@@ -28,5 +28,6 @@ allowBackground: false, | ||
allowPlaceholders: false, | ||
inMultilineArgument: false, | ||
allowMultilineArgument: false, | ||
inMultilineString: false, | ||
inMultilineTable: false | ||
inMultilineTable: false, | ||
inKeywordLine: false | ||
}; | ||
@@ -37,7 +38,15 @@ }, | ||
state.lineNumber++; | ||
state.inKeywordLine = false; | ||
if (state.inMultilineTable) { | ||
state.tableHeaderLine = false; | ||
if (!stream.match(/\s*\|/, false)) { | ||
state.allowMultilineArgument = false; | ||
state.inMultilineTable = false; | ||
} | ||
} | ||
} | ||
stream.eatSpace(); | ||
// INSIDE OF MULTILINE ARGUMENTS | ||
if (state.inMultilineArgument) { | ||
if (state.allowMultilineArgument) { | ||
@@ -48,3 +57,3 @@ // STRING | ||
state.inMultilineString = false; | ||
state.inMultilineArgument = false; | ||
state.allowMultilineArgument = false; | ||
} else { | ||
@@ -58,15 +67,7 @@ stream.match(/.*/); | ||
if (state.inMultilineTable) { | ||
// New table, assume first row is headers | ||
if (state.tableHeaderLine === null) { | ||
state.tableHeaderLine = state.lineNumber; | ||
} | ||
if (stream.match(/\|\s*/)) { | ||
if (stream.eol()) { | ||
state.inMultilineTable = false; | ||
} | ||
return "bracket"; | ||
} else { | ||
stream.match(/[^\|]*/); | ||
return state.tableHeaderLine === state.lineNumber ? "property" : "string"; | ||
return state.tableHeaderLine ? "header" : "string"; | ||
} | ||
@@ -83,11 +84,6 @@ } | ||
state.inMultilineTable = true; | ||
state.tableHeaderLine = true; | ||
return "bracket"; | ||
} else { | ||
// Or abort | ||
state.inMultilineArgument = false; | ||
state.tableHeaderLine = null; | ||
} | ||
return null; | ||
} | ||
@@ -100,7 +96,7 @@ | ||
// TAG | ||
} else if (stream.match(/@\S+/)) { | ||
return "def"; | ||
} else if (!state.inKeywordLine && stream.match(/@\S+/)) { | ||
return "tag"; | ||
// FEATURE | ||
} else if (state.allowFeature && stream.match(/Feature:/)) { | ||
} else if (!state.inKeywordLine && state.allowFeature && stream.match(/(機能|功能|フィーチャ|기능|โครงหลัก|ความสามารถ|ความต้องการทางธุรกิจ|ಹೆಚ್ಚಳ|గుణము|ਮੁਹਾਂਦਰਾ|ਨਕਸ਼ ਨੁਹਾਰ|ਖਾਸੀਅਤ|रूप लेख|وِیژگی|خاصية|תכונה|Функціонал|Функция|Функционалност|Функционал|Үзенчәлеклелек|Свойство|Особина|Мөмкинлек|Могућност|Λειτουργία|Δυνατότητα|Właściwość|Vlastnosť|Trajto|Tính năng|Savybė|Pretty much|Požiadavka|Požadavek|Potrzeba biznesowa|Özellik|Osobina|Ominaisuus|Omadus|OH HAI|Mogućnost|Mogucnost|Jellemző|Hwæt|Hwaet|Funzionalità|Funktionalitéit|Funktionalität|Funkcja|Funkcionalnost|Funkcionalitāte|Funkcia|Fungsi|Functionaliteit|Funcționalitate|Funcţionalitate|Functionalitate|Funcionalitat|Funcionalidade|Fonctionnalité|Fitur|Fīča|Feature|Eiginleiki|Egenskap|Egenskab|Característica|Caracteristica|Business Need|Aspekt|Arwedd|Ahoy matey!|Ability):/)) { | ||
state.allowScenario = true; | ||
@@ -110,63 +106,62 @@ state.allowBackground = true; | ||
state.allowSteps = false; | ||
state.allowMultilineArgument = false; | ||
state.inKeywordLine = true; | ||
return "keyword"; | ||
// BACKGROUND | ||
} else if (state.allowBackground && stream.match("Background:")) { | ||
} else if (!state.inKeywordLine && state.allowBackground && stream.match(/(背景|배경|แนวคิด|ಹಿನ್ನೆಲೆ|నేపథ్యం|ਪਿਛੋਕੜ|पृष्ठभूमि|زمینه|الخلفية|רקע|Тарих|Предыстория|Предистория|Позадина|Передумова|Основа|Контекст|Кереш|Υπόβαθρο|Założenia|Yo\-ho\-ho|Tausta|Taust|Situācija|Rerefons|Pozadina|Pozadie|Pozadí|Osnova|Latar Belakang|Kontext|Konteksts|Kontekstas|Kontekst|Háttér|Hannergrond|Grundlage|Geçmiş|Fundo|Fono|First off|Dis is what went down|Dasar|Contexto|Contexte|Context|Contesto|Cenário de Fundo|Cenario de Fundo|Cefndir|Bối cảnh|Bakgrunnur|Bakgrunn|Bakgrund|Baggrund|Background|B4|Antecedents|Antecedentes|Ær|Aer|Achtergrond):/)) { | ||
state.allowPlaceholders = false; | ||
state.allowSteps = true; | ||
state.allowBackground = false; | ||
state.allowMultilineArgument = false; | ||
state.inKeywordLine = true; | ||
return "keyword"; | ||
// SCENARIO OUTLINE | ||
} else if (state.allowScenario && stream.match("Scenario Outline:")) { | ||
} else if (!state.inKeywordLine && state.allowScenario && stream.match(/(場景大綱|场景大纲|劇本大綱|剧本大纲|テンプレ|シナリオテンプレート|シナリオテンプレ|シナリオアウトライン|시나리오 개요|สรุปเหตุการณ์|โครงสร้างของเหตุการณ์|ವಿವರಣೆ|కథనం|ਪਟਕਥਾ ਰੂਪ ਰੇਖਾ|ਪਟਕਥਾ ਢਾਂਚਾ|परिदृश्य रूपरेखा|سيناريو مخطط|الگوی سناریو|תבנית תרחיש|Сценарийның төзелеше|Сценарий структураси|Структура сценарію|Структура сценария|Структура сценарија|Скица|Рамка на сценарий|Концепт|Περιγραφή Σεναρίου|Wharrimean is|Template Situai|Template Senario|Template Keadaan|Tapausaihio|Szenariogrundriss|Szablon scenariusza|Swa hwær swa|Swa hwaer swa|Struktura scenarija|Structură scenariu|Structura scenariu|Skica|Skenario konsep|Shiver me timbers|Senaryo taslağı|Schema dello scenario|Scenariomall|Scenariomal|Scenario Template|Scenario Outline|Scenario Amlinellol|Scenārijs pēc parauga|Scenarijaus šablonas|Reckon it's like|Raamstsenaarium|Plang vum Szenario|Plan du Scénario|Plan du scénario|Osnova scénáře|Osnova Scenára|Náčrt Scenáru|Náčrt Scénáře|Náčrt Scenára|MISHUN SRSLY|Menggariskan Senario|Lýsing Dæma|Lýsing Atburðarásar|Konturo de la scenaro|Koncept|Khung tình huống|Khung kịch bản|Forgatókönyv vázlat|Esquema do Cenário|Esquema do Cenario|Esquema del escenario|Esquema de l'escenari|Esbozo do escenario|Delineação do Cenário|Delineacao do Cenario|All y'all|Abstrakt Scenario|Abstract Scenario):/)) { | ||
state.allowPlaceholders = true; | ||
state.allowSteps = true; | ||
state.allowMultilineArgument = false; | ||
state.inKeywordLine = true; | ||
return "keyword"; | ||
// EXAMPLES | ||
} else if (state.allowScenario && stream.match("Examples:")) { | ||
} else if (state.allowScenario && stream.match(/(例子|例|サンプル|예|ชุดของเหตุการณ์|ชุดของตัวอย่าง|ಉದಾಹರಣೆಗಳು|ఉదాహరణలు|ਉਦਾਹਰਨਾਂ|उदाहरण|نمونه ها|امثلة|דוגמאות|Үрнәкләр|Сценарији|Примеры|Примери|Приклади|Мисоллар|Мисаллар|Σενάρια|Παραδείγματα|You'll wanna|Voorbeelden|Variantai|Tapaukset|Se þe|Se the|Se ðe|Scenarios|Scenariji|Scenarijai|Przykłady|Primjeri|Primeri|Příklady|Príklady|Piemēri|Példák|Pavyzdžiai|Paraugs|Örnekler|Juhtumid|Exemplos|Exemples|Exemple|Exempel|EXAMPLZ|Examples|Esempi|Enghreifftiau|Ekzemploj|Eksempler|Ejemplos|Dữ liệu|Dead men tell no tales|Dæmi|Contoh|Cenários|Cenarios|Beispiller|Beispiele|Atburðarásir):/)) { | ||
state.allowPlaceholders = false; | ||
state.allowSteps = true; | ||
state.allowBackground = false; | ||
state.inMultilineArgument = true; | ||
state.allowMultilineArgument = true; | ||
return "keyword"; | ||
// SCENARIO | ||
} else if (state.allowScenario && stream.match(/Scenario:/)) { | ||
} else if (!state.inKeywordLine && state.allowScenario && stream.match(/(場景|场景|劇本|剧本|シナリオ|시나리오|เหตุการณ์|ಕಥಾಸಾರಾಂಶ|సన్నివేశం|ਪਟਕਥਾ|परिदृश्य|سيناريو|سناریو|תרחיש|Сценарій|Сценарио|Сценарий|Пример|Σενάριο|Tình huống|The thing of it is|Tapaus|Szenario|Swa|Stsenaarium|Skenario|Situai|Senaryo|Senario|Scenaro|Scenariusz|Scenariu|Scénario|Scenario|Scenarijus|Scenārijs|Scenarij|Scenarie|Scénář|Scenár|Primer|MISHUN|Kịch bản|Keadaan|Heave to|Forgatókönyv|Escenario|Escenari|Cenário|Cenario|Awww, look mate|Atburðarás):/)) { | ||
state.allowPlaceholders = false; | ||
state.allowSteps = true; | ||
state.allowBackground = false; | ||
state.allowMultilineArgument = false; | ||
state.inKeywordLine = true; | ||
return "keyword"; | ||
// STEPS | ||
} else if (state.allowSteps && stream.match(/(Given|When|Then|And|But)/)) { | ||
} else if (!state.inKeywordLine && state.allowSteps && stream.match(/(那麼|那么|而且|當|当|并且|同時|同时|前提|假设|假設|假定|假如|但是|但し|並且|もし|ならば|ただし|しかし|かつ|하지만|조건|먼저|만일|만약|단|그리고|그러면|และ |เมื่อ |แต่ |ดังนั้น |กำหนดให้ |ಸ್ಥಿತಿಯನ್ನು |ಮತ್ತು |ನೀಡಿದ |ನಂತರ |ಆದರೆ |మరియు |చెప్పబడినది |కాని |ఈ పరిస్థితిలో |అప్పుడు |ਪਰ |ਤਦ |ਜੇਕਰ |ਜਿਵੇਂ ਕਿ |ਜਦੋਂ |ਅਤੇ |यदि |परन्तु |पर |तब |तदा |तथा |जब |चूंकि |किन्तु |कदा |और |अगर |و |هنگامی |متى |لكن |عندما |ثم |بفرض |با فرض |اما |اذاً |آنگاه |כאשר |וגם |בהינתן |אזי |אז |אבל |Якщо |Һәм |Унда |Тоді |Тогда |То |Также |Та |Пусть |Припустимо, що |Припустимо |Онда |Но |Нехай |Нәтиҗәдә |Лекин |Ләкин |Коли |Когда |Когато |Када |Кад |К тому же |І |И |Задато |Задати |Задате |Если |Допустим |Дано |Дадено |Вә |Ва |Бирок |Әмма |Әйтик |Әгәр |Аммо |Али |Але |Агар |А також |А |Τότε |Όταν |Και |Δεδομένου |Αλλά |Þurh |Þegar |Þa þe |Þá |Þa |Zatati |Zakładając |Zadato |Zadate |Zadano |Zadani |Zadan |Za předpokladu |Za predpokladu |Youse know when youse got |Youse know like when |Yna |Yeah nah |Y'know |Y |Wun |Wtedy |When y'all |When |Wenn |WEN |wann |Ve |Và |Und |Un |ugeholl |Too right |Thurh |Thì |Then y'all |Then |Tha the |Tha |Tetapi |Tapi |Tak |Tada |Tad |Stel |Soit |Siis |Și |Şi |Si |Sed |Se |Så |Quando |Quand |Quan |Pryd |Potom |Pokud |Pokiaľ |Però |Pero |Pak |Oraz |Onda |Ond |Oletetaan |Og |Och |O zaman |Niin |Nhưng |När |Når |Mutta |Men |Mas |Maka |Majd |Mając |Mais |Maar |mä |Ma |Lorsque |Lorsqu'|Logo |Let go and haul |Kun |Kuid |Kui |Kiedy |Khi |Ketika |Kemudian |Keď |Když |Kaj |Kai |Kada |Kad |Jeżeli |Jeśli |Ja |It's just unbelievable |Ir |I CAN HAZ |I |Ha |Givun |Givet |Given y'all |Given |Gitt |Gegeven |Gegeben seien |Gegeben sei |Gdy |Gangway! |Fakat |Étant donnés |Etant donnés |Étant données |Etant données |Étant donnée |Etant donnée |Étant donné |Etant donné |Et |És |Entonces |Entón |Então |Entao |En |Eğer ki |Ef |Eeldades |E |Ðurh |Duota |Dun |Donitaĵo |Donat |Donada |Do |Diyelim ki |Diberi |Dengan |Den youse gotta |DEN |De |Dato |Dați fiind |Daţi fiind |Dati fiind |Dati |Date fiind |Date |Data |Dat fiind |Dar |Dann |dann |Dan |Dados |Dado |Dadas |Dada |Ða ðe |Ða |Cuando |Cho |Cando |Când |Cand |Cal |But y'all |But at the end of the day I reckon |BUT |But |Buh |Blimey! |Biết |Bet |Bagi |Aye |awer |Avast! |Atunci |Atesa |Atès |Apabila |Anrhegedig a |Angenommen |And y'all |And |AN |An |an |Amikor |Amennyiben |Ama |Als |Alors |Allora |Ali |Aleshores |Ale |Akkor |Ak |Adott |Ac |Aber |A zároveň |A tiež |A taktiež |A také |A |a |7 |\* )/)) { | ||
state.inStep = true; | ||
state.allowPlaceholders = true; | ||
state.allowMultilineArgument = true; | ||
state.inKeywordLine = true; | ||
return "keyword"; | ||
// INLINE STRING | ||
} else if (!state.inMultilineArgument && stream.match(/"/)) { | ||
stream.match(/.*?"/); | ||
} else if (stream.match(/"[^"]*"?/)) { | ||
return "string"; | ||
// MULTILINE ARGUMENTS | ||
} else if (state.allowSteps && stream.eat(":")) { | ||
if (stream.match(/\s*$/)) { | ||
state.inMultilineArgument = true; | ||
return "keyword"; | ||
} else { | ||
return null; | ||
} | ||
// PLACEHOLDER | ||
} else if (state.allowPlaceholders && stream.match(/<[^>]*>?/)) { | ||
return "variable"; | ||
} else if (state.allowSteps && stream.match("<")) { | ||
if (stream.match(/.*?>/)) { | ||
return "property"; | ||
} else { | ||
return null; | ||
} | ||
// Fall through | ||
} else { | ||
stream.eatWhile(/[^":<]/); | ||
stream.next(); | ||
stream.eatWhile(/[^@"<#]/); | ||
return null; | ||
} | ||
return null; | ||
} | ||
@@ -173,0 +168,0 @@ }; |
@@ -144,6 +144,2 @@ (function() { | ||
return style; | ||
}, | ||
indent: function(state) { | ||
return state.indented; | ||
} | ||
@@ -150,0 +146,0 @@ }; |
(function() { | ||
var mode = CodeMirror.getMode({tabSize: 4}, "haml"); | ||
var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, "haml"); | ||
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } | ||
@@ -4,0 +4,0 @@ |
@@ -11,3 +11,3 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) { | ||
var largeRE = /[A-Z]/; | ||
var digitRE = /[0-9]/; | ||
var digitRE = /\d/; | ||
var hexitRE = /[0-9A-Fa-f]/; | ||
@@ -80,5 +80,4 @@ var octitRE = /[0-7]/; | ||
var t = "number"; | ||
if (source.eat('.')) { | ||
if (source.match(/^\.\d+/)) { | ||
t = "number"; | ||
source.eatWhile(digitRE); // should require at least 1 | ||
} | ||
@@ -93,2 +92,5 @@ if (source.eat(/[eE]/)) { | ||
if (ch == "." && source.eat(".")) | ||
return "keyword"; | ||
if (symbolRE.test(ch)) { | ||
@@ -95,0 +97,0 @@ if (ch == '-' && source.eat(/-/)) { |
@@ -61,4 +61,2 @@ CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { | ||
electricChars: "/{}:", | ||
innerMode: function(state) { | ||
@@ -65,0 +63,0 @@ if (state.token == scriptingDispatch) return {state: state.scriptState, mode: scriptingMode}; |
@@ -96,4 +96,2 @@ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { | ||
electricChars: "/{}:", | ||
innerMode: function(state) { | ||
@@ -100,0 +98,0 @@ return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; |
@@ -18,3 +18,3 @@ // TODO actually recognize syntax of TypeScript constructs | ||
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, | ||
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, | ||
"return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "debugger": C, | ||
"var": kw("var"), "const": kw("var"), "let": kw("var"), | ||
@@ -58,10 +58,12 @@ "function": kw("function"), "catch": kw("catch"), | ||
function nextUntilUnescaped(stream, end) { | ||
var escaped = false, next; | ||
function readRegexp(stream) { | ||
var escaped = false, next, inSet = false; | ||
while ((next = stream.next()) != null) { | ||
if (next == end && !escaped) | ||
return false; | ||
if (!escaped) { | ||
if (next == "/" && !inSet) return; | ||
if (next == "[") inSet = true; | ||
else if (inSet && next == "]") inSet = false; | ||
} | ||
escaped = !escaped && next == "\\"; | ||
} | ||
return escaped; | ||
} | ||
@@ -88,3 +90,3 @@ | ||
} else if (ch == "=" && stream.eat(">")) { | ||
return ret("=>"); | ||
return ret("=>", "operator"); | ||
} else if (ch == "0" && stream.eat(/x/i)) { | ||
@@ -105,3 +107,3 @@ stream.eatWhile(/[\da-f]/i); | ||
state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) { | ||
nextUntilUnescaped(stream, "/"); | ||
readRegexp(stream); | ||
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla | ||
@@ -111,3 +113,3 @@ return ret("regexp", "string-2"); | ||
stream.eatWhile(isOperatorChar); | ||
return ret("operator", null, stream.current()); | ||
return ret("operator", "operator", stream.current()); | ||
} | ||
@@ -122,3 +124,3 @@ } else if (ch == "`") { | ||
stream.eatWhile(isOperatorChar); | ||
return ret("operator", null, stream.current()); | ||
return ret("operator", "operator", stream.current()); | ||
} else { | ||
@@ -134,4 +136,8 @@ stream.eatWhile(/[\w\$_]/); | ||
return function(stream, state) { | ||
if (!nextUntilUnescaped(stream, quote)) | ||
state.tokenize = tokenBase; | ||
var escaped = false, next; | ||
while ((next = stream.next()) != null) { | ||
if (next == quote && !escaped) break; | ||
escaped = !escaped && next == "\\"; | ||
} | ||
if (!escaped) state.tokenize = tokenBase; | ||
return ret("string", "string"); | ||
@@ -314,3 +320,3 @@ }; | ||
if (type == "function") return cont(functiondef); | ||
if (type == "for") return cont(pushlex("form"), forspec, poplex, statement, poplex); | ||
if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); | ||
if (type == "variable") return cont(pushlex("stat"), maybelabel); | ||
@@ -338,3 +344,3 @@ if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), | ||
var body = noComma ? arrowBodyNoComma : arrowBody; | ||
if (type == "(") return cont(pushcontext, commasep(pattern, ")"), expect("=>"), body, popcontext); | ||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); | ||
else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); | ||
@@ -349,4 +355,4 @@ } | ||
if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); | ||
if (type == "[") return cont(pushlex("]"), expressionNoComma, maybeArrayComprehension, poplex, maybeop); | ||
if (type == "{") return cont(commasep(objprop, "}"), maybeop); | ||
if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); | ||
if (type == "{") return contCommasep(objprop, "}", null, maybeop); | ||
return cont(); | ||
@@ -378,3 +384,3 @@ } | ||
if (type == ";") return; | ||
if (type == "(") return cont(commasep(expressionNoComma, ")", "call"), me); | ||
if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); | ||
if (type == ".") return cont(property, me); | ||
@@ -384,3 +390,2 @@ if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); | ||
function quasi(value) { | ||
if (!value) debugger; | ||
if (value.slice(value.length - 2) != "${") return cont(); | ||
@@ -433,3 +438,3 @@ return cont(expression, continueQuasi); | ||
} | ||
function commasep(what, end, info) { | ||
function commasep(what, end) { | ||
function proceed(type) { | ||
@@ -446,6 +451,10 @@ if (type == ",") { | ||
if (type == end) return cont(); | ||
if (info === false) return pass(what, proceed); | ||
return pass(pushlex(end, info), what, proceed, poplex); | ||
return pass(what, proceed); | ||
}; | ||
} | ||
function contCommasep(what, end, info) { | ||
for (var i = 3; i < arguments.length; i++) | ||
cx.cc.push(arguments[i]); | ||
return cont(pushlex(end, info), commasep(what, end), poplex); | ||
} | ||
function block(type) { | ||
@@ -466,4 +475,4 @@ if (type == "}") return cont(); | ||
if (type == "variable") { register(value); return cont(); } | ||
if (type == "[") return cont(commasep(pattern, "]")); | ||
if (type == "{") return cont(commasep(proppattern, "}")); | ||
if (type == "[") return contCommasep(pattern, "]"); | ||
if (type == "{") return contCommasep(proppattern, "}"); | ||
} | ||
@@ -488,3 +497,3 @@ function proppattern(type, value) { | ||
function forspec(type) { | ||
if (type == "(") return cont(pushlex(")"), forspec1, expect(")")); | ||
if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); | ||
} | ||
@@ -512,3 +521,3 @@ function forspec1(type) { | ||
if (type == "variable") {register(value); return cont(functiondef);} | ||
if (type == "(") return cont(pushcontext, commasep(funarg, ")"), statement, popcontext); | ||
if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext); | ||
} | ||
@@ -526,3 +535,3 @@ function funarg(type) { | ||
function objlit(type) { | ||
if (type == "{") return cont(commasep(objprop, "}")); | ||
if (type == "{") return contCommasep(objprop, "}"); | ||
} | ||
@@ -543,3 +552,3 @@ function afterModule(type, value) { | ||
function importSpec(type, value) { | ||
if (type == "{") return cont(commasep(importSpec, "}")); | ||
if (type == "{") return contCommasep(importSpec, "}"); | ||
if (type == "variable") register(value); | ||
@@ -551,6 +560,10 @@ return cont(); | ||
} | ||
function arrayLiteral(type) { | ||
if (type == "]") return cont(); | ||
return pass(expressionNoComma, maybeArrayComprehension); | ||
} | ||
function maybeArrayComprehension(type) { | ||
if (type == "for") return pass(comprehension); | ||
if (type == ",") return cont(commasep(expressionNoComma, "]", false)); | ||
return pass(commasep(expressionNoComma, "]", false)); | ||
if (type == "for") return pass(comprehension, expect("]")); | ||
if (type == ",") return cont(commasep(expressionNoComma, "]")); | ||
return pass(commasep(expressionNoComma, "]")); | ||
} | ||
@@ -557,0 +570,0 @@ function comprehension(type) { |
@@ -6,11 +6,11 @@ (function() { | ||
MT("locals", | ||
"[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] = [number 10]; [keyword return] [variable-2 a] + [variable-2 c] + [variable d]; }"); | ||
"[keyword function] [variable foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }"); | ||
MT("comma-and-binop", | ||
"[keyword function](){ [keyword var] [def x] = [number 1] + [number 2], [def y]; }"); | ||
"[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }"); | ||
MT("destructuring", | ||
"([keyword function]([def a], [[[def b], [def c] ]]) {", | ||
" [keyword let] {[def d], [property foo]: [def c]=[number 10], [def x]} = [variable foo]([variable-2 a]);", | ||
" [[[variable-2 c], [variable y] ]] = [variable-2 c];", | ||
" [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);", | ||
" [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];", | ||
"})();"); | ||
@@ -23,3 +23,3 @@ | ||
" [keyword super]([string 'something']);", | ||
" [keyword this].[property x] = [variable-2 x];", | ||
" [keyword this].[property x] [operator =] [variable-2 x];", | ||
" }", | ||
@@ -30,3 +30,3 @@ "}"); | ||
"[keyword module] [string 'foo'] {", | ||
" [keyword export] [keyword let] [def x] = [number 42];", | ||
" [keyword export] [keyword let] [def x] [operator =] [number 42];", | ||
" [keyword export] [keyword *] [keyword from] [string 'somewhere'];", | ||
@@ -44,3 +44,3 @@ "}"); | ||
"[keyword function] [variable f]() {", | ||
" [keyword const] [[ [def a], [def b] ]] = [[ [number 1], [number 2] ]];", | ||
" [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];", | ||
"}"); | ||
@@ -53,3 +53,3 @@ | ||
"[keyword function*] [variable repeat]([def n]) {", | ||
" [keyword for]([keyword var] [def i] = [number 0]; [variable-2 i] < [variable-2 n]; ++[variable-2 i])", | ||
" [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])", | ||
" [keyword yield] [variable-2 i];", | ||
@@ -59,5 +59,5 @@ "}"); | ||
MT("fatArrow", | ||
"[variable array].[property filter]([def a] => [variable-2 a] + [number 1]);", | ||
"[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);", | ||
"[variable a];", // No longer in scope | ||
"[keyword let] [variable f] = ([[ [def a], [def b] ]], [def c]) => [variable-2 a] + [variable-2 c];", | ||
"[keyword let] [variable f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];", | ||
"[variable c];"); | ||
@@ -72,8 +72,49 @@ | ||
"[keyword function] [variable f]() {", | ||
" [[ [variable x] + [number 1] [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];", | ||
" ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] === [string 'blue']));", | ||
" [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];", | ||
" ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));", | ||
"}"); | ||
MT("quasi", | ||
"[variable re][string-2 `fofdlakj${][variable x] + ([variable re][string-2 `foo`]) + [number 1][string-2 }fdsa`] + [number 2]"); | ||
"[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]"); | ||
MT("indent_statement", | ||
"[keyword var] [variable x] [operator =] [number 10]", | ||
"[variable x] [operator +=] [variable y] [operator +]", | ||
" [atom Infinity]", | ||
"[keyword debugger];"); | ||
MT("indent_if", | ||
"[keyword if] ([number 1])", | ||
" [keyword break];", | ||
"[keyword else] [keyword if] ([number 2])", | ||
" [keyword continue];", | ||
"[keyword else]", | ||
" [number 10];", | ||
"[keyword if] ([number 1]) {", | ||
" [keyword break];", | ||
"} [keyword else] [keyword if] ([number 2]) {", | ||
" [keyword continue];", | ||
"} [keyword else] {", | ||
" [number 10];", | ||
"}"); | ||
MT("indent_for", | ||
"[keyword for] ([keyword var] [variable i] [operator =] [number 0];", | ||
" [variable i] [operator <] [number 100];", | ||
" [variable i][operator ++])", | ||
" [variable doSomething]([variable i]);", | ||
"[keyword debugger];"); | ||
MT("indent_c_style", | ||
"[keyword function] [variable foo]()", | ||
"{", | ||
" [keyword debugger];", | ||
"}"); | ||
MT("multilinestring", | ||
"[keyword var] [variable x] [operator =] [string 'foo\\]", | ||
"[string bar'];"); | ||
MT("scary_regexp", | ||
"[string-2 /foo[[/]]bar/];"); | ||
})(); |
CodeMirror.defineMode("jinja2", function() { | ||
var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", | ||
"loop", "none", "self", "super", "if", "as", "not", "and", | ||
"else", "import", "with", "without", "context"]; | ||
var keywords = ["and", "as", "block", "endblock", "by", "cycle", "debug", "else", "elif", | ||
"extends", "filter", "endfilter", "firstof", "for", | ||
"endfor", "if", "endif", "ifchanged", "endifchanged", | ||
"ifequal", "endifequal", "ifnotequal", | ||
"endifnotequal", "in", "include", "load", "not", "now", "or", | ||
"parsed", "regroup", "reversed", "spaceless", | ||
"endspaceless", "ssi", "templatetag", "openblock", | ||
"closeblock", "openvariable", "closevariable", | ||
"openbrace", "closebrace", "opencomment", | ||
"closecomment", "widthratio", "url", "with", "endwith", | ||
"get_current_language", "trans", "noop", "blocktrans", | ||
"endblocktrans", "get_available_languages", | ||
"get_current_language_bidi", "plural"]; | ||
keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); | ||
@@ -6,0 +16,0 @@ |
@@ -74,3 +74,3 @@ /* | ||
else if(stream.current().length >1){ | ||
if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit"); | ||
if(state.stack[state.stack.length-1] === "rule" && !stream.match(/^[{,+(]/, false)) return ret("number", "unit"); | ||
} | ||
@@ -174,3 +174,3 @@ return ret("tag", "tag"); | ||
} else if(type == "|" || stream.current() == "-" || type == "["){ | ||
if(type == "|" && stream.peek().match(/\]|=|\~/) !== null)return ret("number", stream.current()); | ||
if (type == "|" && stream.match(/^[\]=~]/, false)) return ret("number", stream.current()); | ||
else if(type == "|" )return ret("tag", "tag"); | ||
@@ -206,3 +206,3 @@ else if(type == "["){ | ||
else if(/\^|\$/.test(stream.current()) && stream.peek().match(/\~|=/) !== null)return ret("string", "string");//att^=val | ||
else if (/\^|\$/.test(stream.current()) && stream.match(/^[~=]/, false)) return ret("string", "string");//att^=val | ||
@@ -216,3 +216,4 @@ else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit"); | ||
else if((type === ";" || type === "}" || type === ",") && state.stack[state.stack.length-1] === ";")return ret("tag", stream.current()); | ||
else if((type === ";" && stream.peek() !== undefined && stream.peek().match(/{|./) === null) || (type === ";" && stream.eatSpace() && stream.peek().match(/{|./) === null))return ret("variable", stream.current()); | ||
else if((type === ";" && stream.peek() !== undefined && !stream.match(/^[{\.]/, false)) || | ||
(type === ";" && stream.eatSpace() && !stream.match(/^[{\.]/))) return ret("variable", stream.current()); | ||
else if((type === "@media" && state.stack[state.stack.length-1] === "@media") || type === "@namespace")return ret("tag", stream.current()); | ||
@@ -222,3 +223,3 @@ | ||
else if((type === "{" || type === ":") && state.stack[state.stack.length-1] === ";")return ret(null, stream.current()); | ||
else if((state.stack[state.stack.length-1] === "{" && stream.eatSpace() && stream.peek().match(/.|#/) === null) || type === "select-op" || (state.stack[state.stack.length-1] === "rule" && type === ",") )return ret("tag", "tag"); | ||
else if((state.stack[state.stack.length-1] === "{" && stream.eatSpace() && !stream.match(/^[\.#]/)) || type === "select-op" || (state.stack[state.stack.length-1] === "rule" && type === ",") )return ret("tag", "tag"); | ||
else if(type === "variable" && state.stack[state.stack.length-1] === "rule")return ret("tag", "tag"); | ||
@@ -235,3 +236,3 @@ else if((stream.eatSpace() && stream.peek() === "{") || stream.eol() || stream.peek() === "{")return ret("tag", "tag"); | ||
else if(state.stack.length === 0)return ret("tag", "tag"); | ||
else if(type === ";" && stream.peek() !== undefined && stream.peek().match(/^[.|\#]/g) !== null)return ret("tag", "tag"); | ||
else if(type === ";" && stream.peek() !== undefined && stream.match(/^[\.|#]/g)) return ret("tag", "tag"); | ||
@@ -238,0 +239,0 @@ else if(type === ":"){stream.eatSpace();return ret(null, stream.current());} |
@@ -45,2 +45,12 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { | ||
// Should characters that affect highlighting be highlighted separate? | ||
// Does not include characters that will be output (such as `1.` and `-` for lists) | ||
if (modeCfg.highlightFormatting === undefined) | ||
modeCfg.highlightFormatting = false; | ||
// Maximum number of nested blockquotes. Set to 0 for infinite nesting. | ||
// Excess `>` will emit `error` token. | ||
if (modeCfg.maxBlockquoteDepth === undefined) | ||
modeCfg.maxBlockquoteDepth = 0; | ||
// Should underscores in words open/close em/strong? | ||
@@ -60,4 +70,3 @@ if (modeCfg.underscoresBreakWords === undefined) | ||
, code = 'comment' | ||
, quote1 = 'atom' | ||
, quote2 = 'number' | ||
, quote = 'quote' | ||
, list1 = 'variable-2' | ||
@@ -68,2 +77,3 @@ , list2 = 'variable-3' | ||
, image = 'tag' | ||
, formatting = 'formatting' | ||
, linkinline = 'link' | ||
@@ -82,3 +92,3 @@ , linkemail = 'link' | ||
, setextHeaderRE = /^(?:\={1,}|-{1,})$/ | ||
, textRE = /^[^!\[\]*_\\<>` "'(]+/; | ||
, textRE = /^[^#!\[\]*_\\<>` "'(]+/; | ||
@@ -121,2 +131,4 @@ function switchInline(stream, state, f) { | ||
var sol = stream.sol(); | ||
var prevLineIsList = (state.list !== false); | ||
@@ -145,12 +157,16 @@ if (state.list !== false && state.indentationDiff >= 0) { // Continued list | ||
state.header = match[0].length <= 6 ? match[0].length : 6; | ||
if (modeCfg.highlightFormatting) state.formatting = "header"; | ||
state.f = state.inline; | ||
return getType(state); | ||
} else if (state.prevLineHasContent && (match = stream.match(setextHeaderRE))) { | ||
state.header = match[0].charAt(0) == '=' ? 1 : 2; | ||
if (modeCfg.highlightFormatting) state.formatting = "header"; | ||
state.f = state.inline; | ||
return getType(state); | ||
} else if (stream.eat('>')) { | ||
state.indentation++; | ||
state.quote = 1; | ||
state.quote = sol ? 1 : state.quote + 1; | ||
if (modeCfg.highlightFormatting) state.formatting = "quote"; | ||
stream.eatSpace(); | ||
while (stream.eat('>')) { | ||
stream.eatSpace(); | ||
state.quote++; | ||
} | ||
return getType(state); | ||
} else if (stream.peek() === '[') { | ||
@@ -160,3 +176,10 @@ return switchInline(stream, state, footnoteLink); | ||
return hr; | ||
} else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, true) || stream.match(olRE, true))) { | ||
} else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) { | ||
var listType = null; | ||
if (stream.match(ulRE, true)) { | ||
listType = 'ul'; | ||
} else { | ||
stream.match(olRE, true); | ||
listType = 'ol'; | ||
} | ||
state.indentation += 4; | ||
@@ -168,2 +191,5 @@ state.list = true; | ||
} | ||
state.f = state.inline; | ||
if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType]; | ||
return getType(state); | ||
} else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) { | ||
@@ -174,3 +200,5 @@ // try switching mode | ||
switchBlock(stream, state, local); | ||
return code; | ||
if (modeCfg.highlightFormatting) state.formatting = "code-block"; | ||
state.code = true; | ||
return getType(state); | ||
} | ||
@@ -183,11 +211,8 @@ | ||
var style = htmlMode.token(stream, state.htmlState); | ||
if (htmlFound && style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) { | ||
if ((htmlFound && !state.htmlState.tagName && !state.htmlState.context) || | ||
(state.md_inside && stream.current().indexOf(">") > -1)) { | ||
state.f = inlineNormal; | ||
state.block = blockNormal; | ||
state.htmlState = null; | ||
} | ||
if (state.md_inside && stream.current().indexOf(">")!=-1) { | ||
state.f = inlineNormal; | ||
state.block = blockNormal; | ||
state.htmlState.context = undefined; | ||
} | ||
return style; | ||
@@ -201,3 +226,7 @@ } | ||
state.block = blockNormal; | ||
return code; | ||
if (modeCfg.highlightFormatting) state.formatting = "code-block"; | ||
state.code = true; | ||
var returnType = getType(state); | ||
state.code = false; | ||
return returnType; | ||
} else if (state.localMode) { | ||
@@ -215,5 +244,40 @@ return state.localMode.token(stream, state.localState); | ||
if (state.taskOpen) { return "meta"; } | ||
if (state.taskClosed) { return "property"; } | ||
if (state.formatting) { | ||
styles.push(formatting); | ||
if (typeof state.formatting === "string") state.formatting = [state.formatting]; | ||
for (var i = 0; i < state.formatting.length; i++) { | ||
styles.push(formatting + "-" + state.formatting[i]); | ||
if (state.formatting[i] === "header") { | ||
styles.push(formatting + "-" + state.formatting[i] + state.header); | ||
} | ||
// Add `formatting-quote` and `formatting-quote-#` for blockquotes | ||
// Add `error` instead if the maximum blockquote nesting depth is passed | ||
if (state.formatting[i] === "quote") { | ||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) { | ||
styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote); | ||
} else { | ||
styles.push("error"); | ||
} | ||
} | ||
} | ||
} | ||
if (state.taskOpen) { | ||
styles.push("meta"); | ||
return styles.length ? styles.join(' ') : null; | ||
} | ||
if (state.taskClosed) { | ||
styles.push("property"); | ||
return styles.length ? styles.join(' ') : null; | ||
} | ||
if (state.linkHref) { | ||
styles.push(linkhref); | ||
return styles.length ? styles.join(' ') : null; | ||
} | ||
if (state.strong) { styles.push(strong); } | ||
@@ -227,3 +291,14 @@ if (state.em) { styles.push(em); } | ||
if (state.header) { styles.push(header); styles.push(header + state.header); } | ||
if (state.quote) { styles.push(state.quote % 2 ? quote1 : quote2); } | ||
if (state.quote) { | ||
styles.push(quote); | ||
// Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth | ||
if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) { | ||
styles.push(quote + "-" + state.quote); | ||
} else { | ||
styles.push(quote + "-" + modeCfg.maxBlockquoteDepth); | ||
} | ||
} | ||
if (state.list !== false) { | ||
@@ -270,2 +345,3 @@ var listMod = (state.listDepth - 1) % 3; | ||
else state.taskClosed = true; | ||
if (modeCfg.highlightFormatting) state.formatting = "task"; | ||
state.taskList = false; | ||
@@ -278,2 +354,7 @@ return getType(state); | ||
if (state.header && stream.match(/^#+$/, true)) { | ||
if (modeCfg.highlightFormatting) state.formatting = "header"; | ||
return getType(state); | ||
} | ||
// Get sol() value now, before character is consumed | ||
@@ -284,4 +365,10 @@ var sol = stream.sol(); | ||
if (state.escape) { | ||
state.escape = false; | ||
return getType(state); | ||
} | ||
if (ch === '\\') { | ||
stream.next(); | ||
if (modeCfg.highlightFormatting) state.formatting = "escape"; | ||
state.escape = true; | ||
return getType(state); | ||
@@ -306,2 +393,4 @@ } | ||
if (ch === '`') { | ||
var previousFormatting = state.formatting; | ||
if (modeCfg.highlightFormatting) state.formatting = "code"; | ||
var t = getType(state); | ||
@@ -320,2 +409,3 @@ var before = stream.pos; | ||
} | ||
state.formatting = previousFormatting; | ||
return getType(state); | ||
@@ -335,2 +425,3 @@ } | ||
state.linkText = true; | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
return getType(state); | ||
@@ -340,2 +431,3 @@ } | ||
if (ch === ']' && state.linkText) { | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
var type = getType(state); | ||
@@ -348,11 +440,27 @@ state.linkText = false; | ||
if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) { | ||
return switchInline(stream, state, inlineElement(linkinline, '>')); | ||
state.f = state.inline = linkInline; | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
var type = getType(state); | ||
if (type){ | ||
type += " "; | ||
} else { | ||
type = ""; | ||
} | ||
return type + linkinline; | ||
} | ||
if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) { | ||
return switchInline(stream, state, inlineElement(linkemail, '>')); | ||
state.f = state.inline = linkInline; | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
var type = getType(state); | ||
if (type){ | ||
type += " "; | ||
} else { | ||
type = ""; | ||
} | ||
return type + linkemail; | ||
} | ||
if (ch === '<' && stream.match(/^\w/, false)) { | ||
if (stream.string.indexOf(">")!=-1) { | ||
if (stream.string.indexOf(">") != -1) { | ||
var atts = stream.string.substring(1,stream.string.indexOf(">")); | ||
@@ -364,2 +472,3 @@ if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) { | ||
stream.backUp(1); | ||
state.htmlState = CodeMirror.startState(htmlMode); | ||
return switchBlock(stream, state, htmlBlock); | ||
@@ -385,3 +494,2 @@ } | ||
} | ||
var t = getType(state); | ||
if (ch === '*' || (ch === '_' && !ignoreUnderscore)) { | ||
@@ -391,2 +499,4 @@ if (sol && stream.peek() === ' ') { | ||
} else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG | ||
if (modeCfg.highlightFormatting) state.formatting = "strong"; | ||
var t = getType(state); | ||
state.strong = false; | ||
@@ -396,4 +506,7 @@ return t; | ||
state.strong = ch; | ||
if (modeCfg.highlightFormatting) state.formatting = "strong"; | ||
return getType(state); | ||
} else if (state.em === ch) { // Remove EM | ||
if (modeCfg.highlightFormatting) state.formatting = "em"; | ||
var t = getType(state); | ||
state.em = false; | ||
@@ -403,2 +516,3 @@ return t; | ||
state.em = ch; | ||
if (modeCfg.highlightFormatting) state.formatting = "em"; | ||
return getType(state); | ||
@@ -427,2 +541,22 @@ } | ||
function linkInline(stream, state) { | ||
var ch = stream.next(); | ||
if (ch === ">") { | ||
state.f = state.inline = inlineNormal; | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
var type = getType(state); | ||
if (type){ | ||
type += " "; | ||
} else { | ||
type = ""; | ||
} | ||
return type + linkinline; | ||
} | ||
stream.match(/^[^>]+/, true); | ||
return linkinline; | ||
} | ||
function linkHref(stream, state) { | ||
@@ -435,3 +569,6 @@ // Check if space, and return NULL if so (to avoid marking the space) | ||
if (ch === '(' || ch === '[') { | ||
return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']')); | ||
state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]"); | ||
if (modeCfg.highlightFormatting) state.formatting = "link-string"; | ||
state.linkHref = true; | ||
return getType(state); | ||
} | ||
@@ -441,6 +578,30 @@ return 'error'; | ||
function getLinkHrefInside(endChar) { | ||
return function(stream, state) { | ||
var ch = stream.next(); | ||
if (ch === endChar) { | ||
state.f = state.inline = inlineNormal; | ||
if (modeCfg.highlightFormatting) state.formatting = "link-string"; | ||
var returnState = getType(state); | ||
state.linkHref = false; | ||
return returnState; | ||
} | ||
if (stream.match(inlineRE(endChar), true)) { | ||
stream.backUp(1); | ||
} | ||
state.linkHref = true; | ||
return getType(state); | ||
}; | ||
} | ||
function footnoteLink(stream, state) { | ||
if (stream.match(/^[^\]]*\]:/, true)) { | ||
state.f = footnoteUrl; | ||
return linktext; | ||
if (stream.match(/^[^\]]*\]:/, false)) { | ||
state.f = footnoteLinkInside; | ||
stream.next(); // Consume [ | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
state.linkText = true; | ||
return getType(state); | ||
} | ||
@@ -450,2 +611,16 @@ return switchInline(stream, state, inlineNormal); | ||
function footnoteLinkInside(stream, state) { | ||
if (stream.match(/^\]:/, true)) { | ||
state.f = state.inline = footnoteUrl; | ||
if (modeCfg.highlightFormatting) state.formatting = "link"; | ||
var returnType = getType(state); | ||
state.linkText = false; | ||
return returnType; | ||
} | ||
stream.match(/^[^\]]+/, true); | ||
return linktext; | ||
} | ||
function footnoteUrl(stream, state) { | ||
@@ -480,12 +655,3 @@ // Check if space, and return NULL if so (to avoid marking the space) | ||
function inlineElement(type, endChar, next) { | ||
next = next || inlineNormal; | ||
return function(stream, state) { | ||
stream.match(inlineRE(endChar)); | ||
state.inline = state.f = next; | ||
return type; | ||
}; | ||
} | ||
return { | ||
var mode = { | ||
startState: function() { | ||
@@ -499,3 +665,3 @@ return { | ||
block: blockNormal, | ||
htmlState: CodeMirror.startState(htmlMode), | ||
htmlState: null, | ||
indentation: 0, | ||
@@ -506,3 +672,6 @@ | ||
escape: false, | ||
formatting: false, | ||
linkText: false, | ||
linkHref: false, | ||
linkTitle: false, | ||
@@ -529,3 +698,3 @@ em: false, | ||
block: s.block, | ||
htmlState: CodeMirror.copyState(htmlMode, s.htmlState), | ||
htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState), | ||
indentation: s.indentation, | ||
@@ -538,2 +707,4 @@ | ||
text: s.text, | ||
escape: false, | ||
formatting: false, | ||
linkTitle: s.linkTitle, | ||
@@ -554,4 +725,13 @@ em: s.em, | ||
token: function(stream, state) { | ||
// Reset state.formatting | ||
state.formatting = false; | ||
if (stream.sol()) { | ||
if (stream.match(/^\s*$/, true)) { | ||
var forceBlankLine = stream.match(/^\s*$/, true) || state.header; | ||
// Reset state.header | ||
state.header = 0; | ||
if (forceBlankLine) { | ||
state.prevLineHasContent = false; | ||
@@ -564,4 +744,4 @@ return blankLine(state); | ||
// Reset state.header | ||
state.header = 0; | ||
// Reset state.escape | ||
state.escape = false; | ||
@@ -590,2 +770,8 @@ // Reset state.taskList | ||
innerMode: function(state) { | ||
if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode}; | ||
if (state.localState) return {state: state.localState, mode: state.localMode}; | ||
return {state: state, mode: mode}; | ||
}, | ||
blankLine: blankLine, | ||
@@ -595,5 +781,5 @@ | ||
}; | ||
return mode; | ||
}, "xml"); | ||
CodeMirror.defineMIME("text/x-markdown", "markdown"); |
(function() { | ||
var mode = CodeMirror.getMode({tabSize: 4}, "markdown"); | ||
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); } | ||
var modeHighlightFormatting = CodeMirror.getMode({tabSize: 4}, {name: "markdown", highlightFormatting: true}); | ||
function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); } | ||
FT("formatting_emAsterisk", | ||
"[em&formatting&formatting-em *][em foo][em&formatting&formatting-em *]"); | ||
FT("formatting_emUnderscore", | ||
"[em&formatting&formatting-em _][em foo][em&formatting&formatting-em _]"); | ||
FT("formatting_strongAsterisk", | ||
"[strong&formatting&formatting-strong **][strong foo][strong&formatting&formatting-strong **]"); | ||
FT("formatting_strongUnderscore", | ||
"[strong&formatting&formatting-strong __][strong foo][strong&formatting&formatting-strong __]"); | ||
FT("formatting_codeBackticks", | ||
"[comment&formatting&formatting-code `][comment foo][comment&formatting&formatting-code `]"); | ||
FT("formatting_doubleBackticks", | ||
"[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]"); | ||
FT("formatting_atxHeader", | ||
"[header&header1&formatting&formatting-header&formatting-header1 #][header&header1 foo # bar ][header&header1&formatting&formatting-header&formatting-header1 #]"); | ||
FT("formatting_setextHeader", | ||
"foo", | ||
"[header&header1&formatting&formatting-header&formatting-header1 =]"); | ||
FT("formatting_blockquote", | ||
"[quote"e-1&formatting&formatting-quote&formatting-quote-1 > ][quote"e-1 foo]"); | ||
FT("formatting_list", | ||
"[variable-2&formatting&formatting-list&formatting-list-ul - ][variable-2 foo]"); | ||
FT("formatting_list", | ||
"[variable-2&formatting&formatting-list&formatting-list-ol 1. ][variable-2 foo]"); | ||
FT("formatting_link", | ||
"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string (][string http://example.com/][string&formatting&formatting-link-string )]"); | ||
FT("formatting_linkReference", | ||
"[link&formatting&formatting-link [][link foo][link&formatting&formatting-link ]]][string&formatting&formatting-link-string [][string bar][string&formatting&formatting-link-string ]]]", | ||
"[link&formatting&formatting-link [][link bar][link&formatting&formatting-link ]]:] [string http://example.com/]"); | ||
FT("formatting_linkWeb", | ||
"[link&formatting&formatting-link <][link http://example.com/][link&formatting&formatting-link >]"); | ||
FT("formatting_linkEmail", | ||
"[link&formatting&formatting-link <][link user@example.com][link&formatting&formatting-link >]"); | ||
FT("formatting_escape", | ||
"[formatting&formatting-escape \\]*"); | ||
MT("plainText", | ||
@@ -145,7 +196,7 @@ "foo"); | ||
MT("blockquoteSpace", | ||
"[atom > foo]"); | ||
"[quote"e-1 > foo]"); | ||
// Single-line blockquote | ||
MT("blockquoteNoSpace", | ||
"[atom >foo]"); | ||
"[quote"e-1 >foo]"); | ||
@@ -155,13 +206,13 @@ // No blank line before blockquote | ||
"foo", | ||
"[atom > bar]"); | ||
"[quote"e-1 > bar]"); | ||
// Nested blockquote | ||
MT("blockquoteSpace", | ||
"[atom > foo]", | ||
"[number > > foo]", | ||
"[atom > > > foo]"); | ||
"[quote"e-1 > foo]", | ||
"[quote"e-1 >][quote"e-2 > foo]", | ||
"[quote"e-1 >][quote"e-2 >][quote"e-3 > foo]"); | ||
// Single-line blockquote followed by normal paragraph | ||
MT("blockquoteThenParagraph", | ||
"[atom >foo]", | ||
"[quote"e-1 >foo]", | ||
"", | ||
@@ -172,9 +223,9 @@ "bar"); | ||
MT("multiBlockquoteLazy", | ||
"[atom >foo]", | ||
"[atom bar]"); | ||
"[quote"e-1 >foo]", | ||
"[quote"e-1 bar]"); | ||
// Multi-line blockquote followed by normal paragraph (lazy mode) | ||
MT("multiBlockquoteLazyThenParagraph", | ||
"[atom >foo]", | ||
"[atom bar]", | ||
"[quote"e-1 >foo]", | ||
"[quote"e-1 bar]", | ||
"", | ||
@@ -185,9 +236,9 @@ "hello"); | ||
MT("multiBlockquote", | ||
"[atom >foo]", | ||
"[atom >bar]"); | ||
"[quote"e-1 >foo]", | ||
"[quote"e-1 >bar]"); | ||
// Multi-line blockquote followed by normal paragraph (non-lazy mode) | ||
MT("multiBlockquoteThenParagraph", | ||
"[atom >foo]", | ||
"[atom >bar]", | ||
"[quote"e-1 >foo]", | ||
"[quote"e-1 >bar]", | ||
"", | ||
@@ -232,2 +283,7 @@ "hello"); | ||
// List after header | ||
MT("listAfterHeader", | ||
"[header&header1 # foo]", | ||
"[variable-2 - bar]"); | ||
// Formatting in lists (*) | ||
@@ -318,3 +374,3 @@ MT("listAsteriskFormatting", | ||
"", | ||
" [variable-2&atom > hello]"); | ||
" [variable-2"e"e-1 > hello]"); | ||
@@ -377,3 +433,3 @@ // Code block | ||
"", | ||
" [atom&variable-3 > hello]"); | ||
" [quote"e-1&variable-3 > hello]"); | ||
@@ -661,3 +717,7 @@ MT("listCode", | ||
MT("escapeNewline", | ||
"\\", | ||
"[em *foo*]"); | ||
// Tests to make sure GFM-specific things aren't getting through | ||
@@ -664,0 +724,0 @@ |
@@ -21,2 +21,3 @@ CodeMirror.modeInfo = [ | ||
{name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'}, | ||
{name: 'F#', mime: 'text/x-fsharp', mode: 'mllike'}, | ||
{name: 'Gas', mime: 'text/x-gas', mode: 'gas'}, | ||
@@ -49,3 +50,3 @@ {name: 'Gherkin', mime: 'text/x-feature', mode: 'gherkin'}, | ||
{name: 'NTriples', mime: 'text/n-triples', mode: 'ntriples'}, | ||
{name: 'OCaml', mime: 'text/x-ocaml', mode: 'ocaml'}, | ||
{name: 'OCaml', mime: 'text/x-ocaml', mode: 'mllike'}, | ||
{name: 'Octave', mime: 'text/x-octave', mode: 'octave'}, | ||
@@ -52,0 +53,0 @@ {name: 'Pascal', mime: 'text/x-pascal', mode: 'pascal'}, |
@@ -57,11 +57,7 @@ CodeMirror.defineMode("pegjs", function (config) { | ||
} else if (state.inChracterClass) { | ||
if (stream.match(/^[^\]\\]+/)) { | ||
return; | ||
} else if (stream.match(/^\\./)) { | ||
return; | ||
} else { | ||
stream.next(); | ||
state.inChracterClass = false; | ||
return 'bracket'; | ||
} | ||
while (state.inChracterClass && !stream.eol()) { | ||
if (!(stream.match(/^[^\]\\]+/) || stream.match(/^\\./))) { | ||
state.inChracterClass = false; | ||
} | ||
} | ||
} else if (stream.peek() === '[') { | ||
@@ -68,0 +64,0 @@ stream.next(); |
@@ -59,3 +59,3 @@ (function() { | ||
var isPHP = state.curMode == phpMode; | ||
if (stream.sol() && state.pending != '"') state.pending = null; | ||
if (stream.sol() && state.pending && state.pending != '"' && state.pending != "'") state.pending = null; | ||
if (!isPHP) { | ||
@@ -67,4 +67,4 @@ if (stream.match(/^<\?\w*/)) { | ||
} | ||
if (state.pending == '"') { | ||
while (!stream.eol() && stream.next() != '"') {} | ||
if (state.pending == '"' || state.pending == "'") { | ||
while (!stream.eol() && stream.next() != state.pending) {} | ||
var style = "string"; | ||
@@ -77,6 +77,6 @@ } else if (state.pending && stream.pos < state.pending.end) { | ||
} | ||
state.pending = null; | ||
var cur = stream.current(), openPHP = cur.search(/<\?/); | ||
if (state.pending) state.pending = null; | ||
var cur = stream.current(), openPHP = cur.search(/<\?/), m; | ||
if (openPHP != -1) { | ||
if (style == "string" && /\"$/.test(cur) && !/\?>/.test(cur)) state.pending = '"'; | ||
if (style == "string" && (m = cur.match(/[\'\"]$/)) && !/\?>/.test(cur)) state.pending = m[0]; | ||
else state.pending = {end: stream.pos, style: style}; | ||
@@ -123,3 +123,2 @@ stream.backUp(cur.length - openPHP); | ||
electricChars: "/{}:", | ||
blockCommentStart: "/*", | ||
@@ -126,0 +125,0 @@ blockCommentEnd: "*/", |
@@ -14,2 +14,3 @@ CodeMirror.defineMode("python", function(conf, parserConf) { | ||
var identifiers = parserConf.identifiers|| new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); | ||
var hangingIndent = parserConf.hangingIndent || parserConf.indentUnit; | ||
@@ -215,2 +216,7 @@ var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']); | ||
} | ||
} else if (stream.match(/\s*($|#)/, false)) { | ||
// An open paren/bracket/brace with only space or comments after it | ||
// on the line will indent the next line a fixed amount, to make it | ||
// easier to put arguments, list items, etc. on their own lines. | ||
indentUnit = stream.indentation() + hangingIndent; | ||
} else { | ||
@@ -217,0 +223,0 @@ indentUnit = stream.column() + stream.current().length; |
@@ -39,3 +39,7 @@ CodeMirror.defineMode("r", function(config) { | ||
if (keywords.propertyIsEnumerable(word)) { | ||
if (blockkeywords.propertyIsEnumerable(word)) curPunc = "block"; | ||
// Block keywords start new blocks, except 'else if', which only starts | ||
// one new block for the 'if', no block for the 'else'. | ||
if (blockkeywords.propertyIsEnumerable(word) && | ||
!stream.match(/\s*if(\s+|$)/, false)) | ||
curPunc = "block"; | ||
return "keyword"; | ||
@@ -42,0 +46,0 @@ } |
@@ -7,3 +7,3 @@ // Quick and dirty spec file highlighting | ||
var preamble = /^(Name|Version|Release|License|Summary|Url|Group|Source|BuildArch|BuildRequires|BuildRoot|AutoReqProv|Provides|Requires(\(\w+\))?|Obsoletes|Conflicts|Recommends|Source\d*|Patch\d*|ExclusiveArch|NoSource|Supplements):/; | ||
var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preun|postun|pre|post|triggerin|triggerun|pretrans|posttrans|verifyscript|check|triggerpostun|triggerprein|trigger)/; | ||
var section = /^%(debug_package|package|description|prep|build|install|files|clean|changelog|preinstall|preun|postinstall|postun|pre|post|triggerin|triggerun|pretrans|posttrans|verifyscript|check|triggerpostun|triggerprein|trigger)/; | ||
var control_flow_complex = /^%(ifnarch|ifarch|if)/; // rpm control flow macros | ||
@@ -10,0 +10,0 @@ var control_flow_simple = /^%(else|endif)/; // rpm control flow macros |
@@ -15,3 +15,3 @@ CodeMirror.defineMode("ruby", function(config) { | ||
]); | ||
var indentWords = wordObj(["def", "class", "case", "for", "while", "do", "module", "then", | ||
var indentWords = wordObj(["def", "class", "case", "for", "while", "module", "then", | ||
"catch", "loop", "proc", "begin"]); | ||
@@ -218,2 +218,4 @@ var dedentWords = wordObj(["end", "until"]); | ||
kwtype = "indent"; | ||
else if (word == "do" && state.context.indented < state.indented) | ||
kwtype = "indent"; | ||
} | ||
@@ -220,0 +222,0 @@ if (curPunc || (style && style != "comment")) state.lastTok = word || curPunc || style; |
@@ -43,4 +43,6 @@ CodeMirror.defineMode('smalltalk', function(config) { | ||
} else { | ||
stream.eatWhile(/[^ .\[\]()]/); | ||
token.name = 'string-2'; | ||
if (stream.eatWhile(/[^ .{}\[\]()]/)) | ||
token.name = 'string-2'; | ||
else | ||
token.name = 'meta'; | ||
} | ||
@@ -47,0 +49,0 @@ |
@@ -162,4 +162,2 @@ /** | ||
electricChars: "/{}:", | ||
innerMode: function(state) { | ||
@@ -166,0 +164,0 @@ return { |
@@ -180,5 +180,6 @@ CodeMirror.defineMode("sql", function(config, parserConfig) { | ||
var cx = state.context; | ||
if (!cx) return CodeMirror.Pass; | ||
if (cx.align) return cx.col + (textAfter.charAt(0) == cx.type ? 0 : 1); | ||
else return cx.indent + config.indentUnit; | ||
if (!cx) return 0; | ||
var closing = textAfter.charAt(0) == cx.type; | ||
if (cx.align) return cx.col + (closing ? 0 : 1); | ||
else return cx.indent + (closing ? 0 : config.indentUnit); | ||
}, | ||
@@ -330,3 +331,3 @@ | ||
keywords: set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"), | ||
builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2 abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"), | ||
builtin: set("abs acos add_months ascii asin atan atan2 average bfile bfilename bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv varchar varchar2 variance varying vsize xml"), | ||
operatorChars: /^[*+\-%<>!=~]/, | ||
@@ -333,0 +334,0 @@ dateSQL: set("date time timestamp"), |
@@ -48,3 +48,3 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { | ||
// Return variables for tokenizers | ||
var tagName, type; | ||
var tagName, type, setStyle; | ||
@@ -114,2 +114,4 @@ function inText(stream, state) { | ||
state.tokenize = inText; | ||
state.state = baseState; | ||
state.tagName = state.tagStart = null; | ||
var next = state.tokenize(stream, state); | ||
@@ -174,38 +176,40 @@ return next ? next + " error" : "error"; | ||
var curState, curStream, setStyle; | ||
function pass() { | ||
for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); | ||
function Context(state, tagName, startOfLine) { | ||
this.prev = state.context; | ||
this.tagName = tagName; | ||
this.indent = state.indented; | ||
this.startOfLine = startOfLine; | ||
if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) | ||
this.noIndent = true; | ||
} | ||
function cont() { | ||
pass.apply(null, arguments); | ||
return true; | ||
function popContext(state) { | ||
if (state.context) state.context = state.context.prev; | ||
} | ||
function pushContext(tagName, startOfLine) { | ||
var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); | ||
curState.context = { | ||
prev: curState.context, | ||
tagName: tagName, | ||
indent: curState.indented, | ||
startOfLine: startOfLine, | ||
noIndent: noIndent | ||
}; | ||
function maybePopContext(state, nextTagName) { | ||
var parentTagName; | ||
while (true) { | ||
if (!state.context) { | ||
return; | ||
} | ||
parentTagName = state.context.tagName.toLowerCase(); | ||
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || | ||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { | ||
return; | ||
} | ||
popContext(state); | ||
} | ||
} | ||
function popContext() { | ||
if (curState.context) curState.context = curState.context.prev; | ||
} | ||
function element(type) { | ||
function baseState(type, stream, state) { | ||
if (type == "openTag") { | ||
curState.tagName = tagName; | ||
curState.tagStart = curStream.column(); | ||
return cont(attributes, endtag(curState.startOfLine)); | ||
state.tagName = tagName; | ||
state.tagStart = stream.column(); | ||
return attrState; | ||
} else if (type == "closeTag") { | ||
var err = false; | ||
if (curState.context) { | ||
if (curState.context.tagName != tagName) { | ||
if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { | ||
popContext(); | ||
} | ||
err = !curState.context || curState.context.tagName != tagName; | ||
if (state.context) { | ||
if (state.context.tagName != tagName) { | ||
if (Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName.toLowerCase())) | ||
popContext(state); | ||
err = !state.context || state.context.tagName != tagName; | ||
} | ||
@@ -216,67 +220,53 @@ } else { | ||
if (err) setStyle = "error"; | ||
return cont(endclosetag(err)); | ||
return err ? closeStateErr : closeState; | ||
} else { | ||
return baseState; | ||
} | ||
return cont(); | ||
} | ||
function endtag(startOfLine) { | ||
return function(type) { | ||
var tagName = curState.tagName; | ||
curState.tagName = curState.tagStart = null; | ||
if (type == "selfcloseTag" || | ||
(type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { | ||
maybePopContext(tagName.toLowerCase()); | ||
return cont(); | ||
} | ||
if (type == "endTag") { | ||
maybePopContext(tagName.toLowerCase()); | ||
pushContext(tagName, startOfLine); | ||
return cont(); | ||
} | ||
return cont(); | ||
}; | ||
} | ||
function endclosetag(err) { | ||
return function(type) { | ||
if (err) setStyle = "error"; | ||
if (type == "endTag") { popContext(); return cont(); } | ||
function closeState(type, _stream, state) { | ||
if (type != "endTag") { | ||
setStyle = "error"; | ||
return cont(arguments.callee); | ||
}; | ||
return closeState; | ||
} | ||
popContext(state); | ||
return baseState; | ||
} | ||
function maybePopContext(nextTagName) { | ||
var parentTagName; | ||
while (true) { | ||
if (!curState.context) { | ||
return; | ||
function closeStateErr(type, stream, state) { | ||
setStyle = "error"; | ||
return closeState(type, stream, state); | ||
} | ||
function attrState(type, _stream, state) { | ||
if (type == "word") { | ||
setStyle = "attribute"; | ||
return attrEqState; | ||
} else if (type == "endTag" || type == "selfcloseTag") { | ||
var tagName = state.tagName, tagStart = state.tagStart; | ||
state.tagName = state.tagStart = null; | ||
if (type == "selfcloseTag" || | ||
Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase())) { | ||
maybePopContext(state, tagName.toLowerCase()); | ||
} else { | ||
maybePopContext(state, tagName.toLowerCase()); | ||
state.context = new Context(state, tagName, tagStart == state.indented); | ||
} | ||
parentTagName = curState.context.tagName.toLowerCase(); | ||
if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || | ||
!Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { | ||
return; | ||
} | ||
popContext(); | ||
return baseState; | ||
} | ||
} | ||
function attributes(type) { | ||
if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} | ||
if (type == "endTag" || type == "selfcloseTag") return pass(); | ||
setStyle = "error"; | ||
return cont(attributes); | ||
return attrState; | ||
} | ||
function attribute(type) { | ||
if (type == "equals") return cont(attvalue, attributes); | ||
function attrEqState(type, stream, state) { | ||
if (type == "equals") return attrValueState; | ||
if (!Kludges.allowMissing) setStyle = "error"; | ||
else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} | ||
return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); | ||
return attrState(type, stream, state); | ||
} | ||
function attvalue(type) { | ||
if (type == "string") return cont(attvaluemaybe); | ||
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} | ||
function attrValueState(type, stream, state) { | ||
if (type == "string") return attrContinuedState; | ||
if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;} | ||
setStyle = "error"; | ||
return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); | ||
return attrState(type, stream, state); | ||
} | ||
function attvaluemaybe(type) { | ||
if (type == "string") return cont(attvaluemaybe); | ||
else return pass(); | ||
function attrContinuedState(type, stream, state) { | ||
if (type == "string") return attrContinuedState; | ||
return attrState(type, stream, state); | ||
} | ||
@@ -286,25 +276,22 @@ | ||
startState: function() { | ||
return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null}; | ||
return {tokenize: inText, | ||
state: baseState, | ||
indented: 0, | ||
tagName: null, tagStart: null, | ||
context: null}; | ||
}, | ||
token: function(stream, state) { | ||
if (!state.tagName && stream.sol()) { | ||
state.startOfLine = true; | ||
if (!state.tagName && stream.sol()) | ||
state.indented = stream.indentation(); | ||
} | ||
if (stream.eatSpace()) return null; | ||
setStyle = type = tagName = null; | ||
tagName = type = null; | ||
var style = state.tokenize(stream, state); | ||
state.type = type; | ||
if ((style || type) && style != "comment") { | ||
curState = state; curStream = stream; | ||
while (true) { | ||
var comb = state.cc.pop() || element; | ||
if (comb(type || style)) break; | ||
} | ||
setStyle = null; | ||
state.state = state.state(type || style, stream, state); | ||
if (setStyle) | ||
style = setStyle == "error" ? style + " error" : setStyle; | ||
} | ||
state.startOfLine = false; | ||
if (setStyle) | ||
style = setStyle == "error" ? style + " error" : setStyle; | ||
return style; | ||
@@ -319,4 +306,4 @@ }, | ||
} | ||
if ((state.tokenize != inTag && state.tokenize != inText) || | ||
context && context.noIndent) | ||
if (context && context.noIndent) return CodeMirror.Pass; | ||
if (state.tokenize != inTag && state.tokenize != inText) | ||
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; | ||
@@ -323,0 +310,0 @@ // Indent the starts of attribute names. |
{ | ||
"name": "codemirror", | ||
"version":"3.20.0", | ||
"version":"3.21.0", | ||
"main": "lib/codemirror.js", | ||
@@ -10,3 +10,4 @@ "description": "In-browser code editing made bearable", | ||
"scripts": {"test": "node ./test/run.js"}, | ||
"devDependencies": {"node-static": "0.6.0"}, | ||
"devDependencies": {"node-static": "0.6.0", | ||
"phantomjs": "1.9.2-5"}, | ||
"bugs": "http://github.com/marijnh/CodeMirror/issues", | ||
@@ -19,3 +20,3 @@ "keywords": ["JavaScript", "CodeMirror", "Editor"], | ||
"repository": {"type": "git", | ||
"url": "http://marijnhaverbeke.nl/git/codemirror"} | ||
"url": "https://github.com/marijnh/CodeMirror.git"} | ||
} |
@@ -51,2 +51,14 @@ namespace = "comment_"; | ||
}, "a;\n\nb;", "a;\n// \nb;"); | ||
test("dontMessWithStrings", "javascript", function(cm) { | ||
cm.execCommand("toggleComment"); | ||
}, "console.log(\"/*string*/\");", "// console.log(\"/*string*/\");"); | ||
test("dontMessWithStrings2", "javascript", function(cm) { | ||
cm.execCommand("toggleComment"); | ||
}, "console.log(\"// string\");", "// console.log(\"// string\");"); | ||
test("dontMessWithStrings3", "javascript", function(cm) { | ||
cm.execCommand("toggleComment"); | ||
}, "// console.log(\"// string\");", "console.log(\"// string\");"); | ||
})(); |
@@ -60,3 +60,3 @@ (function() { | ||
} finally { | ||
if ((debug && !successful) || verbose) { | ||
if (!successful || verbose) { | ||
place.style.visibility = "visible"; | ||
@@ -63,0 +63,0 @@ } else { |
@@ -1,2 +0,2 @@ | ||
var tests = [], debug = null, debugUsed = new Array(), allNames = []; | ||
var tests = [], filters = [], allNames = []; | ||
@@ -35,3 +35,3 @@ function Failure(why) {this.message = why;} | ||
} finally { | ||
if ((debug && !successful) || verbose) { | ||
if (!successful || verbose) { | ||
place.style.visibility = "visible"; | ||
@@ -46,11 +46,2 @@ } else { | ||
function runTests(callback) { | ||
if (debug) { | ||
if (indexOf(debug, "verbose") === 0) { | ||
verbose = true; | ||
debug.splice(0, 1); | ||
} | ||
if (debug.length < 1) { | ||
debug = null; | ||
} | ||
} | ||
var totalTime = 0; | ||
@@ -61,21 +52,14 @@ function step(i) { | ||
return callback("done"); | ||
} | ||
} | ||
var test = tests[i], expFail = test.expectedFail, startTime = +new Date; | ||
if (debug !== null) { | ||
var debugIndex = indexOf(debug, test.name); | ||
if (debugIndex !== -1) { | ||
// Remove from array for reporting incorrect tests later | ||
debug.splice(debugIndex, 1); | ||
} else { | ||
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) return step(i + 1); | ||
if (filters.length) { | ||
for (var j = 0; j < filters.length; j++) { | ||
if (test.name.match(filters[j])) { | ||
break; | ||
} | ||
} | ||
if (j == filters.length) { | ||
callback("skipped", test.name, message); | ||
return step(i + 1); | ||
} | ||
} | ||
@@ -133,11 +117,19 @@ var threw = false; | ||
function countTests() { | ||
if (!debug) return tests.length; | ||
if (!filters.length) 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; | ||
for (var j = 0; j < filters.length; j++) { | ||
if (name.match(filters[j])) { | ||
++sum; | ||
break; | ||
} | ||
} | ||
} | ||
return sum; | ||
} | ||
function parseTestFilter(s) { | ||
if (/_\*$/.test(s)) return new RegExp("^" + s.slice(0, s.length - 2), "i"); | ||
else return new RegExp(s, "i"); | ||
} |
@@ -128,2 +128,5 @@ (function() { | ||
sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F", | ||
"Ctrl-G", "Ctrl-W", txt("abcde")); | ||
testCM("save", function(cm) { | ||
@@ -130,0 +133,0 @@ var saved = false; |
@@ -62,9 +62,2 @@ /** | ||
test.indentation = function(name, mode, tokens, modeName) { | ||
var data = parseTokens(tokens); | ||
return test((modeName || mode.name) + "_indent_" + name, function() { | ||
return compare(data.plain, data.tokens, mode, true); | ||
}); | ||
}; | ||
test.mode = function(name, mode, tokens, modeName) { | ||
@@ -77,4 +70,8 @@ var data = parseTokens(tokens); | ||
function compare(text, expected, mode, compareIndentation) { | ||
function esc(str) { | ||
return str.replace('&', '&').replace('<', '<'); | ||
} | ||
function compare(text, expected, mode) { | ||
var expectedOutput = []; | ||
@@ -87,42 +84,25 @@ for (var i = 0; i < expected.length; i += 2) { | ||
var observedOutput = highlight(text, mode, compareIndentation); | ||
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.replace('&', '&').replace('<', '<') + '</pre>'; | ||
var s = ""; | ||
var diff = highlightOutputsDifferent(expectedOutput, observedOutput); | ||
if (diff != null) { | ||
s += '<div class="mt-test mt-fail">'; | ||
s += '<pre>' + esc(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.replace('&', '&').replace('<', '<') + '</pre>'; | ||
s += '<div class="cm-s-default">'; | ||
s += 'expected:'; | ||
s += prettyPrintOutputTable(expectedOutput); | ||
s += prettyPrintOutputTable(expectedOutput, diff); | ||
s += 'observed:'; | ||
s += prettyPrintOutputTable(observedOutput); | ||
s += prettyPrintOutputTable(observedOutput, diff); | ||
s += '</div>'; | ||
s += '</div>'; | ||
throw s; | ||
} | ||
if (observedOutput.indentFailures) { | ||
for (var i = 0; i < observedOutput.indentFailures.length; i++) | ||
s += "<div class='mt-test mt-fail'>" + esc(observedOutput.indentFailures[i]) + "</div>"; | ||
} | ||
if (s) throw new Failure(s); | ||
} | ||
/** | ||
* 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, compareIndentation) { | ||
function highlight(string, mode) { | ||
var state = mode.startState() | ||
@@ -134,2 +114,9 @@ | ||
var line = lines[i], newLine = true; | ||
if (mode.indent) { | ||
var ws = line.match(/^\s*/)[0]; | ||
var indent = mode.indent(state, line.slice(ws.length)); | ||
if (indent != CodeMirror.Pass && indent != ws.length) | ||
(st.indentFailures || (st.indentFailures = [])).push( | ||
"Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")"); | ||
} | ||
var stream = new CodeMirror.StringStream(line); | ||
@@ -139,7 +126,5 @@ if (line == "" && mode.blankLine) mode.blankLine(state); | ||
while (!stream.eol()) { | ||
var compare = mode.token(stream, state), substr = stream.current(); | ||
if(compareIndentation) compare = mode.indent(state) || null; | ||
else if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' '); | ||
stream.start = stream.pos; | ||
var compare = mode.token(stream, state), substr = stream.current(); | ||
if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' '); | ||
stream.start = stream.pos; | ||
if (pos && st[pos-2] == compare && !newLine) { | ||
@@ -162,27 +147,10 @@ st[pos-1] += substr; | ||
/** | ||
* 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; | ||
function highlightOutputsDifferent(o1, o2) { | ||
var minLen = Math.min(o1.length, o2.length); | ||
for (var i = 0; i < minLen; ++i) | ||
if (o1[i] != o2[i]) return i >> 1; | ||
if (o1.length > minLen || o2.length > minLen) return minLen; | ||
} | ||
/** | ||
* Print tokens and corresponding styles in a table. Spaces in the token are | ||
* replaced with 'interpunct' dots (·). | ||
* | ||
* @param output array of [style, token] pairs | ||
* | ||
* @return html string | ||
*/ | ||
function prettyPrintOutputTable(output) { | ||
function prettyPrintOutputTable(output, diffAt) { | ||
var s = '<table class="mt-output">'; | ||
@@ -193,5 +161,5 @@ s += '<tr>'; | ||
s += | ||
'<td class="mt-token">' + | ||
'<span class="cm-' + String(style).replace(/ +/g, " cm-") + '">' + | ||
val.replace(/ /g,'\xb7').replace('&', '&').replace('<', '<') + | ||
'<td class="mt-token"' + (i == diffAt * 2 ? " style='background: pink'" : "") + '>' + | ||
'<span class="cm-' + esc(String(style)) + '">' + | ||
esc(val.replace(/ /g,'\xb7')) + | ||
'</span>' + | ||
@@ -198,0 +166,0 @@ '</td>'; |
@@ -12,24 +12,28 @@ #!/usr/bin/env node | ||
var files = new (require('node-static').Server)('.'); | ||
var files = new (require('node-static').Server)(); | ||
var server = require('http').createServer(function (req, res) { | ||
req.addListener('end', function () { | ||
files.serve(req, res); | ||
}); | ||
files.serve(req, res, function (err/*, result */) { | ||
if (err) { | ||
console.error(err); | ||
process.exit(1); | ||
} | ||
}); | ||
}).resume(); | ||
}).addListener('error', function (err) { | ||
throw err; | ||
}).listen(3000, function () { | ||
var child_process = require('child_process'); | ||
child_process.exec("which phantomjs", function (err) { | ||
if (err) { | ||
console.error("PhantomJS is not installed. Download from http://phantomjs.org"); | ||
process.exit(1); | ||
} | ||
var cmd = 'phantomjs test/phantom_driver.js'; | ||
child_process.exec(cmd, function (err, stdout) { | ||
server.close(); | ||
console.log(stdout); | ||
process.exit(err || !ok ? 1 : 0); | ||
}); | ||
var childProcess = require('child_process'); | ||
var phantomjs = require("phantomjs"); | ||
var childArgs = [ | ||
require("path").join(__dirname, 'phantom_driver.js') | ||
]; | ||
childProcess.execFile(phantomjs.path, childArgs, function (err, stdout, stderr) { | ||
server.close(); | ||
console.log(stdout); | ||
if (err) console.error(err); | ||
if (stderr) console.error(stderr); | ||
process.exit(err || stderr || !ok ? 1 : 0); | ||
}); | ||
}); |
105
test/test.js
@@ -439,2 +439,32 @@ var Pos = CodeMirror.Pos; | ||
testCM("markTextAllowEmpty", function(cm) { | ||
var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false}); | ||
is(m1.find()); | ||
cm.replaceRange("x", Pos(0, 0)); | ||
is(m1.find()); | ||
cm.replaceRange("y", Pos(0, 2)); | ||
is(m1.find()); | ||
cm.replaceRange("z", Pos(0, 3), Pos(0, 4)); | ||
is(!m1.find()); | ||
var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false, | ||
inclusiveLeft: true, | ||
inclusiveRight: true}); | ||
cm.replaceRange("q", Pos(0, 1), Pos(0, 2)); | ||
is(m2.find()); | ||
cm.replaceRange("", Pos(0, 0), Pos(0, 3)); | ||
is(!m2.find()); | ||
var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false}); | ||
cm.replaceRange("a", Pos(0, 3)); | ||
is(m3.find()); | ||
cm.replaceRange("b", Pos(0, 1)); | ||
is(!m3.find()); | ||
}, {value: "abcde"}); | ||
testCM("markTextStacked", function(cm) { | ||
var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false}); | ||
var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false}); | ||
cm.replaceRange("B", Pos(0, 1)); | ||
is(m1.find() && m2.find()); | ||
}, {value: "A"}); | ||
testCM("undoPreservesNewMarks", function(cm) { | ||
@@ -716,4 +746,4 @@ cm.markText(Pos(0, 3), Pos(0, 4)); | ||
m1.clear(); | ||
var m1 = cm.markText(Pos(0, 0), Pos(1, 1), opts); | ||
var m2 = cm.markText(Pos(1, 1), Pos(2, 0), opts); | ||
var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true}); | ||
var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true}); | ||
eqPos(cm.coordsChar(pos_1_3), Pos(3, 3)); | ||
@@ -846,2 +876,19 @@ m1.clear(); m2.clear(); | ||
testCM("nestedFoldOnSide", function(cm) { | ||
var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true}); | ||
var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}); | ||
cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear(); | ||
try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); } | ||
catch(e) { var caught = e; } | ||
is(caught && /overlap/i.test(caught.message)); | ||
var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}); | ||
var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true}); | ||
m1.clear(); m4.clear(); | ||
m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true}); | ||
cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear(); | ||
try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); } | ||
catch(e) { var caught = e; } | ||
is(caught && /overlap/i.test(caught.message)); | ||
}, {value: "ab\ncd\ef"}); | ||
testCM("wrappingInlineWidget", function(cm) { | ||
@@ -979,2 +1026,20 @@ cm.setSize("11em"); | ||
testCM("collapseOnMove", function(cm) { | ||
cm.setSelection(Pos(0, 1), Pos(2, 4)); | ||
cm.execCommand("goLineUp"); | ||
is(!cm.somethingSelected()); | ||
eqPos(cm.getCursor(), Pos(0, 1)); | ||
cm.setSelection(Pos(0, 1), Pos(2, 4)); | ||
cm.execCommand("goPageDown"); | ||
is(!cm.somethingSelected()); | ||
eqPos(cm.getCursor(), Pos(2, 4)); | ||
cm.execCommand("goLineUp"); | ||
cm.execCommand("goLineUp"); | ||
eqPos(cm.getCursor(), Pos(0, 4)); | ||
cm.setSelection(Pos(0, 1), Pos(2, 4)); | ||
cm.execCommand("goCharLeft"); | ||
is(!cm.somethingSelected()); | ||
eqPos(cm.getCursor(), Pos(0, 1)); | ||
}, {value: "aaaaa\nb\nccccc"}); | ||
testCM("clickTab", function(cm) { | ||
@@ -1148,3 +1213,3 @@ var p0 = cm.charCoords(Pos(0, 0)); | ||
forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", | ||
"خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", | ||
"خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق", | ||
"<img src=\"/בדיקה3.jpg\">"], function(line) { | ||
@@ -1422,2 +1487,17 @@ var inv = line.charAt(0) == "خ"; | ||
testCM("changeGeneration", function(cm) { | ||
cm.replaceSelection("x", null, "+insert"); | ||
var softGen = cm.changeGeneration(); | ||
cm.replaceSelection("x", null, "+insert"); | ||
cm.undo(); | ||
eq(cm.getValue(), ""); | ||
is(!cm.isClean(softGen)); | ||
cm.replaceSelection("x", null, "+insert"); | ||
var hardGen = cm.changeGeneration(true); | ||
cm.replaceSelection("x", null, "+insert"); | ||
cm.undo(); | ||
eq(cm.getValue(), "x"); | ||
is(cm.isClean(hardGen)); | ||
}); | ||
testCM("addKeyMap", function(cm) { | ||
@@ -1569,1 +1649,20 @@ function sendKey(code) { | ||
}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: nothing"}); | ||
CodeMirror.registerHelper("xxx", "a", "A"); | ||
CodeMirror.registerHelper("xxx", "b", "B"); | ||
CodeMirror.defineMode("yyy", function() { | ||
return { | ||
token: function(stream) { stream.skipToEnd(); }, | ||
xxx: ["a", "b", "q"] | ||
}; | ||
}); | ||
CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C"); | ||
testCM("helpers", function(cm) { | ||
cm.setOption("mode", "yyy"); | ||
eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B"); | ||
cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}}); | ||
eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C"); | ||
cm.setOption("mode", "javascript"); | ||
eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), ""); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2466502
341
40490
2