diff --git a/AUTHORS b/AUTHORS
index ad2ec99576..260c13a864 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -84,6 +84,7 @@ Ford_Lawnmower
Gabriel Nahmias
galambalazs
Gautam Mehta
+Glenn Jorde
Glenn Ruehle
Golevka
Gordon Smith
diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js
index 0bc3e8be17..d6a8fafd3c 100644
--- a/addon/edit/closetag.js
+++ b/addon/edit/closetag.js
@@ -72,7 +72,7 @@
function autoCloseSlash(cm) {
var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
- if (tok.string.charAt(0) != "<" || inner.mode.name != "xml") return CodeMirror.Pass;
+ if (tok.string.charAt(0) != "<" || tok.start != pos.ch - 1 || inner.mode.name != "xml") return CodeMirror.Pass;
var tagName = state.context && state.context.tagName;
if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js
index fb1fc38ba2..826d17d716 100644
--- a/addon/edit/continuelist.js
+++ b/addon/edit/continuelist.js
@@ -6,7 +6,7 @@
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
var pos = cm.getCursor(),
- inList = cm.getStateAfter(pos.line).list,
+ inList = cm.getStateAfter(pos.line).list !== false,
match;
if (!inList || !(match = cm.getLine(pos.line).match(listRE))) {
diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js
index 931c3726cc..c497bc29b6 100644
--- a/addon/fold/foldcode.js
+++ b/addon/fold/foldcode.js
@@ -1,7 +1,7 @@
(function() {
"use strict";
- function doFold(cm, pos, options) {
+ function doFold(cm, pos, options, force) {
var finder = options && (options.call ? options : options.rangeFinder);
if (!finder) finder = cm.getHelper(pos, "fold");
if (!finder) return;
@@ -13,7 +13,7 @@
if (!range || range.to.line - range.from.line < minSize) return null;
var marks = cm.findMarksAt(range.from);
for (var i = 0; i < marks.length; ++i) {
- if (marks[i].__isFold) {
+ if (marks[i].__isFold && force !== "fold") {
if (!allowFolded) return null;
range.cleared = true;
marks[i].clear();
@@ -27,7 +27,7 @@
pos = CodeMirror.Pos(pos.line - 1, 0);
range = getRange(false);
}
- if (!range || range.cleared) return;
+ if (!range || range.cleared || force === "unfold") return;
var myWidget = makeWidget(options);
CodeMirror.on(myWidget, "mousedown", function() { myRange.clear(); });
@@ -59,7 +59,9 @@
};
// New-style interface
- CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); });
+ CodeMirror.defineExtension("foldCode", function(pos, options, force) {
+ doFold(this, pos, options, force);
+ });
CodeMirror.registerHelper("fold", "combine", function() {
var funcs = Array.prototype.slice.call(arguments, 0);
diff --git a/addon/fold/foldgutter.css b/addon/fold/foldgutter.css
new file mode 100644
index 0000000000..49805393d0
--- /dev/null
+++ b/addon/fold/foldgutter.css
@@ -0,0 +1,21 @@
+.CodeMirror-foldmarker {
+ color: blue;
+ text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
+ font-family: arial;
+ line-height: .3;
+ cursor: pointer;
+}
+.CodeMirror-foldgutter {
+ width: .7em;
+}
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+ color: #555;
+ cursor: pointer;
+}
+.CodeMirror-foldgutter-open:after {
+ content: "\25BE";
+}
+.CodeMirror-foldgutter-folded:after {
+ content: "\25B8";
+}
diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js
index b809c93f56..e3c52bc229 100644
--- a/addon/fold/foldgutter.js
+++ b/addon/fold/foldgutter.js
@@ -10,6 +10,7 @@
cm.off("viewportChange", onViewportChange);
cm.off("fold", onFold);
cm.off("unfold", onFold);
+ cm.off("swapDoc", updateInViewport);
}
if (val) {
cm.state.foldGutter = new State(parseOptions(val));
@@ -19,6 +20,7 @@
cm.on("viewportChange", onViewportChange);
cm.on("fold", onFold);
cm.on("unfold", onFold);
+ cm.on("swapDoc", updateInViewport);
}
});
diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js
index fcbff96696..b54da34777 100644
--- a/addon/fold/indent-fold.js
+++ b/addon/fold/indent-fold.js
@@ -1,12 +1,26 @@
CodeMirror.registerHelper("fold", "indent", function(cm, start) {
- var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
- var myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
- for (var i = start.line + 1, end = cm.lineCount(); i < end; ++i) {
- var curLine = cm.getLine(i);
- if (CodeMirror.countColumn(curLine, null, tabSize) < myIndent &&
- CodeMirror.countColumn(cm.getLine(i-1), null, tabSize) > myIndent)
+ var lastLine = cm.lastLine(),
+ tabSize = cm.getOption("tabSize"),
+ firstLine = cm.getLine(start.line),
+ myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
+
+ function foldEnded(curColumn, prevColumn) {
+ return curColumn < myIndent ||
+ (curColumn == myIndent && prevColumn >= myIndent) ||
+ (curColumn > myIndent && i == lastLine);
+ }
+
+ for (var i = start.line + 1; i <= lastLine; i++) {
+ var curColumn = CodeMirror.countColumn(cm.getLine(i), null, tabSize);
+ var prevColumn = CodeMirror.countColumn(cm.getLine(i-1), null, tabSize);
+
+ if (foldEnded(curColumn, prevColumn)) {
+ var lastFoldLineNumber = curColumn > myIndent && i == lastLine ? i : i-1;
+ var lastFoldLine = cm.getLine(lastFoldLineNumber);
return {from: CodeMirror.Pos(start.line, firstLine.length),
- to: CodeMirror.Pos(i, curLine.length)};
+ to: CodeMirror.Pos(lastFoldLineNumber, lastFoldLine.length)};
+ }
}
});
+
CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated
diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js
index 042fe1325d..513fb782b0 100644
--- a/addon/hint/javascript-hint.js
+++ b/addon/hint/javascript-hint.js
@@ -33,21 +33,6 @@
tprop = getToken(editor, Pos(cur.line, tprop.start));
if (tprop.string != ".") return;
tprop = getToken(editor, Pos(cur.line, tprop.start));
- if (tprop.string == ')') {
- var level = 1;
- do {
- tprop = getToken(editor, Pos(cur.line, tprop.start));
- switch (tprop.string) {
- case ')': level++; break;
- case '(': level--; break;
- default: break;
- }
- } while (level > 0);
- tprop = getToken(editor, Pos(cur.line, tprop.start));
- if (tprop.type.indexOf("variable") === 0)
- tprop.type = "function";
- else return; // no clue
- }
if (!context) var context = [];
context.push(tprop);
}
@@ -110,11 +95,11 @@
for (var name in obj) maybeAdd(name);
}
- if (context) {
+ if (context && context.length) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base;
- if (obj.type.indexOf("variable") === 0) {
+ if (obj.type && obj.type.indexOf("variable") === 0) {
if (options && options.additionalContext)
base = options.additionalContext[obj.string];
base = base || window[obj.string];
@@ -132,8 +117,7 @@
while (base != null && context.length)
base = base[context.pop().string];
if (base != null) gatherCompletions(base);
- }
- else {
+ } else {
// If not, just look in the window object and any local scope
// (reading into JS mode internals to get at the local and global variables)
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js
new file mode 100644
index 0000000000..95f6b50a15
--- /dev/null
+++ b/addon/hint/sql-hint.js
@@ -0,0 +1,105 @@
+(function () {
+ "use strict";
+
+ var tables;
+ var keywords;
+
+ function getKeywords(editor) {
+ var mode = editor.doc.modeOption;
+ if(mode === "sql") mode = "text/x-sql";
+ return CodeMirror.resolveMode(mode).keywords;
+ }
+
+ function match(string, word) {
+ var len = string.length;
+ var sub = word.substr(0, len);
+ return string.toUpperCase() === sub.toUpperCase();
+ }
+
+ function addMatches(result, search, wordlist, formatter) {
+ for(var word in wordlist) {
+ if(!wordlist.hasOwnProperty(word)) continue;
+ if(Array.isArray(wordlist)) {
+ word = wordlist[word];
+ }
+ if(match(search, word)) {
+ result.push(formatter(word));
+ }
+ }
+ }
+
+ function columnCompletion(result, editor) {
+ var cur = editor.getCursor();
+ var token = editor.getTokenAt(cur);
+ var string = token.string.substr(1);
+ var prevCur = CodeMirror.Pos(cur.line, token.start);
+ var table = editor.getTokenAt(prevCur).string;
+ var columns = tables[table];
+ if(!columns) {
+ table = findTableByAlias(table, editor);
+ }
+ columns = tables[table];
+ if(!columns) {
+ return;
+ }
+ addMatches(result, string, columns,
+ function(w) {return "." + w;});
+ }
+
+ function eachWord(line, f) {
+ var words = line.text.split(" ");
+ for(var i = 0; i < words.length; i++) {
+ f(words[i]);
+ }
+ }
+
+ // Tries to find possible table name from alias.
+ function findTableByAlias(alias, editor) {
+ var aliasUpperCase = alias.toUpperCase();
+ var previousWord = "";
+ var table = "";
+
+ editor.eachLine(function(line) {
+ eachWord(line, function(word) {
+ var wordUpperCase = word.toUpperCase();
+ if(wordUpperCase === aliasUpperCase) {
+ if(tables.hasOwnProperty(previousWord)) {
+ table = previousWord;
+ }
+ }
+ if(wordUpperCase !== "AS") {
+ previousWord = word;
+ }
+ });
+ });
+ return table;
+ }
+
+ function sqlHint(editor, options) {
+ tables = (options && options.tables) || {};
+ keywords = keywords || getKeywords(editor);
+ var cur = editor.getCursor();
+ var token = editor.getTokenAt(cur);
+
+ var result = [];
+
+ var search = token.string.trim();
+
+ addMatches(result, search, keywords,
+ function(w) {return w.toUpperCase();});
+
+ addMatches(result, search, tables,
+ function(w) {return w;});
+
+ if(search.lastIndexOf('.') === 0) {
+ columnCompletion(result, editor);
+ }
+
+ return {
+ list: result,
+ from: CodeMirror.Pos(cur.line, token.start),
+ to: CodeMirror.Pos(cur.line, token.end)
+ };
+ }
+ CodeMirror.registerHelper("hint", "sql", sqlHint);
+})();
diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js
index b6c1da2ce2..a721743449 100644
--- a/addon/hint/xml-hint.js
+++ b/addon/hint/xml-hint.js
@@ -37,6 +37,7 @@
Pos(cur.line, token.type == "string" ? token.start : token.end));
var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
+ if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
if (token.type == "string") {
prefix = token.string;
if (/['"]/.test(token.string.charAt(0))) {
diff --git a/addon/lint/lint.css b/addon/lint/lint.css
index e592b3672a..414a9a0e06 100644
--- a/addon/lint/lint.css
+++ b/addon/lint/lint.css
@@ -14,6 +14,7 @@
padding: 2px 5px;
position: fixed;
white-space: pre;
+ white-space: pre-wrap;
z-index: 100;
max-width: 600px;
opacity: 0;
diff --git a/addon/search/search.js b/addon/search/search.js
index c30922df4f..71ed75b500 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -70,6 +70,7 @@
if (!cursor.find(rev)) return;
}
cm.setSelection(cursor.from(), cursor.to());
+ cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
state.posFrom = cursor.from(); state.posTo = cursor.to();
});}
function clearSearch(cm) {cm.operation(function() {
@@ -108,6 +109,7 @@
(start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
}
cm.setSelection(cursor.from(), cursor.to());
+ cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
confirmDialog(cm, doReplaceConfirm, "Replace?",
[function() {doReplace(match);}, advance]);
};
diff --git a/addon/tern/tern.js b/addon/tern/tern.js
index 29ccc94886..fe93fa7335 100644
--- a/addon/tern/tern.js
+++ b/addon/tern/tern.js
@@ -439,7 +439,7 @@
for (var file in perFile) {
var known = ts.docs[file], chs = perFile[file];;
if (!known) continue;
- chs.sort(function(a, b) { return cmpPos(b, a); });
+ chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
var origin = "*rename" + (++nextChangeOrig);
for (var i = 0; i < chs.length; ++i) {
var ch = chs[i];
diff --git a/addon/wrap/hardwrap.js b/addon/wrap/hardwrap.js
new file mode 100644
index 0000000000..f252ffc9e9
--- /dev/null
+++ b/addon/wrap/hardwrap.js
@@ -0,0 +1,99 @@
+(function() {
+ "use strict";
+
+ var Pos = CodeMirror.Pos;
+
+ function findParagraph(cm, pos, options) {
+ var startRE = options.paragraphStart || cm.getHelper(pos, "paragraphStart");
+ for (var start = pos.line, first = cm.firstLine(); start > first; --start) {
+ var line = cm.getLine(start);
+ if (startRE && startRE.test(line)) break;
+ if (!/\S/.test(line)) { ++start; break; }
+ }
+ var endRE = options.paragraphEnd || cm.getHelper(pos, "paragraphEnd");
+ for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {
+ var line = cm.getLine(end);
+ if (endRE && endRE.test(line)) { ++end; break; }
+ if (!/\S/.test(line)) break;
+ }
+ return {from: start, to: end};
+ }
+
+ function findBreakPoint(text, column, wrapOn, killTrailingSpace) {
+ for (var at = column; at > 0; --at)
+ if (wrapOn.test(text.slice(at - 1, at + 1))) break;
+ if (at == 0) at = column;
+ var endOfText = at;
+ if (killTrailingSpace)
+ while (text.charAt(endOfText - 1) == " ") --endOfText;
+ return {from: endOfText, to: at};
+ }
+
+ function wrapRange(cm, from, to, options) {
+ from = cm.clipPos(from); to = cm.clipPos(to);
+ var column = options.column || 80;
+ var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/;
+ var killTrailing = options.killTrailingSpace !== false;
+ var changes = [], curLine = "", curNo = from.line;
+ var lines = cm.getRange(from, to, false);
+ for (var i = 0; i < lines.length; ++i) {
+ var text = lines[i], oldLen = curLine.length, spaceInserted = 0;
+ if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {
+ curLine += " ";
+ spaceInserted = 1;
+ }
+ curLine += text;
+ if (i) {
+ var firstBreak = curLine.length > column && 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 ? " " : "",
+ from: Pos(curNo, oldLen),
+ to: Pos(curNo + 1, 0)});
+ } else {
+ curLine = text;
+ ++curNo;
+ }
+ }
+ while (curLine.length > column) {
+ var bp = findBreakPoint(curLine, column, wrapOn, killTrailing);
+ changes.push({text: "\n",
+ from: Pos(curNo, bp.from),
+ to: Pos(curNo, bp.to)});
+ curLine = curLine.slice(bp.to);
+ ++curNo;
+ }
+ }
+ if (changes.length) cm.operation(function() {
+ for (var i = 0; i < changes.length; ++i) {
+ var change = changes[i];
+ cm.replaceRange(change.text, change.from, change.to);
+ }
+ });
+ }
+
+ CodeMirror.defineExtension("wrapParagraph", function(pos, options) {
+ options = options || {};
+ if (!pos) pos = this.getCursor();
+ var para = findParagraph(this, pos, options);
+ wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options);
+ });
+
+ CodeMirror.defineExtension("wrapRange", function(from, to, options) {
+ wrapRange(this, from, to, options || {});
+ });
+
+ CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) {
+ options = options || {};
+ var cm = this, paras = [];
+ for (var line = from.line; line <= to.line;) {
+ var para = findParagraph(cm, Pos(line, 0), options);
+ paras.push(para);
+ line = para.to;
+ }
+ 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);
+ });
+ });
+})();
diff --git a/bower.json b/bower.json
index 8259431c4d..66e049dfbe 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,5 @@
{
"name": "CodeMirror",
- "version": "3.18.0",
"main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [
"**/.*",
diff --git a/demo/folding.html b/demo/folding.html
index 29ddb15cb1..9461801277 100644
--- a/demo/folding.html
+++ b/demo/folding.html
@@ -5,6 +5,7 @@
+
@@ -15,28 +16,7 @@
+
@@ -56,7 +36,7 @@
JavaScript:
HTML:
-
+
+
+
+
+
+
+
+
+
+
+
+Hard-wrapping Demo
+
+
+Demonstration of
+the hardwrap addon.
+The above editor has its change event hooked up to
+the wrapParagraphsInRange method, so that the paragraphs
+are reflown as you are typing.
+
+
+
+
diff --git a/demo/theme.html b/demo/theme.html
index 504673d865..a6e2b2da09 100644
--- a/demo/theme.html
+++ b/demo/theme.html
@@ -16,6 +16,7 @@
+
@@ -80,6 +81,7 @@
elegant
erlang-dark
lesser-dark
+
mbo
midnight
monokai
neat
diff --git a/doc/compress.html b/doc/compress.html
index a3e3792a18..e01001d2bb 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -33,6 +33,7 @@
Version:
HEAD
+ 3.19
3.18
3.16
3.15
@@ -89,10 +90,12 @@
diff.js
dtd.js
ecl.js
+ eiffel.js
erlang.js
fortran.js
gfm.js
gas.js
+ gherkin.js
go.js
groovy.js
haml.js
@@ -166,6 +169,7 @@
foldcode.js
foldgutter.js
fullscreen.js
+ hardwrap.js
html-hint.js
indent-fold.js
javascript-hint.js
@@ -189,6 +193,7 @@
search.js
searchcursor.js
show-hint.js
+ sql-hint.js
trailingspace.js
tern.js
xml-fold.js
diff --git a/doc/docs.css b/doc/docs.css
index d1ab21bad9..c56289346c 100644
--- a/doc/docs.css
+++ b/doc/docs.css
@@ -30,6 +30,10 @@ a, a:visited, a:link, .quasilink {
text-decoration: none;
}
+em {
+ padding-right: 2px;
+}
+
.quasilink {
cursor: pointer;
}
diff --git a/doc/manual.html b/doc/manual.html
index 9f516b7e05..e5a000a5c6 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -14,9 +14,10 @@
@@ -358,6 +359,11 @@
which causes the cursor to not reach all the way to the bottom
of the line, looks better
+
+
Controls whether, when the context menu is opened with a
+ click outside of the current selection, the cursor is moved to
+ the point of the click. Defaults to true.
+
workTime , workDelay : number
Highlighting is done by a pseudo background-thread that will
work for workTime milliseconds, and then use
@@ -506,6 +512,11 @@
factor). The from and to arguments
give the new start and end of the viewport.
+
"swapDoc" (instance: CodeMirror, oldDoc: Doc)
+
This is signalled when the editor's document is replaced
+ using the swapDoc
+ method.
+
"gutterClick" (instance: CodeMirror, line: integer, gutter: string, clickEvent: Event)
Fires when the editor gutter (the line-number area) is
clicked. Will pass the editor instance as first argument, the
@@ -1373,7 +1384,7 @@ Sizing, scrolling and positioning methods
cm.setSize (width: number|string, height: number|string)
Programatically set the size of the editor (overriding the
applicable CSS
- rules ). width and height height
+ rules). width and height
can be either numbers (interpreted as pixels) or CSS units
("100%", for example). You can
pass null for either of them to indicate that that
@@ -1388,14 +1399,16 @@ Sizing, scrolling and positioning methods
clientHeight} object that represents the current scroll
position, the size of the scrollable area, and the size of the
visible area (minus scrollbars).
-
cm.scrollIntoView (pos: {line, ch}|{left, top, right, bottom}, ?margin: number)
-
Scrolls the given element into view. pos may be
- either a {line, ch} position, referring to a given
- character, null, to refer to the cursor, or
- a {left, top, right, bottom} object, in
- editor-local coordinates. The margin parameter is
- optional. When given, it indicates the amount of pixels around
- the given area that should be made visible as well.
+
cm.scrollIntoView (what: {line, ch}|{left, top, right, bottom}|{from, to}|null, ?margin: number)
+
Scrolls the given position into view. what may
+ be null to scroll the cursor into view,
+ a {line, ch} position to scroll a character into
+ view, a {left, top, right, bottom} pixel range (in
+ editor-local coordinates), or a range {from, to}
+ containing either two character positions or two pixel squares.
+ The margin parameter is optional. When given, it
+ indicates the amount of vertical pixels around the given area
+ that should be made visible as well.
cm.cursorCoords (where: boolean|{line, ch}, mode: string) → {left, top, bottom}
Returns an {left, top, bottom} object
@@ -1760,7 +1773,7 @@ Static properties
edit/matchtags.js
Defines an option matchTags that, when enabled,
will cause the tags around the cursor to be highlighted (using
- the CodeMirror-matchingbrackets class). Also
+ the CodeMirror-matchingtag class). Also
defines
a command toMatchingTag,
which you can bind a key to in order to jump to the tag mathing
@@ -1775,6 +1788,19 @@ Static properties
The demo has a nice
squiggly underline style for this class.
+
edit/closetag.js
+
Provides utility functions for adding automatic tag closing
+ to XML modes. See
+ the demo .
+
+
edit/continuelist.js
+
Markdown specific. Defines
+ a "newlineAndIndentContinueMarkdownList" command
+ command that can be bound to enter to automatically
+ insert the leading characters for continuing a list. See
+ the Markdown mode
+ demo .
+
Addon for commenting and uncommenting code. Adds three
methods to CodeMirror instances:
@@ -1866,7 +1892,14 @@ Static properties
giving it the class CodeMirror-foldgutter or
something else if you configure the addon to use a different
class, and this addon will show markers next to folded and
- foldable blocks, and handle clicks in this gutter. The option
+ foldable blocks, and handle clicks in this gutter. Note that
+ CSS styles should be applied to make the gutter, and the fold
+ markers within it, visible. A default set of CSS styles are
+ available in:
+
+ addon/fold/foldgutter.css
+ .
+ The option
can be either set to true, or an object containing
the following optional option fields:
@@ -1874,7 +1907,7 @@ Static properties
The CSS class of the gutter. Defaults
to "CodeMirror-foldgutter". You will have to
style this yourself to give it a width (and possibly a
- background).
+ background). See the default gutter style rules above.
indicatorOpen : string | Element
A CSS class or DOM element to be used as the marker for
open, foldable blocks. Defaults
@@ -1900,6 +1933,22 @@ Static properties
for running under
node.js .
+
runmode/colorize.js
+
Provides a convenient way to syntax-highlight code snippets
+ in a webpage. Depends on
+ the runmode addon (or
+ its standalone variant). Provides
+ a CodeMirror.colorize function that can be called
+ with an array (or other array-ish collection) of DOM nodes that
+ represent the code snippets. By default, it'll get
+ all pre tags. Will read the data-lang
+ attribute of these nodes to figure out their language, and
+ syntax-color their content using the relevant CodeMirror mode
+ (you'll have to load the scripts for the relevant modes
+ yourself). A second argument may be provided to give a default
+ mode, used when no language attribute is found for a node. Used
+ in this manual to highlight example code.
+
mode/overlay.js
Mode combinator that can be used to extend a mode with an
'overlay' — a secondary mode is run over the stream, along with
@@ -2066,7 +2115,13 @@ Static properties
and range, which defines how many lines the addon
should scan when completing (defaults to 500).
-
match-highlighter.js
+
hint/sql-hint.js
+
A simple SQL hinter. Defines CodeMirror.hint.sql.
+
+
hint/pig-hint.js
+
A simple hinter for Pig Latin . Defines CodeMirror.hint.pig.
+
+
search/match-highlighter.js
Adds a highlightSelectionMatches option that
can be enabled to highlight all instances of a currently
selected word. Can be set either to true or to an object
@@ -2085,8 +2140,9 @@ Static properties
lint/lint.js
Defines an interface component for showing linting warnings,
with pluggable warning sources
- (see json-lint.js
- and javascript-lint.js
+ (see json-lint.js ,
+ javascript-lint.js ,
+ and css-lint.js
in the same directory). Defines a lint option that
can be set to a warning source (for
example CodeMirror.lint.javascript), or
@@ -2108,11 +2164,6 @@ Static properties
and adds a background with the class CodeMirror-activeline-background.
is enabled. See the demo .
-
edit/closetag.js
-
Provides utility functions for adding automatic tag closing
- to XML modes. See
- the demo .
-
mode/loadmode.js
Defines a CodeMirror.requireMode(modename,
callback) function that will try to load a given mode and
@@ -2147,6 +2198,39 @@ Static properties
on fullscreen.css . Demo
here .
+
wrap/hardwrap.js
+
Addon to perform hard line wrapping/breaking for paragraphs
+ of text. Adds these methods to editor instances:
+
+ wrapParagraph (?pos: {line, ch}, ?options: object)
+ Wraps the paragraph at the given position.
+ If pos is not given, it defaults to the cursor
+ position.
+ wrapRange (from: {line, ch}, to: {line, ch}, ?options: object)
+ Wraps the given range as one big paragraph.
+ wrapParagraphsInRange (from: {line, ch}, to: {line, ch}, ?options: object)
+ Wrapps the paragraphs in (and overlapping with) the
+ given range individually.
+
+ The following options are recognized:
+
+ paragraphStart , paragraphEnd : RegExp
+ Blank lines are always considered paragraph boundaries.
+ These options can be used to specify a pattern that causes
+ lines to be considered the start or end of a paragraph.
+ column : number
+ The column to wrap at. Defaults to 80.
+ wrapOn : RegExp
+ A regular expression that matches only those
+ two-character strings that allow wrapping. By default, the
+ addon wraps on whitespace and after dash characters.
+ killTrailingSpace : boolean
+ Whether trailing space caused by wrapping should be
+ preserved, or deleted. Defaults to true.
+
+ A demo of the addon is available here .
+
+
merge/merge.js
Implements an interface for merging changes, using either a
2-way or a 3-way view. The CodeMirror.MergeView
@@ -2160,6 +2244,18 @@ Static properties
editor in non-editable CodeMirror instances. The merge interface
will highlight changes between the editable document and the
original(s) (demo ).
+
+
tern/tern.js
+
Provides integration with
+ the Tern JavaScript analysis
+ engine, for completion, definition finding, and minor
+ refactoring help. See the demo
+ for a very simple integration. For more involved scenarios, see
+ the comments at the top of
+ the addon and the
+ implementation of the
+ (multi-file) demonstration
+ on the Tern website .
@@ -2173,15 +2269,15 @@
Static properties
advanced modes can also handle indentation for the language.
The mode script should
- call CodeMirror.defineMode to register itself with
- CodeMirror. This function takes two arguments. The first should be
- the name of the mode, for which you should use a lowercase string,
- preferably one that is also the name of the files that define the
- mode (i.e. "xml" is defined in xml.js). The
- second argument should be a function that, given a CodeMirror
- configuration object (the thing passed to
- the CodeMirror function) and an optional mode
- configuration object (as in
+ call CodeMirror.defineMode to
+ register itself with CodeMirror. This function takes two
+ arguments. The first should be the name of the mode, for which you
+ should use a lowercase string, preferably one that is also the
+ name of the files that define the mode (i.e. "xml" is
+ defined in xml.js). The second argument should be a
+ function that, given a CodeMirror configuration object (the thing
+ passed to the CodeMirror function) and an optional
+ mode configuration object (as in
the mode option), returns
a mode object.
@@ -2201,30 +2297,43 @@
Static properties
reading a token, and which can be mutated by the tokenizer.
Modes that use a state must define
- a startState method on their mode object. This is a
- function of no arguments that produces a state object to be used
- at the start of a document.
+ a
startState method on their mode
+ object. This is a function of no arguments that produces a state
+ object to be used at the start of a document.
The most important part of a mode object is
- its token(stream, state) method. All modes must
- define this method. It should read one token from the stream it is
- given as an argument, optionally update its state, and return a
- style string, or null for tokens that do not have to
- be styled. For your styles, you are encouraged to use the
- 'standard' names defined in the themes (without
+ its token (stream, state) method. All
+ modes must define this method. It should read one token from the
+ stream it is given as an argument, optionally update its state,
+ and return a style string, or null for tokens that do
+ not have to be styled. For your styles, you are encouraged to use
+ the 'standard' names defined in the themes (without
the cm- prefix). If that fails, it is also possible
to come up with your own and write your own CSS theme file.
+
A typical token string would
+ be "variable" or "comment". Multiple
+ styles can be returned (separated by spaces), for
+ example "string error" for a thing that looks like a
+ string but is invalid somehow (say, missing its closing quote).
+ When a style is prefixed by "line-"
+ or "line-background-", the style will be applied to
+ the whole line, analogous to what
+ the addLineClass method
+ does—styling the "text" in the simple case, and
+ the "background" element
+ when "line-background-" is prefixed.
+
The stream object that's passed
to token encapsulates a line of code (tokens may
never span lines) and our current position in that line. It has
the following API:
- eol () → boolean
+ eol () → boolean
Returns true only if the stream is at the end of the
line.
- sol () → boolean
+ sol () → boolean
Returns true only if the stream is at the start of the
line.
@@ -2287,9 +2396,10 @@ Static properties
By default, blank lines are simply skipped when
tokenizing a document. For languages that have significant blank
- lines, you can define a blankLine(state) method on
- your mode that will get called whenever a blank line is passed
- over, so that it can update the parser state.
+ lines, you can define
+ a blankLine (state) method on your
+ mode that will get called whenever a blank line is passed over, so
+ that it can update the parser state.
Because state object are mutated, and CodeMirror
needs to keep valid versions of a state around so that it can
@@ -2299,17 +2409,17 @@
Static properties
which hold arrays get a copy of these arrays (since arrays tend to
be used as mutable stacks). When this is not correct, for example
because a mode mutates non-array properties of its state object, a
- mode object should define a copyState method,
- which is given a state and should return a safe copy of that
- state.
+ mode object should define
+ a copyState method, which is given a
+ state and should return a safe copy of that state.
If you want your mode to provide smart indentation
(through the indentLine
method and the indentAuto
and newlineAndIndent commands, to which keys can be
bound ), you must define
- an indent(state, textAfter) method on your mode
- object.
+ an indent (state, textAfter) method
+ on your mode object.
The indentation method should inspect the given state object,
and optionally the textAfter string, which contains
@@ -2322,8 +2432,9 @@
Static properties
Static properties
state.
In a nested mode, it is recommended to add an
- extra method, innerMode which, given a state object,
- returns a {state, mode} object with the inner mode
- and its state for the current position. These are used by utility
- scripts such as the tag closer to
- get context information. Use the CodeMirror.innerMode
- helper function to, starting from a mode and a state, recursively
- walk down to the innermost mode and state.
+ extra method, innerMode which, given
+ a state object, returns a {state, mode} object with
+ the inner mode and its state for the current position. These are
+ used by utility scripts such as the tag
+ closer to get context information. Use
+ the CodeMirror.innerMode helper function to, starting
+ from a mode and a state, recursively walk down to the innermost
+ mode and state.
To make indentation work properly in a nested parser, it is
advisable to give the startState method of modes that
@@ -2374,25 +2486,25 @@
Static properties
parser do this, for example, to allow JavaScript and CSS code
inside the mixed-mode HTML mode to be properly indented.
- It is possible, and encouraged, to associate your mode, or a
- certain configuration of your mode, with
+
It is possible, and encouraged, to associate
+ your mode, or a certain configuration of your mode, with
a MIME type. For
example, the JavaScript mode associates itself
with text/javascript, and its JSON variant
with application/json. To do this,
- call CodeMirror.defineMIME(mime, modeSpec),
- where modeSpec can be a string or object specifying a
- mode, as in the mode
- option.
+ call CodeMirror.defineMIME (mime,
+ modeSpec), where modeSpec can be a string or
+ object specifying a mode, as in
+ the mode option.
Sometimes, it is useful to add or override mode
object properties from external code.
- The CodeMirror.extendMode can be used to add
- properties to mode objects produced for a specific mode. Its first
- argument is the name of the mode, its second an object that
- specifies the properties that should be added. This is mostly
- useful to add utilities that can later be looked
- up through getMode .
+ The CodeMirror.extendMode function
+ can be used to add properties to mode objects produced for a
+ specific mode. Its first argument is the name of the mode, its
+ second an object that specifies the properties that should be
+ added. This is mostly useful to add utilities that can later be
+ looked up through getMode .
diff --git a/doc/realworld.html b/doc/realworld.html
index 421250162c..8afe73aa0a 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -33,6 +33,7 @@
BlueGriffon (HTML editor)
Cargo Collective (creative publishing platform)
ClickHelp (technical writing tool)
+
Complete.ly playground
Code per Node (Drupal module)
Codebug (PHP Xdebug front-end)
CodeMirror Eclipse (embed CM in Eclipse)
@@ -45,8 +46,10 @@
Code together (collaborative editing)
Codev (collaborative IDE)
CodeZample (code snippet sharing)
+
Codio (Web IDE)
Collaborative CodeMirror demo (CodeMirror + operational transforms)
Community Code Camp (code snippet sharing)
+
compilejava.net (online Java sandbox)
CKWNC (UML editor)
CSSDeck (CSS showcase)
Deck.js integration (slides with editors)
@@ -79,6 +82,7 @@
Jumpseller (online store builder)
kl1p (paste service)
Light Table (experimental IDE)
+
Liveweave (HTML/CSS/JS scratchpad)
Marklight editor (lightweight markup editor)
Mergely (interactive diffing)
MIHTool (iOS web-app debugging tool)
@@ -91,7 +95,9 @@
ORG (z80 assembly IDE)
Orion-CodeMirror integration (running CodeMirror modes in Orion)
Paper.js (graphics scripting)
+
PrinBit (collaborative coding tool)
Prose.io (github content editor)
+
Puzzlescript (puzzle game engine)
ql.io (http API query helper)
QiYun web app platform
Qt+Webkit integration (building a desktop CodeMirror app)
@@ -113,6 +119,7 @@
uiCod (animation demo gallery and sharing)
UmpleOnline (model-oriented programming tool)
Upsource (code viewer)
+
webappfind (windows file bindings for webapps)
WebGL academy (learning WebGL)
WebGL playground
WebKit Web inspector
diff --git a/doc/releases.html b/doc/releases.html
index 88996e27a0..f04b4dcad8 100644
--- a/doc/releases.html
+++ b/doc/releases.html
@@ -28,6 +28,22 @@
Version 3.x
+
21-10-2013: Version 3.19 :
+
+
+
+
23-09-2013: Version 3.18 :
+
+
Emergency release to fix a problem in 3.17
+ where .setOption("lineNumbers", false) would raise an
+ error.
+
23-09-2013: Version 3.17 :
diff --git a/index.html b/index.html
index 6816c1ff82..67771e8bd4 100644
--- a/index.html
+++ b/index.html
@@ -84,7 +84,7 @@
DOWNLOAD LATEST RELEASE
-
+
DONATE WITH PAYPAL
diff --git a/keymap/vim.js b/keymap/vim.js
index 9ad17af31c..e67a46ed7f 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -76,8 +76,10 @@
{ keys: ['
'], type: 'keyToKey', toKeys: ['k'] },
{ keys: ['C-['], type: 'keyToKey', toKeys: [''] },
{ keys: [''], type: 'keyToKey', toKeys: [''] },
- { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'] },
- { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'] },
+ { keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'], context: 'normal' },
+ { keys: ['s'], type: 'keyToKey', toKeys: ['x', 'i'], context: 'visual'},
+ { keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'], context: 'normal' },
+ { keys: ['S'], type: 'keyToKey', toKeys: ['d', 'c', 'c'], context: 'visual' },
{ keys: [''], type: 'keyToKey', toKeys: ['0'] },
{ keys: [''], type: 'keyToKey', toKeys: ['$'] },
{ keys: [''], type: 'keyToKey', toKeys: [''] },
@@ -609,6 +611,9 @@
}
commandDispatcher.processCommand(cm, vim, command);
}
+ },
+ handleEx: function(cm, input) {
+ exCommandDispatcher.processCommand(cm, input);
}
};
@@ -695,6 +700,9 @@
if (linewise && text.charAt(0) == '\n') {
text = text.slice(1) + '\n';
}
+ if(linewise && text.charAt(text.length - 1) !== '\n'){
+ text += '\n';
+ }
// Lowercase and uppercase registers refer to the same register.
// Uppercase just means append.
var register = this.isValidRegister(registerName) ?
@@ -766,44 +774,74 @@
matchCommand: function(key, keyMap, vim) {
var inputState = vim.inputState;
var keys = inputState.keyBuffer.concat(key);
+ var matchedCommands = [];
+ var selectedCharacter;
for (var i = 0; i < keyMap.length; i++) {
var command = keyMap[i];
if (matchKeysPartial(keys, command.keys)) {
- if (keys.length < command.keys.length) {
- // Matches part of a multi-key command. Buffer and wait for next
- // stroke.
- inputState.keyBuffer.push(key);
- return null;
- }
if (inputState.operator && command.type == 'action') {
// Ignore matched action commands after an operator. Operators
// only operate on motions. This check is really for text
// objects since aW, a[ etcs conflicts with a.
continue;
}
- // Matches whole comand. Return the command.
+ // Match commands that take as an argument.
if (command.keys[keys.length - 1] == 'character') {
- inputState.selectedCharacter = keys[keys.length - 1];
- if(inputState.selectedCharacter.length>1){
- switch(inputState.selectedCharacter){
+ selectedCharacter = keys[keys.length - 1];
+ if(selectedCharacter.length>1){
+ switch(selectedCharacter){
case '':
- inputState.selectedCharacter='\n';
+ selectedCharacter='\n';
break;
case '':
- inputState.selectedCharacter=' ';
+ selectedCharacter=' ';
break;
default:
continue;
}
}
}
+ // Add the command to the list of matched commands. Choose the best
+ // command later.
+ matchedCommands.push(command);
+ }
+ }
+
+ // Returns the command if it is a full match, or null if not.
+ function getFullyMatchedCommandOrNull(command) {
+ if (keys.length < command.keys.length) {
+ // Matches part of a multi-key command. Buffer and wait for next
+ // stroke.
+ inputState.keyBuffer.push(key);
+ return null;
+ } else {
+ if (command.keys[keys.length - 1] == 'character') {
+ inputState.selectedCharacter = selectedCharacter;
+ }
+ // Clear the buffer since a full match was found.
inputState.keyBuffer = [];
return command;
}
}
- // Clear the buffer since there are no partial matches.
- inputState.keyBuffer = [];
- return null;
+
+ if (!matchedCommands.length) {
+ // Clear the buffer since there were no matches.
+ inputState.keyBuffer = [];
+ return null;
+ } else if (matchedCommands.length == 1) {
+ return getFullyMatchedCommandOrNull(matchedCommands[0]);
+ } else {
+ // Find the best match in the list of matchedCommands.
+ var context = vim.visualMode ? 'visual' : 'normal';
+ var bestMatch = matchedCommands[0]; // Default to first in the list.
+ for (var i = 0; i < matchedCommands.length; i++) {
+ if (matchedCommands[i].context == context) {
+ bestMatch = matchedCommands[i];
+ break;
+ }
+ }
+ return getFullyMatchedCommandOrNull(bestMatch);
+ }
},
processCommand: function(cm, vim, command) {
vim.inputState.repeatOverride = command.repeatOverride;
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 4e300b2be2..23eaf74d44 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -74,7 +74,6 @@
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
-.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
@@ -91,6 +90,7 @@
.cm-em {font-style: italic;}
.cm-link {text-decoration: underline;}
+.cm-s-default .cm-error {color: #f00;}
.cm-invalidchar {color: #f00;}
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
diff --git a/lib/codemirror.js b/lib/codemirror.js
index 97ab9a0cde..563f688a26 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -353,8 +353,10 @@ window.CodeMirror = (function() {
d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
} else d.gutterFiller.style.display = "";
- if (mac_geLion && scrollbarWidth(d.measure) === 0)
+ if (mac_geLion && scrollbarWidth(d.measure) === 0) {
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
+ d.scrollbarV.style.pointerEvents = d.scrollbarH.style.pointerEvents = "none";
+ }
}
function visibleLines(display, doc, viewPort) {
@@ -935,8 +937,9 @@ window.CodeMirror = (function() {
// smallest indentation, which tends to need the least context to
// parse correctly.
function findStartLine(cm, n, precise) {
- var minindent, minline, doc = cm.doc, maxScan = cm.doc.mode.innerMode ? 1000 : 100;
- for (var search = n, lim = n - maxScan; search > lim; --search) {
+ var minindent, minline, doc = cm.doc;
+ var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
+ for (var search = n; search > lim; --search) {
if (search <= doc.first) return doc.first;
var line = getLine(doc, search - 1);
if (line.stateAfter && (!precise || search <= doc.frontier)) return search;
@@ -961,6 +964,7 @@ window.CodeMirror = (function() {
line.stateAfter = save ? copyState(doc.mode, state) : null;
++pos;
});
+ if (precise) doc.frontier = pos;
return state;
}
@@ -1383,7 +1387,8 @@ window.CodeMirror = (function() {
display.scroller.scrollLeft = display.scrollbarH.scrollLeft = doc.scrollLeft = newScrollPos.scrollLeft;
alignHorizontally(cm);
if (op.scrollToPos)
- scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos), op.scrollToPosMargin);
+ scrollPosIntoView(cm, clipPos(cm.doc, op.scrollToPos.from),
+ clipPos(cm.doc, op.scrollToPos.to), op.scrollToPos.margin);
} else if (newScrollPos) {
scrollCursorIntoView(cm);
}
@@ -2179,7 +2184,11 @@ window.CodeMirror = (function() {
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
if (!pos || opera) return; // Opera is difficult.
- if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
+
+ // Reset the current text selection only if the click is done outside of the selection
+ // and 'resetSelectionOnContextMenu' option is true.
+ var reset = cm.options.resetSelectionOnContextMenu;
+ if (reset && (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)))
operation(cm, setSelection)(cm.doc, pos, pos);
var oldCSS = display.input.style.cssText;
@@ -2630,7 +2639,7 @@ window.CodeMirror = (function() {
// SCROLLING
function scrollCursorIntoView(cm) {
- var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
+ var coords = scrollPosIntoView(cm, cm.doc.sel.head, null, cm.options.cursorScrollMargin);
if (!cm.state.focused) return;
var display = cm.display, box = getRect(display.sizer), doScroll = null;
if (coords.top + box.top < 0) doScroll = true;
@@ -2647,11 +2656,15 @@ window.CodeMirror = (function() {
}
}
- function scrollPosIntoView(cm, pos, margin) {
+ function scrollPosIntoView(cm, pos, end, margin) {
if (margin == null) margin = 0;
for (;;) {
var changed = false, coords = cursorCoords(cm, pos);
- var scrollPos = calculateScrollPos(cm, coords.left, coords.top - margin, coords.left, coords.bottom + margin);
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
+ var scrollPos = calculateScrollPos(cm, Math.min(coords.left, endCoords.left),
+ Math.min(coords.top, endCoords.top) - margin,
+ Math.max(coords.left, endCoords.left),
+ Math.max(coords.bottom, endCoords.bottom) + margin);
var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
if (scrollPos.scrollTop != null) {
setScrollTop(cm, scrollPos.scrollTop);
@@ -3169,17 +3182,23 @@ window.CodeMirror = (function() {
clientHeight: scroller.clientHeight - co, clientWidth: scroller.clientWidth - co};
},
- scrollIntoView: operation(null, function(pos, margin) {
- if (typeof pos == "number") pos = Pos(pos, 0);
+ scrollIntoView: operation(null, function(range, margin) {
+ if (range == null) range = {from: this.doc.sel.head, to: null};
+ else if (typeof range == "number") range = {from: Pos(range, 0), to: null};
+ else if (range.from == null) range = {from: range, to: null};
+ if (!range.to) range.to = range.from;
if (!margin) margin = 0;
- var coords = pos;
- if (!pos || pos.line != null) {
- this.curOp.scrollToPos = pos ? clipPos(this.doc, pos) : this.doc.sel.head;
- this.curOp.scrollToPosMargin = margin;
- coords = cursorCoords(this, this.curOp.scrollToPos);
+ var coords = range;
+ if (range.from.line != null) {
+ this.curOp.scrollToPos = {from: range.from, to: range.to, margin: margin};
+ coords = {from: cursorCoords(this, range.from),
+ to: cursorCoords(this, range.to)};
}
- var sPos = calculateScrollPos(this, coords.left, coords.top - margin, coords.right, coords.bottom + margin);
+ var sPos = calculateScrollPos(this, Math.min(coords.from.left, coords.to.left),
+ Math.min(coords.from.top, coords.to.top) - margin,
+ Math.max(coords.from.right, coords.to.right),
+ Math.max(coords.from.bottom, coords.to.bottom) + margin);
updateScrollPos(this, sPos.scrollLeft, sPos.scrollTop);
}),
@@ -3211,6 +3230,7 @@ window.CodeMirror = (function() {
clearCaches(this);
resetInput(this, true);
updateScrollPos(this, doc.scrollLeft, doc.scrollTop);
+ signalLater(this, "swapDoc", this, old);
return old;
}),
@@ -3285,6 +3305,8 @@ window.CodeMirror = (function() {
option("lineNumberFormatter", function(integer) {return integer;}, guttersChanged, true);
option("showCursorWhenSelecting", false, updateSelection, true);
+ option("resetSelectionOnContextMenu", true);
+
option("readOnly", false, function(cm, val) {
if (val == "nocursor") {onBlur(cm); cm.display.input.blur();}
else if (!val) resetInput(cm, true);
@@ -3521,7 +3543,8 @@ window.CodeMirror = (function() {
keyMap.basic = {
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
- "Delete": "delCharAfter", "Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
+ "Tab": "defaultTab", "Shift-Tab": "indentAuto",
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
};
// Note that the save and find-related commands aren't defined by
@@ -4461,7 +4484,7 @@ window.CodeMirror = (function() {
return out;
}
return function(builder, text, style, startStyle, endStyle, title) {
- return inner(builder, text.replace(/ {3,}/, split), style, startStyle, endStyle, title);
+ return inner(builder, text.replace(/ {3,}/g, split), style, startStyle, endStyle, title);
};
}
@@ -5879,7 +5902,7 @@ window.CodeMirror = (function() {
// THE END
- CodeMirror.version = "3.18.0";
+ CodeMirror.version = "3.19.0";
return CodeMirror;
})();
diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js
index b7203f124b..d29ad2b73a 100644
--- a/mode/coffeescript/coffeescript.js
+++ b/mode/coffeescript/coffeescript.js
@@ -2,347 +2,348 @@
* Link to the project's GitHub page:
* https://github.com/pickhardt/coffeescript-codemirror-mode
*/
-CodeMirror.defineMode('coffeescript', function(conf) {
- var ERRORCLASS = 'error';
-
- function wordRegexp(words) {
- return new RegExp("^((" + words.join(")|(") + "))\\b");
- }
-
- var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]");
- var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\},:`=;\\.]');
- var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))");
- var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
- var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))");
- var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*");
- var properties = new RegExp("^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*");
-
- var wordOperators = wordRegexp(['and', 'or', 'not',
- 'is', 'isnt', 'in',
- 'instanceof', 'typeof']);
- var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else',
- 'switch', 'try', 'catch', 'finally', 'class'];
- var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete',
- 'do', 'in', 'of', 'new', 'return', 'then',
- 'this', 'throw', 'when', 'until'];
-
- var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
-
- indentKeywords = wordRegexp(indentKeywords);
-
-
- var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])");
- var regexPrefixes = new RegExp("^(/{3}|/)");
- var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no'];
- var constants = wordRegexp(commonConstants);
-
- // Tokenizers
- function tokenBase(stream, state) {
- // Handle scope changes
- if (stream.sol()) {
- var scopeOffset = state.scopes[0].offset;
- if (stream.eatSpace()) {
- var lineOffset = stream.indentation();
- if (lineOffset > scopeOffset) {
- return 'indent';
- } else if (lineOffset < scopeOffset) {
- return 'dedent';
- }
- return null;
- } else {
- if (scopeOffset > 0) {
- dedent(stream, state);
- }
- }
+CodeMirror.defineMode("coffeescript", function(conf) {
+ var ERRORCLASS = "error";
+
+ function wordRegexp(words) {
+ return new RegExp("^((" + words.join(")|(") + "))\\b");
+ }
+
+ var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?)/;
+ var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
+ var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
+ var properties = /^(@|this\.)[_A-Za-z$][_A-Za-z$0-9]*/;
+
+ var wordOperators = wordRegexp(["and", "or", "not",
+ "is", "isnt", "in",
+ "instanceof", "typeof"]);
+ var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
+ "switch", "try", "catch", "finally", "class"];
+ var commonKeywords = ["break", "by", "continue", "debugger", "delete",
+ "do", "in", "of", "new", "return", "then",
+ "this", "throw", "when", "until"];
+
+ var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
+
+ indentKeywords = wordRegexp(indentKeywords);
+
+
+ var stringPrefixes = /^('{3}|\"{3}|['\"])/;
+ var regexPrefixes = /^(\/{3}|\/)/;
+ var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
+ var constants = wordRegexp(commonConstants);
+
+ // Tokenizers
+ function tokenBase(stream, state) {
+ // Handle scope changes
+ if (stream.sol()) {
+ if (state.scope.align === null) state.scope.align = false;
+ var scopeOffset = state.scope.offset;
+ if (stream.eatSpace()) {
+ var lineOffset = stream.indentation();
+ if (lineOffset > scopeOffset && state.scope.type == "coffee") {
+ return "indent";
+ } else if (lineOffset < scopeOffset) {
+ return "dedent";
}
- if (stream.eatSpace()) {
- return null;
- }
-
- var ch = stream.peek();
-
- // Handle docco title comment (single line)
- if (stream.match("####")) {
- stream.skipToEnd();
- return 'comment';
- }
-
- // Handle multi line comments
- if (stream.match("###")) {
- state.tokenize = longComment;
- return state.tokenize(stream, state);
+ return null;
+ } else {
+ if (scopeOffset > 0) {
+ dedent(stream, state);
}
+ }
+ }
+ if (stream.eatSpace()) {
+ return null;
+ }
- // Single line comment
- if (ch === '#') {
- stream.skipToEnd();
- return 'comment';
- }
+ var ch = stream.peek();
- // Handle number literals
- if (stream.match(/^-?[0-9\.]/, false)) {
- var floatLiteral = false;
- // Floats
- if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
- floatLiteral = true;
- }
- if (stream.match(/^-?\d+\.\d*/)) {
- floatLiteral = true;
- }
- if (stream.match(/^-?\.\d+/)) {
- floatLiteral = true;
- }
-
- if (floatLiteral) {
- // prevent from getting extra . on 1..
- if (stream.peek() == "."){
- stream.backUp(1);
- }
- return 'number';
- }
- // Integers
- var intLiteral = false;
- // Hex
- if (stream.match(/^-?0x[0-9a-f]+/i)) {
- intLiteral = true;
- }
- // Decimal
- if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
- intLiteral = true;
- }
- // Zero by itself with no other piece of number.
- if (stream.match(/^-?0(?![\dx])/i)) {
- intLiteral = true;
- }
- if (intLiteral) {
- return 'number';
- }
- }
+ // Handle docco title comment (single line)
+ if (stream.match("####")) {
+ stream.skipToEnd();
+ return "comment";
+ }
- // Handle strings
- if (stream.match(stringPrefixes)) {
- state.tokenize = tokenFactory(stream.current(), 'string');
- return state.tokenize(stream, state);
- }
- // Handle regex literals
- if (stream.match(regexPrefixes)) {
- if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division
- state.tokenize = tokenFactory(stream.current(), 'string-2');
- return state.tokenize(stream, state);
- } else {
- stream.backUp(1);
- }
- }
+ // Handle multi line comments
+ if (stream.match("###")) {
+ state.tokenize = longComment;
+ return state.tokenize(stream, state);
+ }
- // Handle operators and delimiters
- if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
- return 'punctuation';
- }
- if (stream.match(doubleOperators)
- || stream.match(singleOperators)
- || stream.match(wordOperators)) {
- return 'operator';
- }
- if (stream.match(singleDelimiters)) {
- return 'punctuation';
- }
+ // Single line comment
+ if (ch === "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
- if (stream.match(constants)) {
- return 'atom';
+ // Handle number literals
+ if (stream.match(/^-?[0-9\.]/, false)) {
+ var floatLiteral = false;
+ // Floats
+ if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
+ floatLiteral = true;
+ }
+ if (stream.match(/^-?\d+\.\d*/)) {
+ floatLiteral = true;
+ }
+ if (stream.match(/^-?\.\d+/)) {
+ floatLiteral = true;
+ }
+
+ if (floatLiteral) {
+ // prevent from getting extra . on 1..
+ if (stream.peek() == "."){
+ stream.backUp(1);
}
+ return "number";
+ }
+ // Integers
+ var intLiteral = false;
+ // Hex
+ if (stream.match(/^-?0x[0-9a-f]+/i)) {
+ intLiteral = true;
+ }
+ // Decimal
+ if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
+ intLiteral = true;
+ }
+ // Zero by itself with no other piece of number.
+ if (stream.match(/^-?0(?![\dx])/i)) {
+ intLiteral = true;
+ }
+ if (intLiteral) {
+ return "number";
+ }
+ }
- if (stream.match(keywords)) {
- return 'keyword';
- }
+ // Handle strings
+ if (stream.match(stringPrefixes)) {
+ state.tokenize = tokenFactory(stream.current(), "string");
+ return state.tokenize(stream, state);
+ }
+ // Handle regex literals
+ if (stream.match(regexPrefixes)) {
+ if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
+ state.tokenize = tokenFactory(stream.current(), "string-2");
+ return state.tokenize(stream, state);
+ } else {
+ stream.backUp(1);
+ }
+ }
- if (stream.match(identifiers)) {
- return 'variable';
- }
+ // Handle operators and delimiters
+ if (stream.match(operators) || stream.match(wordOperators)) {
+ return "operator";
+ }
+ if (stream.match(delimiters)) {
+ return "punctuation";
+ }
- if (stream.match(properties)) {
- return 'property';
- }
+ if (stream.match(constants)) {
+ return "atom";
+ }
- // Handle non-detected items
- stream.next();
- return ERRORCLASS;
+ if (stream.match(keywords)) {
+ return "keyword";
}
- function tokenFactory(delimiter, outclass) {
- var singleline = delimiter.length == 1;
- return function(stream, state) {
- while (!stream.eol()) {
- stream.eatWhile(/[^'"\/\\]/);
- if (stream.eat('\\')) {
- stream.next();
- if (singleline && stream.eol()) {
- return outclass;
- }
- } else if (stream.match(delimiter)) {
- state.tokenize = tokenBase;
- return outclass;
- } else {
- stream.eat(/['"\/]/);
- }
- }
- if (singleline) {
- if (conf.mode.singleLineStringErrors) {
- outclass = ERRORCLASS;
- } else {
- state.tokenize = tokenBase;
- }
- }
- return outclass;
- };
+ if (stream.match(identifiers)) {
+ return "variable";
}
- function longComment(stream, state) {
- while (!stream.eol()) {
- stream.eatWhile(/[^#]/);
- if (stream.match("###")) {
- state.tokenize = tokenBase;
- break;
- }
- stream.eatWhile("#");
- }
- return "comment";
+ if (stream.match(properties)) {
+ return "property";
}
- function indent(stream, state, type) {
- type = type || 'coffee';
- var indentUnit = 0;
- if (type === 'coffee') {
- for (var i = 0; i < state.scopes.length; i++) {
- if (state.scopes[i].type === 'coffee') {
- indentUnit = state.scopes[i].offset + conf.indentUnit;
- break;
- }
- }
+ // Handle non-detected items
+ stream.next();
+ return ERRORCLASS;
+ }
+
+ function tokenFactory(delimiter, outclass) {
+ var singleline = delimiter.length == 1;
+ return function(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^'"\/\\]/);
+ if (stream.eat("\\")) {
+ stream.next();
+ if (singleline && stream.eol()) {
+ return outclass;
+ }
+ } else if (stream.match(delimiter)) {
+ state.tokenize = tokenBase;
+ return outclass;
} else {
- indentUnit = stream.column() + stream.current().length;
+ stream.eat(/['"\/]/);
}
- state.scopes.unshift({
- offset: indentUnit,
- type: type
- });
- }
-
- function dedent(stream, state) {
- if (state.scopes.length == 1) return;
- if (state.scopes[0].type === 'coffee') {
- var _indent = stream.indentation();
- var _indent_index = -1;
- for (var i = 0; i < state.scopes.length; ++i) {
- if (_indent === state.scopes[i].offset) {
- _indent_index = i;
- break;
- }
- }
- if (_indent_index === -1) {
- return true;
- }
- while (state.scopes[0].offset !== _indent) {
- state.scopes.shift();
- }
- return false;
+ }
+ if (singleline) {
+ if (conf.mode.singleLineStringErrors) {
+ outclass = ERRORCLASS;
} else {
- state.scopes.shift();
- return false;
+ state.tokenize = tokenBase;
}
+ }
+ return outclass;
+ };
+ }
+
+ function longComment(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^#]/);
+ if (stream.match("###")) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ stream.eatWhile("#");
}
-
- function tokenLexer(stream, state) {
- var style = state.tokenize(stream, state);
- var current = stream.current();
-
- // Handle '.' connected identifiers
- if (current === '.') {
- style = state.tokenize(stream, state);
- current = stream.current();
- if (/^\.[\w$]+$/.test(current)) {
- return 'variable';
- } else {
- return ERRORCLASS;
- }
- }
-
- // Handle scope changes.
- if (current === 'return') {
- state.dedent += 1;
- }
- if (((current === '->' || current === '=>') &&
- !state.lambda &&
- state.scopes[0].type == 'coffee' &&
- stream.peek() === '')
- || style === 'indent') {
- indent(stream, state);
- }
- var delimiter_index = '[({'.indexOf(current);
- if (delimiter_index !== -1) {
- indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
- }
- if (indentKeywords.exec(current)){
- indent(stream, state);
- }
- if (current == 'then'){
- dedent(stream, state);
- }
-
-
- if (style === 'dedent') {
- if (dedent(stream, state)) {
- return ERRORCLASS;
- }
- }
- delimiter_index = '])}'.indexOf(current);
- if (delimiter_index !== -1) {
- if (dedent(stream, state)) {
- return ERRORCLASS;
- }
- }
- if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') {
- if (state.scopes.length > 1) state.scopes.shift();
- state.dedent -= 1;
+ return "comment";
+ }
+
+ function indent(stream, state, type) {
+ type = type || "coffee";
+ var offset = 0, align = false, alignOffset = null;
+ for (var scope = state.scope; scope; scope = scope.prev) {
+ if (scope.type === "coffee") {
+ offset = scope.offset + conf.indentUnit;
+ break;
+ }
+ }
+ if (type !== "coffee") {
+ align = null;
+ alignOffset = stream.column() + stream.current().length;
+ }
+ state.scope = {
+ offset: offset,
+ type: type,
+ prev: state.scope,
+ align: align,
+ alignOffset: alignOffset
+ };
+ }
+
+ function dedent(stream, state) {
+ if (!state.scope.prev) return;
+ if (state.scope.type === "coffee") {
+ var _indent = stream.indentation();
+ var matched = false;
+ for (var scope = state.scope; scope; scope = scope.prev) {
+ if (_indent === scope.offset) {
+ matched = true;
+ break;
}
-
- return style;
+ }
+ if (!matched) {
+ return true;
+ }
+ while (state.scope.prev && state.scope.offset !== _indent) {
+ state.scope = state.scope.prev;
+ }
+ return false;
+ } else {
+ state.scope = state.scope.prev;
+ return false;
+ }
+ }
+
+ function tokenLexer(stream, state) {
+ var style = state.tokenize(stream, state);
+ var current = stream.current();
+
+ // Handle "." connected identifiers
+ if (current === ".") {
+ style = state.tokenize(stream, state);
+ current = stream.current();
+ if (/^\.[\w$]+$/.test(current)) {
+ return "variable";
+ } else {
+ return ERRORCLASS;
+ }
}
- var external = {
- startState: function(basecolumn) {
- return {
- tokenize: tokenBase,
- scopes: [{offset:basecolumn || 0, type:'coffee'}],
- lastToken: null,
- lambda: false,
- dedent: 0
- };
- },
-
- token: function(stream, state) {
- var style = tokenLexer(stream, state);
-
- state.lastToken = {style:style, content: stream.current()};
-
- if (stream.eol() && stream.lambda) {
- state.lambda = false;
- }
-
- return style;
- },
+ // Handle scope changes.
+ if (current === "return") {
+ state.dedent += 1;
+ }
+ if (((current === "->" || current === "=>") &&
+ !state.lambda &&
+ state.scope.type == "coffee" &&
+ !stream.peek())
+ || style === "indent") {
+ indent(stream, state);
+ }
+ var delimiter_index = "[({".indexOf(current);
+ if (delimiter_index !== -1) {
+ indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+ }
+ if (indentKeywords.exec(current)){
+ indent(stream, state);
+ }
+ if (current == "then"){
+ dedent(stream, state);
+ }
- indent: function(state) {
- if (state.tokenize != tokenBase) {
- return 0;
- }
- return state.scopes[0].offset;
- },
+ if (style === "dedent") {
+ if (dedent(stream, state)) {
+ return ERRORCLASS;
+ }
+ }
+ delimiter_index = "])}".indexOf(current);
+ if (delimiter_index !== -1) {
+ if (dedent(stream, state)) {
+ return ERRORCLASS;
+ }
+ }
+ if (state.dedent > 0 && stream.eol() && state.scope.type == "coffee") {
+ if (state.scope.prev) state.scope = state.scope.prev;
+ state.dedent -= 1;
+ }
- lineComment: "#",
- fold: "indent"
- };
- return external;
+ return style;
+ }
+
+ var external = {
+ startState: function(basecolumn) {
+ return {
+ tokenize: tokenBase,
+ scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
+ lastToken: null,
+ lambda: false,
+ dedent: 0
+ };
+ },
+
+ token: function(stream, state) {
+ var fillAlign = state.scope.align === null && state.scope;
+ if (fillAlign && stream.sol()) fillAlign.align = false;
+
+ var style = tokenLexer(stream, state);
+ if (fillAlign && style && style != "comment") fillAlign.align = true;
+
+ state.lastToken = {style:style, content: stream.current()};
+
+ if (stream.eol() && stream.lambda) {
+ state.lambda = false;
+ }
+
+ return style;
+ },
+
+ indent: function(state, text) {
+ if (state.tokenize != tokenBase) return 0;
+ var closes = state.scope.type === (text && text.charAt(0));
+ if (state.scope.align)
+ return state.scope.alignOffset - (closes ? 1 : 0);
+ else
+ return (closes ? state.scope.prev : state.scope).offset;
+ },
+
+ lineComment: "#",
+ fold: "indent"
+ };
+ return external;
});
-CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript');
+CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
diff --git a/mode/css/css.js b/mode/css/css.js
index 4264bc606c..f47aba75a1 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -3,7 +3,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
- var indentUnit = config.indentUnit,
+ var indentUnit = config.indentUnit || config.tabSize || 2,
hooks = parserConfig.hooks || {},
atMediaTypes = parserConfig.atMediaTypes || {},
atMediaFeatures = parserConfig.atMediaFeatures || {},
@@ -259,8 +259,13 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
else if (type == "}") {
if (context == "interpolation") style = "operator";
- state.stack.pop();
- if (context == "propertyValue") state.stack.pop();
+ // 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 == "interpolation") state.stack.push("interpolation");
else if (type == "@media") state.stack.push("@media");
@@ -278,11 +283,13 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
else state.stack.push("(");
}
else if (type == ")") {
- if (context == "propertyValue") {
- // In @mediaType( without closing ; after propertyValue
- state.stack.pop();
+ // Pop off end of array until ( is reached
+ while(state.stack.length){
+ var removed = state.stack.pop();
+ if(removed.indexOf("(") > -1){
+ break;
+ }
}
- state.stack.pop();
}
else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue");
else if (context == "propertyValue" && type == ";") state.stack.pop();
@@ -602,6 +609,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
}
return ["variable", "variable"];
},
+ ",": function(_stream, state) {
+ if (state.stack[state.stack.length - 1] == "propertyValue") {
+ return ["operator", ";"];
+ }
+ },
"/": function(stream, state) {
if (stream.eat("/")) {
stream.skipToEnd();
diff --git a/mode/css/scss_test.js b/mode/css/scss_test.js
index 3644f63de8..bc2a078576 100644
--- a/mode/css/scss_test.js
+++ b/mode/css/scss_test.js
@@ -1,6 +1,7 @@
(function() {
- var mode = CodeMirror.getMode({tabSize: 4}, "text/x-scss");
+ var mode = CodeMirror.getMode({tabSize: 1}, "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]) }");
@@ -77,4 +78,7 @@
MT('nested_structure_with_id_selector',
"[tag p] { [builtin #hello] { [property color][operator :][keyword red]; } }");
+
+ IT('mixin',
+ "@mixin container[1 (][2 $a: 10][1 , ][2 $b: 10][1 , ][2 $c: 10]) [1 {]}");
})();
diff --git a/mode/css/test.js b/mode/css/test.js
index 13fc04c1b3..43647833aa 100644
--- a/mode/css/test.js
+++ b/mode/css/test.js
@@ -1,6 +1,7 @@
(function() {
- var mode = CodeMirror.getMode({tabSize: 4}, "css");
+ var mode = CodeMirror.getMode({tabSize: 1}, "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",
@@ -123,4 +124,7 @@
MT("commentSGML",
"[comment ]");
+
+ IT("tagSelector",
+ "strong, em [1 { background][2 : rgba][3 (255, 255, 0, .2][2 )][1 ;]}");
})();
diff --git a/mode/eiffel/eiffel.js b/mode/eiffel/eiffel.js
new file mode 100644
index 0000000000..15f34a9325
--- /dev/null
+++ b/mode/eiffel/eiffel.js
@@ -0,0 +1,147 @@
+CodeMirror.defineMode("eiffel", function() {
+ function wordObj(words) {
+ var o = {};
+ for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
+ return o;
+ }
+ var keywords = wordObj([
+ 'note',
+ 'across',
+ 'when',
+ 'variant',
+ 'until',
+ 'unique',
+ 'undefine',
+ 'then',
+ 'strip',
+ 'select',
+ 'retry',
+ 'rescue',
+ 'require',
+ 'rename',
+ 'reference',
+ 'redefine',
+ 'prefix',
+ 'once',
+ 'old',
+ 'obsolete',
+ 'loop',
+ 'local',
+ 'like',
+ 'is',
+ 'inspect',
+ 'infix',
+ 'include',
+ 'if',
+ 'frozen',
+ 'from',
+ 'external',
+ 'export',
+ 'ensure',
+ 'end',
+ 'elseif',
+ 'else',
+ 'do',
+ 'creation',
+ 'create',
+ 'check',
+ 'alias',
+ 'agent',
+ 'separate',
+ 'invariant',
+ 'inherit',
+ 'indexing',
+ 'feature',
+ 'expanded',
+ 'deferred',
+ 'class',
+ 'Void',
+ 'True',
+ 'Result',
+ 'Precursor',
+ 'False',
+ 'Current',
+ 'create',
+ 'attached',
+ 'detachable',
+ 'as',
+ 'and',
+ 'implies',
+ 'not',
+ 'or'
+ ]);
+ var operators = wordObj([":=", "and then","and", "or","<<",">>"]);
+ var curPunc;
+
+ function chain(newtok, stream, state) {
+ state.tokenize.push(newtok);
+ return newtok(stream, state);
+ }
+
+ function tokenBase(stream, state) {
+ curPunc = null;
+ if (stream.eatSpace()) return null;
+ var ch = stream.next();
+ if (ch == '"'||ch == "'") {
+ return chain(readQuoted(ch, "string"), stream, state);
+ } else if (ch == "-"&&stream.eat("-")) {
+ stream.skipToEnd();
+ return "comment";
+ } else if (ch == ":"&&stream.eat("=")) {
+ return "operator";
+ } else if (/[0-9]/.test(ch)) {
+ stream.eatWhile(/[xXbBCc0-9\.]/);
+ stream.eat(/[\?\!]/);
+ return "ident";
+ } else if (/[a-zA-Z_0-9]/.test(ch)) {
+ stream.eatWhile(/[a-zA-Z_0-9]/);
+ stream.eat(/[\?\!]/);
+ return "ident";
+ } else if (/[=+\-\/*^%<>~]/.test(ch)) {
+ stream.eatWhile(/[=+\-\/*^%<>~]/);
+ return "operator";
+ } else {
+ return null;
+ }
+ }
+
+ function readQuoted(quote, style, unescaped) {
+ return function(stream, state) {
+ var escaped = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == quote && (unescaped || !escaped)) {
+ state.tokenize.pop();
+ break;
+ }
+ escaped = !escaped && ch == "%";
+ }
+ return style;
+ };
+ }
+
+ return {
+ startState: function() {
+ return {tokenize: [tokenBase]};
+ },
+
+ token: function(stream, state) {
+ var style = state.tokenize[state.tokenize.length-1](stream, state);
+ if (style == "ident") {
+ var word = stream.current();
+ style = keywords.propertyIsEnumerable(stream.current()) ? "keyword"
+ : operators.propertyIsEnumerable(stream.current()) ? "operator"
+ : /^[A-Z][A-Z_0-9]*$/g.test(word) ? "tag"
+ : /^0[bB][0-1]+$/g.test(word) ? "number"
+ : /^0[cC][0-7]+$/g.test(word) ? "number"
+ : /^0[xX][a-fA-F0-9]+$/g.test(word) ? "number"
+ : /^([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+)$/g.test(word) ? "number"
+ : /^[0-9]+$/g.test(word) ? "number"
+ : "variable";
+ }
+ return style;
+ },
+ lineComment: "--"
+ };
+});
+
+CodeMirror.defineMIME("text/x-eiffel", "eiffel");
diff --git a/mode/eiffel/index.html b/mode/eiffel/index.html
new file mode 100644
index 0000000000..5d395a05cd
--- /dev/null
+++ b/mode/eiffel/index.html
@@ -0,0 +1,430 @@
+
+
+CodeMirror: Eiffel mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Eiffel mode
+
+note
+ description: "[
+ Project-wide universal properties.
+ This class is an ancestor to all developer-written classes.
+ ANY may be customized for individual projects or teams.
+ ]"
+
+ library: "Free implementation of ELKS library"
+ status: "See notice at end of class."
+ legal: "See notice at end of class."
+ date: "$Date: 2013-01-25 11:49:00 -0800 (Fri, 25 Jan 2013) $"
+ revision: "$Revision: 712 $"
+
+class
+ ANY
+
+feature -- Customization
+
+feature -- Access
+
+ generator: STRING
+ -- Name of current object's generating class
+ -- (base class of the type of which it is a direct instance)
+ external
+ "built_in"
+ ensure
+ generator_not_void: Result /= Void
+ generator_not_empty: not Result.is_empty
+ end
+
+ generating_type: TYPE [detachable like Current]
+ -- Type of current object
+ -- (type of which it is a direct instance)
+ do
+ Result := {detachable like Current}
+ ensure
+ generating_type_not_void: Result /= Void
+ end
+
+feature -- Status report
+
+ conforms_to (other: ANY): BOOLEAN
+ -- Does type of current object conform to type
+ -- of `other' (as per Eiffel: The Language, chapter 13)?
+ require
+ other_not_void: other /= Void
+ external
+ "built_in"
+ end
+
+ same_type (other: ANY): BOOLEAN
+ -- Is type of current object identical to type of `other'?
+ require
+ other_not_void: other /= Void
+ external
+ "built_in"
+ ensure
+ definition: Result = (conforms_to (other) and
+ other.conforms_to (Current))
+ end
+
+feature -- Comparison
+
+ is_equal (other: like Current): BOOLEAN
+ -- Is `other' attached to an object considered
+ -- equal to current object?
+ require
+ other_not_void: other /= Void
+ external
+ "built_in"
+ ensure
+ symmetric: Result implies other ~ Current
+ consistent: standard_is_equal (other) implies Result
+ end
+
+ frozen standard_is_equal (other: like Current): BOOLEAN
+ -- Is `other' attached to an object of the same type
+ -- as current object, and field-by-field identical to it?
+ require
+ other_not_void: other /= Void
+ external
+ "built_in"
+ ensure
+ same_type: Result implies same_type (other)
+ symmetric: Result implies other.standard_is_equal (Current)
+ end
+
+ frozen equal (a: detachable ANY; b: like a): BOOLEAN
+ -- Are `a' and `b' either both void or attached
+ -- to objects considered equal?
+ do
+ if a = Void then
+ Result := b = Void
+ else
+ Result := b /= Void and then
+ a.is_equal (b)
+ end
+ ensure
+ definition: Result = (a = Void and b = Void) or else
+ ((a /= Void and b /= Void) and then
+ a.is_equal (b))
+ end
+
+ frozen standard_equal (a: detachable ANY; b: like a): BOOLEAN
+ -- Are `a' and `b' either both void or attached to
+ -- field-by-field identical objects of the same type?
+ -- Always uses default object comparison criterion.
+ do
+ if a = Void then
+ Result := b = Void
+ else
+ Result := b /= Void and then
+ a.standard_is_equal (b)
+ end
+ ensure
+ definition: Result = (a = Void and b = Void) or else
+ ((a /= Void and b /= Void) and then
+ a.standard_is_equal (b))
+ end
+
+ frozen is_deep_equal (other: like Current): BOOLEAN
+ -- Are `Current' and `other' attached to isomorphic object structures?
+ require
+ other_not_void: other /= Void
+ external
+ "built_in"
+ ensure
+ shallow_implies_deep: standard_is_equal (other) implies Result
+ same_type: Result implies same_type (other)
+ symmetric: Result implies other.is_deep_equal (Current)
+ end
+
+ frozen deep_equal (a: detachable ANY; b: like a): BOOLEAN
+ -- Are `a' and `b' either both void
+ -- or attached to isomorphic object structures?
+ do
+ if a = Void then
+ Result := b = Void
+ else
+ Result := b /= Void and then a.is_deep_equal (b)
+ end
+ ensure
+ shallow_implies_deep: standard_equal (a, b) implies Result
+ both_or_none_void: (a = Void) implies (Result = (b = Void))
+ same_type: (Result and (a /= Void)) implies (b /= Void and then a.same_type (b))
+ symmetric: Result implies deep_equal (b, a)
+ end
+
+feature -- Duplication
+
+ frozen twin: like Current
+ -- New object equal to `Current'
+ -- `twin' calls `copy'; to change copying/twinning semantics, redefine `copy'.
+ external
+ "built_in"
+ ensure
+ twin_not_void: Result /= Void
+ is_equal: Result ~ Current
+ end
+
+ copy (other: like Current)
+ -- Update current object using fields of object attached
+ -- to `other', so as to yield equal objects.
+ require
+ other_not_void: other /= Void
+ type_identity: same_type (other)
+ external
+ "built_in"
+ ensure
+ is_equal: Current ~ other
+ end
+
+ frozen standard_copy (other: like Current)
+ -- Copy every field of `other' onto corresponding field
+ -- of current object.
+ require
+ other_not_void: other /= Void
+ type_identity: same_type (other)
+ external
+ "built_in"
+ ensure
+ is_standard_equal: standard_is_equal (other)
+ end
+
+ frozen clone (other: detachable ANY): like other
+ -- Void if `other' is void; otherwise new object
+ -- equal to `other'
+ --
+ -- For non-void `other', `clone' calls `copy';
+ -- to change copying/cloning semantics, redefine `copy'.
+ obsolete
+ "Use `twin' instead."
+ do
+ if other /= Void then
+ Result := other.twin
+ end
+ ensure
+ equal: Result ~ other
+ end
+
+ frozen standard_clone (other: detachable ANY): like other
+ -- Void if `other' is void; otherwise new object
+ -- field-by-field identical to `other'.
+ -- Always uses default copying semantics.
+ obsolete
+ "Use `standard_twin' instead."
+ do
+ if other /= Void then
+ Result := other.standard_twin
+ end
+ ensure
+ equal: standard_equal (Result, other)
+ end
+
+ frozen standard_twin: like Current
+ -- New object field-by-field identical to `other'.
+ -- Always uses default copying semantics.
+ external
+ "built_in"
+ ensure
+ standard_twin_not_void: Result /= Void
+ equal: standard_equal (Result, Current)
+ end
+
+ frozen deep_twin: like Current
+ -- New object structure recursively duplicated from Current.
+ external
+ "built_in"
+ ensure
+ deep_twin_not_void: Result /= Void
+ deep_equal: deep_equal (Current, Result)
+ end
+
+ frozen deep_clone (other: detachable ANY): like other
+ -- Void if `other' is void: otherwise, new object structure
+ -- recursively duplicated from the one attached to `other'
+ obsolete
+ "Use `deep_twin' instead."
+ do
+ if other /= Void then
+ Result := other.deep_twin
+ end
+ ensure
+ deep_equal: deep_equal (other, Result)
+ end
+
+ frozen deep_copy (other: like Current)
+ -- Effect equivalent to that of:
+ -- `copy' (`other' . `deep_twin')
+ require
+ other_not_void: other /= Void
+ do
+ copy (other.deep_twin)
+ ensure
+ deep_equal: deep_equal (Current, other)
+ end
+
+feature {NONE} -- Retrieval
+
+ frozen internal_correct_mismatch
+ -- Called from runtime to perform a proper dynamic dispatch on `correct_mismatch'
+ -- from MISMATCH_CORRECTOR.
+ local
+ l_msg: STRING
+ l_exc: EXCEPTIONS
+ do
+ if attached {MISMATCH_CORRECTOR} Current as l_corrector then
+ l_corrector.correct_mismatch
+ else
+ create l_msg.make_from_string ("Mismatch: ")
+ create l_exc
+ l_msg.append (generating_type.name)
+ l_exc.raise_retrieval_exception (l_msg)
+ end
+ end
+
+feature -- Output
+
+ io: STD_FILES
+ -- Handle to standard file setup
+ once
+ create Result
+ Result.set_output_default
+ ensure
+ io_not_void: Result /= Void
+ end
+
+ out: STRING
+ -- New string containing terse printable representation
+ -- of current object
+ do
+ Result := tagged_out
+ ensure
+ out_not_void: Result /= Void
+ end
+
+ frozen tagged_out: STRING
+ -- New string containing terse printable representation
+ -- of current object
+ external
+ "built_in"
+ ensure
+ tagged_out_not_void: Result /= Void
+ end
+
+ print (o: detachable ANY)
+ -- Write terse external representation of `o'
+ -- on standard output.
+ do
+ if o /= Void then
+ io.put_string (o.out)
+ end
+ end
+
+feature -- Platform
+
+ Operating_environment: OPERATING_ENVIRONMENT
+ -- Objects available from the operating system
+ once
+ create Result
+ ensure
+ operating_environment_not_void: Result /= Void
+ end
+
+feature {NONE} -- Initialization
+
+ default_create
+ -- Process instances of classes with no creation clause.
+ -- (Default: do nothing.)
+ do
+ end
+
+feature -- Basic operations
+
+ default_rescue
+ -- Process exception for routines with no Rescue clause.
+ -- (Default: do nothing.)
+ do
+ end
+
+ frozen do_nothing
+ -- Execute a null action.
+ do
+ end
+
+ frozen default: detachable like Current
+ -- Default value of object's type
+ do
+ end
+
+ frozen default_pointer: POINTER
+ -- Default value of type `POINTER'
+ -- (Avoid the need to write `p'.`default' for
+ -- some `p' of type `POINTER'.)
+ do
+ ensure
+ -- Result = Result.default
+ end
+
+ frozen as_attached: attached like Current
+ -- Attached version of Current
+ -- (Can be used during transitional period to convert
+ -- non-void-safe classes to void-safe ones.)
+ do
+ Result := Current
+ end
+
+invariant
+ reflexive_equality: standard_is_equal (Current)
+ reflexive_conformance: conforms_to (Current)
+
+note
+ copyright: "Copyright (c) 1984-2012, Eiffel Software and others"
+ license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
+ source: "[
+ Eiffel Software
+ 5949 Hollister Ave., Goleta, CA 93117 USA
+ Telephone 805-685-1006, Fax 805-685-6869
+ Website http://www.eiffel.com
+ Customer support http://support.eiffel.com
+ ]"
+
+end
+
+
+
+
+ MIME types defined: text/x-eiffel.
+
+ Created by YNH .
+
diff --git a/mode/gfm/gfm.js b/mode/gfm/gfm.js
index 1179b53dc6..1411a9382a 100644
--- a/mode/gfm/gfm.js
+++ b/mode/gfm/gfm.js
@@ -75,7 +75,8 @@ CodeMirror.defineMode("gfm", function(config) {
return "link";
}
}
- 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) != "](") {
// URLs
// Taken from http://daringfireball.net/2010/07/improved_regex_for_matching_urls
// And then (issue #1160) simplified to make it not crash the Chrome Regexp engine
diff --git a/mode/gherkin/gherkin.js b/mode/gherkin/gherkin.js
new file mode 100644
index 0000000000..dadb58362b
--- /dev/null
+++ b/mode/gherkin/gherkin.js
@@ -0,0 +1,168 @@
+/*
+Gherkin mode - http://www.cukes.info/
+Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues
+*/
+
+// Following Objs from Brackets implementation: https://github.com/tregusti/brackets-gherkin/blob/master/main.js
+//var Quotes = {
+// SINGLE: 1,
+// DOUBLE: 2
+//};
+
+//var regex = {
+// keywords: /(Feature| {2}(Scenario|In order to|As|I)| {4}(Given|When|Then|And))/
+//};
+
+CodeMirror.defineMode("gherkin", function () {
+ return {
+ startState: function () {
+ return {
+ lineNumber: 0,
+ tableHeaderLine: null,
+ allowFeature: true,
+ allowBackground: false,
+ allowScenario: false,
+ allowSteps: false,
+ allowPlaceholders: false,
+ inMultilineArgument: false,
+ inMultilineString: false,
+ inMultilineTable: false
+ };
+ },
+ token: function (stream, state) {
+ if (stream.sol()) {
+ state.lineNumber++;
+ }
+ stream.eatSpace();
+
+ // INSIDE OF MULTILINE ARGUMENTS
+ if (state.inMultilineArgument) {
+
+ // STRING
+ if (state.inMultilineString) {
+ if (stream.match('"""')) {
+ state.inMultilineString = false;
+ state.inMultilineArgument = false;
+ } else {
+ stream.match(/.*/);
+ }
+ return "string";
+ }
+
+ // TABLE
+ 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";
+ }
+ }
+
+ // DETECT START
+ if (stream.match('"""')) {
+ // String
+ state.inMultilineString = true;
+ return "string";
+ } else if (stream.match("|")) {
+ // Table
+ state.inMultilineTable = true;
+ return "bracket";
+ } else {
+ // Or abort
+ state.inMultilineArgument = false;
+ state.tableHeaderLine = null;
+ }
+
+
+ return null;
+ }
+
+ // LINE COMMENT
+ if (stream.match(/#.*/)) {
+ return "comment";
+
+ // TAG
+ } else if (stream.match(/@\S+/)) {
+ return "def";
+
+ // FEATURE
+ } else if (state.allowFeature && stream.match(/Feature:/)) {
+ state.allowScenario = true;
+ state.allowBackground = true;
+ state.allowPlaceholders = false;
+ state.allowSteps = false;
+ return "keyword";
+
+ // BACKGROUND
+ } else if (state.allowBackground && stream.match("Background:")) {
+ state.allowPlaceholders = false;
+ state.allowSteps = true;
+ state.allowBackground = false;
+ return "keyword";
+
+ // SCENARIO OUTLINE
+ } else if (state.allowScenario && stream.match("Scenario Outline:")) {
+ state.allowPlaceholders = true;
+ state.allowSteps = true;
+ return "keyword";
+
+ // EXAMPLES
+ } else if (state.allowScenario && stream.match("Examples:")) {
+ state.allowPlaceholders = false;
+ state.allowSteps = true;
+ state.allowBackground = false;
+ state.inMultilineArgument = true;
+ return "keyword";
+
+ // SCENARIO
+ } else if (state.allowScenario && stream.match(/Scenario:/)) {
+ state.allowPlaceholders = false;
+ state.allowSteps = true;
+ state.allowBackground = false;
+ return "keyword";
+
+ // STEPS
+ } else if (state.allowSteps && stream.match(/(Given|When|Then|And|But)/)) {
+ return "keyword";
+
+ // INLINE STRING
+ } else if (!state.inMultilineArgument && stream.match(/"/)) {
+ stream.match(/.*?"/);
+ return "string";
+
+ // MULTILINE ARGUMENTS
+ } else if (state.allowSteps && stream.eat(":")) {
+ if (stream.match(/\s*$/)) {
+ state.inMultilineArgument = true;
+ return "keyword";
+ } else {
+ return null;
+ }
+
+ } else if (state.allowSteps && stream.match("<")) {
+ if (stream.match(/.*?>/)) {
+ return "property";
+ } else {
+ return null;
+ }
+
+ // Fall through
+ } else {
+ stream.eatWhile(/[^":<]/);
+ }
+
+ return null;
+ }
+ };
+});
+
+CodeMirror.defineMIME("text/x-feature", "gherkin");
diff --git a/mode/gherkin/index.html b/mode/gherkin/index.html
new file mode 100644
index 0000000000..b76877acd6
--- /dev/null
+++ b/mode/gherkin/index.html
@@ -0,0 +1,48 @@
+
+
+CodeMirror: Gherkin mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Gherkin mode
+
+Feature: Using Google
+ Background:
+ Something something
+ Something else
+ Scenario: Has a homepage
+ When I navigate to the google home page
+ Then the home page should contain the menu and the search form
+ Scenario: Searching for a term
+ When I navigate to the google home page
+ When I search for Tofu
+ Then the search results page is displayed
+ Then the search results page contains 10 individual search results
+ Then the search results contain a link to the wikipedia tofu page
+
+
+
+ MIME types defined: text/x-feature.
+
+
diff --git a/mode/index.html b/mode/index.html
index 063e69b6c7..cbdef24f09 100644
--- a/mode/index.html
+++ b/mode/index.html
@@ -43,9 +43,11 @@
diff
DTD
ECL
+ Eiffel
Erlang
Fortran
Gas (AT&T-style assembly)
+ Gherkin
Go
Groovy
HAML
diff --git a/mode/less/less.js b/mode/less/less.js
index 8384b3cd76..ec62319080 100644
--- a/mode/less/less.js
+++ b/mode/less/less.js
@@ -32,9 +32,9 @@ CodeMirror.defineMode("less", function(config) {
return tokenSComment(stream, state);
} else {
if(type == "string" || type == "(") return ret("string", "string");
- if(state.stack[state.stack.length-1] != undefined) return ret(null, ch);
+ if(state.stack[state.stack.length-1] !== undefined) return ret(null, ch);
stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/);
- if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() == ")")) || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
+ if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() === ")")) || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
}
} else if (ch == "!") {
stream.match(/^\s*\w*/);
@@ -66,10 +66,11 @@ CodeMirror.defineMode("less", function(config) {
return ret(null, ch);
}
} else if (ch == ".") {
- if(type == "(" || type == "string")return ret("string", "string"); // allow url(../image.png)
+ if(type == "(")return ret("string", "string"); // allow url(../image.png)
stream.eatWhile(/[\a-zA-Z0-9\-_]/);
- if(stream.peek() == " ")stream.eatSpace();
- if(stream.peek() == ")")return ret("number", "unit");//rgba(0,0,0,.25);
+ if(stream.peek() === " ")stream.eatSpace();
+ if(stream.peek() === ")" || type === ":")return ret("number", "unit");//rgba(0,0,0,.25);
+ else if(state.stack[state.stack.length-1] === "rule" && stream.peek().match(/{|,|\+|\(/) === null)return ret("number", "unit");
return ret("tag", "tag");
} else if (ch == "#") {
//we don't eat white-space, we want the hex color and or id only
@@ -82,21 +83,24 @@ CodeMirror.defineMode("less", function(config) {
//eat white-space
stream.eatSpace();
//when hex value declaration doesn't end with [;,] but is does with a slash/cc comment treat it as an id, just like the other hex values that don't end with[;,]
- if( /[\/<>.(){!$%^&*_\-\\?=+\|#'~`]/.test(stream.peek()) )return ret("atom", "tag");
+ if( /[\/<>.(){!$%^&*_\-\\?=+\|#'~`]/.test(stream.peek()) ){
+ if(type === "select-op")return ret("number", "unit"); else return ret("atom", "tag");
+ }
//#time { color: #aaa }
else if(stream.peek() == "}" )return ret("number", "unit");
//we have a valid hex color value, parse as id whenever an element/class is defined after the hex(id) value e.g. #eee aaa || #eee .aaa
- else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag");
+ else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag");
//when a hex value is on the end of a line, parse as id
- else if(stream.eol())return ret("atom", "tag");
+ else if(stream.eol())return ret("atom", "tag");
//default
- else return ret("number", "unit");
+ else return ret("number", "unit");
} else {//when not a valid hexvalue in the current stream e.g. #footer
stream.eatWhile(/[\w\\\-]/);
- return ret("atom", "tag");
+ return ret("atom", stream.current());
}
} else {//when not a valid hexvalue length
stream.eatWhile(/[\w\\\-]/);
+ if(state.stack[state.stack.length-1] === "rule")return ret("atom", stream.current());return ret("atom", stream.current());
return ret("atom", "tag");
}
} else if (ch == "&") {
@@ -104,17 +108,33 @@ CodeMirror.defineMode("less", function(config) {
return ret(null, ch);
} else {
stream.eatWhile(/[\w\\\-_%.{]/);
- if(type == "string"){
- return ret("string", "string");
+ if(stream.current().match(/\\/) !== null){
+ if(stream.current().charAt(stream.current().length-1) === "\\"){
+ stream.eat(/\'|\"|\)|\(/);
+ while(stream.eatWhile(/[\w\\\-_%.{]/)){
+ stream.eat(/\'|\"|\)|\(/);
+ }
+ return ret("string", stream.current());
+ }
+ } //else if(type === "tag")return ret("tag", "tag");
+ else if(type == "string"){
+ if(state.stack[state.stack.length-1] === "{" && stream.peek() === ":")return ret("variable", "variable");
+ if(stream.peek() === "/")stream.eatWhile(/[\w\\\-_%.{:\/]/);
+ return ret(type, stream.current());
} else if(stream.current().match(/(^http$|^https$)/) != null){
stream.eatWhile(/[\w\\\-_%.{:\/]/);
+ if(stream.peek() === "/")stream.eatWhile(/[\w\\\-_%.{:\/]/);
return ret("string", "string");
} else if(stream.peek() == "<" || stream.peek() == ">" || stream.peek() == "+"){
+ if(type === "(" && (stream.current() === "n" || stream.current() === "-n"))return ret("string", stream.current());
return ret("tag", "tag");
} else if( /\(/.test(stream.peek()) ){
+ if(stream.current() === "when")return ret("variable","variable");
+ else if(state.stack[state.stack.length-1] === "@media" && stream.current() === "and")return ret("variable",stream.current());
return ret(null, ch);
- } else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png)
- return ret("string", "string");
+ } else if (stream.peek() == "/" && state.stack[state.stack.length-1] !== undefined){ // url(dir/center/image.png)
+ if(stream.peek() === "/")stream.eatWhile(/[\w\\\-_%.{:\/]/);
+ return ret("string", stream.current());
} else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign
//commment out these 2 comment if you want the minus sign to be parsed as null -500px
//stream.backUp(stream.current().length-1);
@@ -129,14 +149,33 @@ CodeMirror.defineMode("less", function(config) {
if( /[{<>.a-zA-Z\/]/.test(stream.peek()) || stream.eol() )return ret("tag", "tag"); // e.g. button.icon-plus
return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
} else if( stream.eol() || stream.peek() == "[" || stream.peek() == "#" || type == "tag" ){
+
if(stream.current().substring(stream.current().length-1,stream.current().length) == "{")stream.backUp(1);
+ else if(state.stack[state.stack.length-1] === "border-color" || state.stack[state.stack.length-1] === "background-position" || state.stack[state.stack.length-1] === "font-family")return ret(null, stream.current());
+ else if(type === "tag")return ret("tag", "tag");
+ else if((type === ":" || type === "unit") && state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());
+ else if(state.stack[state.stack.length-1] === "rule" && type === "tag")return ret("string", stream.current());
+ else if(state.stack[state.stack.length-1] === ";" && type === ":")return ret(null, stream.current());
+ //else if(state.stack[state.stack.length-1] === ";" || type === "")return ret("variable", stream.current());
+ else if(stream.peek() === "#" && type !== undefined && type.match(/\+|,|tag|select\-op|}|{|;/g) === null)return ret("string", stream.current());
+ else if(type === "variable")return ret(null, stream.current());
+ else if(state.stack[state.stack.length-1] === "{" && type === "comment")return ret("variable", stream.current());
+ else if(state.stack.length === 0 && (type === ";" || type === "comment"))return ret("tag", stream.current());
+ else if((state.stack[state.stack.length-1] === "{" || type === ";") && state.stack[state.stack.length-1] !== "@media{")return ret("variable", stream.current());
+ else if(state.stack[state.stack.length-2] === "{" && state.stack[state.stack.length-1] === ";")return ret("variable", stream.current());
+
return ret("tag", "tag");
} else if(type == "compare" || type == "a" || type == "("){
return ret("string", "string");
} else if(type == "|" || stream.current() == "-" || type == "["){
- if(type == "|" )return ret("tag", "tag");
+ if(type == "|" && stream.peek().match(/\]|=|\~/) !== null)return ret("number", stream.current());
+ else if(type == "|" )return ret("tag", "tag");
+ else if(type == "["){
+ stream.eatWhile(/\w\-/);
+ return ret("number", stream.current());
+ }
return ret(null, ch);
- } else if(stream.peek() == ":") {
+ } else if((stream.peek() == ":") || ( stream.eatSpace() && stream.peek() == ":")) {
stream.next();
var t_v = stream.peek() == ":" ? true : false;
if(!t_v){
@@ -152,11 +191,50 @@ CodeMirror.defineMode("less", function(config) {
stream.backUp(1);
}
if(t_v)return ret("tag", "tag"); else return ret("variable", "variable");
- } else if(state.stack[state.stack.length-1] === "font-family"){
+ } else if(state.stack[state.stack.length-1] === "font-family" || state.stack[state.stack.length-1] === "background-position" || state.stack[state.stack.length-1] === "border-color"){
return ret(null, null);
} else {
- if(state.stack[state.stack.length-1] === "{" || type === "select-op" || (state.stack[state.stack.length-1] === "rule" && type === ",") )return ret("tag", "tag");
- return ret("variable", "variable");
+
+ if(state.stack[state.stack.length-1] === null && type === ":")return ret(null, stream.current());
+
+ //else if((type === ")" && state.stack[state.stack.length-1] === "rule") || (state.stack[state.stack.length-2] === "{" && state.stack[state.stack.length-1] === "rule" && type === "variable"))return ret(null, stream.current());
+
+ else if(/\^|\$/.test(stream.current()) && stream.peek().match(/\~|=/) !== null)return ret("string", "string");//att^=val
+
+ else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
+ else if(type === "unit" && state.stack[state.stack.length-1] === ";")return ret(null, "unit");
+ else if(type === ")" && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
+ else if(type.match("@") !== null && state.stack[state.stack.length-1] === "rule")return ret(null, "unit");
+ //else if(type === "unit" && state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());
+
+ 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 === "@media" && state.stack[state.stack.length-1] === "@media") || type === "@namespace")return ret("tag", stream.current());
+
+ else if(type === "{" && state.stack[state.stack.length-1] === ";" && stream.peek() === "{")return ret("tag", "tag");
+ 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(type === "variable" && state.stack[state.stack.length-1] === "rule")return ret("tag", "tag");
+ else if((stream.eatSpace() && stream.peek() === "{") || stream.eol() || stream.peek() === "{")return ret("tag", "tag");
+ //this one messes up indentation
+ //else if((type === "}" && stream.peek() !== ":") || (type === "}" && stream.eatSpace() && stream.peek() !== ":"))return(type, "tag");
+
+ else if(type === ")" && (stream.current() == "and" || stream.current() == "and "))return ret("variable", "variable");
+ else if(type === ")" && (stream.current() == "when" || stream.current() == "when "))return ret("variable", "variable");
+ else if(type === ")" || type === "comment" || type === "{")return ret("tag", "tag");
+ else if(stream.sol())return ret("tag", "tag");
+ else if((stream.eatSpace() && stream.peek() === "#") || 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.eatSpace();return ret(null, stream.current());}
+
+ else if(stream.current() === "and " || stream.current() === "and")return ret("variable", stream.current());
+ else if(type === ";" && state.stack[state.stack.length-1] === "{")return ret("variable", stream.current());
+
+ else if(state.stack[state.stack.length-1] === "rule")return ret(null, stream.current());
+
+ return ret("tag", stream.current());
}
}
}
@@ -234,20 +312,30 @@ CodeMirror.defineMode("less", function(config) {
else if (type == "}") state.stack.pop();
else if (type == "@media") state.stack.push("@media");
else if (stream.current() === "font-family") state.stack[state.stack.length-1] = "font-family";
+ else if (stream.current() === "background-position") state.stack[state.stack.length-1] = "background-position";
+ else if (stream.current() === "border-color") state.stack[state.stack.length-1] = "border-color";
else if (context == "{" && type != "comment" && type !== "tag") state.stack.push("rule");
else if (stream.peek() === ":" && stream.current().match(/@|#/) === null) style = type;
+ if(type === ";" && (state.stack[state.stack.length-1] == "font-family" || state.stack[state.stack.length-1] == "background-position" || state.stack[state.stack.length-1] == "border-color"))state.stack[state.stack.length-1] = stream.current();
+ else if(type === "tag" && stream.peek() === ")" && stream.current().match(/\:/) === null){type = null; style = null;}
+ // ????
+ else if((type === "variable" && stream.peek() === ")") || (type === "variable" && stream.eatSpace() && stream.peek() === ")"))return ret(null,stream.current());
return style;
},
indent: function(state, textAfter) {
var n = state.stack.length;
-
if (/^\}/.test(textAfter))
- n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
+ n -= state.stack[state.stack.length-1] === "rule" ? 2 : 1;
+ else if (state.stack[state.stack.length-2] === "{")
+ n -= state.stack[state.stack.length-1] === "rule" ? 1 : 0;
return state.baseIndent + n * indentUnit;
},
- electricChars: "}"
+ electricChars: "}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ lineComment: "//"
};
});
diff --git a/mode/meta.js b/mode/meta.js
index ce51c8ae19..f6bbd1b45c 100644
--- a/mode/meta.js
+++ b/mode/meta.js
@@ -15,11 +15,13 @@ CodeMirror.modeInfo = [
{name: 'diff', mime: 'text/x-diff', mode: 'diff'},
{name: 'DTD', mime: 'application/xml-dtd', mode: 'dtd'},
{name: 'ECL', mime: 'text/x-ecl', mode: 'ecl'},
+ {name: 'Eiffel', mime: 'text/x-eiffel', mode: 'eiffel'},
{name: 'Erlang', mime: 'text/x-erlang', mode: 'erlang'},
{name: 'Fortran', mime: 'text/x-fortran', mode: 'fortran'},
{name: 'Gas', mime: 'text/x-gas', mode: 'gas'},
+ {name: 'Gherkin', mime: 'text/x-feature', mode: 'gherkin'},
{name: 'GitHub Flavored Markdown', mime: 'text/x-gfm', mode: 'gfm'},
- {name: 'GO', mime: 'text/x-go', mode: 'go'},
+ {name: 'Go', mime: 'text/x-go', mode: 'go'},
{name: 'Groovy', mime: 'text/x-groovy', mode: 'groovy'},
{name: 'HAML', mime: 'text/x-haml', mode: 'haml'},
{name: 'Haskell', mime: 'text/x-haskell', mode: 'haskell'},
diff --git a/mode/php/php.js b/mode/php/php.js
old mode 100644
new mode 100755
index fa0db5b1fe..3555b8b145
--- a/mode/php/php.js
+++ b/mode/php/php.js
@@ -18,10 +18,10 @@
"for foreach function global goto if implements interface instanceof namespace " +
"new or private protected public static switch throw trait try use var while xor " +
"die echo empty exit eval include include_once isset list require require_once return " +
- "print unset __halt_compiler self static parent"),
- blockKeywords: keywords("catch do else elseif for foreach if switch try while"),
- atoms: keywords("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__"),
- builtin: keywords("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport echo print global static exit array empty eval isset unset die include require include_once require_once"),
+ "print unset __halt_compiler self static parent yield insteadof finally"),
+ blockKeywords: keywords("catch do else elseif for foreach if switch try while finally"),
+ atoms: keywords("true false null TRUE FALSE NULL __CLASS__ __DIR__ __FILE__ __LINE__ __METHOD__ __FUNCTION__ __NAMESPACE__ __TRAIT__"),
+ builtin: keywords("func_num_args func_get_arg func_get_args strlen strcmp strncmp strcasecmp strncasecmp each error_reporting define defined trigger_error user_error set_error_handler restore_error_handler get_declared_classes get_loaded_extensions extension_loaded get_extension_funcs debug_backtrace constant bin2hex hex2bin sleep usleep time mktime gmmktime strftime gmstrftime strtotime date gmdate getdate localtime checkdate flush wordwrap htmlspecialchars htmlentities html_entity_decode md5 md5_file crc32 getimagesize image_type_to_mime_type phpinfo phpversion phpcredits strnatcmp strnatcasecmp substr_count strspn strcspn strtok strtoupper strtolower strpos strrpos strrev hebrev hebrevc nl2br basename dirname pathinfo stripslashes stripcslashes strstr stristr strrchr str_shuffle str_word_count strcoll substr substr_replace quotemeta ucfirst ucwords strtr addslashes addcslashes rtrim str_replace str_repeat count_chars chunk_split trim ltrim strip_tags similar_text explode implode setlocale localeconv parse_str str_pad chop strchr sprintf printf vprintf vsprintf sscanf fscanf parse_url urlencode urldecode rawurlencode rawurldecode readlink linkinfo link unlink exec system escapeshellcmd escapeshellarg passthru shell_exec proc_open proc_close rand srand getrandmax mt_rand mt_srand mt_getrandmax base64_decode base64_encode abs ceil floor round is_finite is_nan is_infinite bindec hexdec octdec decbin decoct dechex base_convert number_format fmod ip2long long2ip getenv putenv getopt microtime gettimeofday getrusage uniqid quoted_printable_decode set_time_limit get_cfg_var magic_quotes_runtime set_magic_quotes_runtime get_magic_quotes_gpc get_magic_quotes_runtime import_request_variables error_log serialize unserialize memory_get_usage var_dump var_export debug_zval_dump print_r highlight_file show_source highlight_string ini_get ini_get_all ini_set ini_alter ini_restore get_include_path set_include_path restore_include_path setcookie header headers_sent connection_aborted connection_status ignore_user_abort parse_ini_file is_uploaded_file move_uploaded_file intval floatval doubleval strval gettype settype is_null is_resource is_bool is_long is_float is_int is_integer is_double is_real is_numeric is_string is_array is_object is_scalar ereg ereg_replace eregi eregi_replace split spliti join sql_regcase dl pclose popen readfile rewind rmdir umask fclose feof fgetc fgets fgetss fread fopen fpassthru ftruncate fstat fseek ftell fflush fwrite fputs mkdir rename copy tempnam tmpfile file file_get_contents stream_select stream_context_create stream_context_set_params stream_context_set_option stream_context_get_options stream_filter_prepend stream_filter_append fgetcsv flock get_meta_tags stream_set_write_buffer set_file_buffer set_socket_blocking stream_set_blocking socket_set_blocking stream_get_meta_data stream_register_wrapper stream_wrapper_register stream_set_timeout socket_set_timeout socket_get_status realpath fnmatch fsockopen pfsockopen pack unpack get_browser crypt opendir closedir chdir getcwd rewinddir readdir dir glob fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype file_exists is_writable is_writeable is_readable is_executable is_file is_dir is_link stat lstat chown touch clearstatcache mail ob_start ob_flush ob_clean ob_end_flush ob_end_clean ob_get_flush ob_get_clean ob_get_length ob_get_level ob_get_status ob_get_contents ob_implicit_flush ob_list_handlers ksort krsort natsort natcasesort asort arsort sort rsort usort uasort uksort shuffle array_walk count end prev next reset current key min max in_array array_search extract compact array_fill range array_multisort array_push array_pop array_shift array_unshift array_splice array_slice array_merge array_merge_recursive array_keys array_values array_count_values array_reverse array_reduce array_pad array_flip array_change_key_case array_rand array_unique array_intersect array_intersect_assoc array_diff array_diff_assoc array_sum array_filter array_map array_chunk array_key_exists pos sizeof key_exists assert assert_options version_compare ftok str_rot13 aggregate session_name session_module_name session_save_path session_id session_regenerate_id session_decode session_register session_unregister session_is_registered session_encode session_start session_destroy session_unset session_set_save_handler session_cache_limiter session_cache_expire session_set_cookie_params session_get_cookie_params session_write_close preg_match preg_match_all preg_replace preg_replace_callback preg_split preg_quote preg_grep overload ctype_alnum ctype_alpha ctype_cntrl ctype_digit ctype_lower ctype_graph ctype_print ctype_punct ctype_space ctype_upper ctype_xdigit virtual apache_request_headers apache_note apache_lookup_uri apache_child_terminate apache_setenv apache_response_headers apache_get_version getallheaders mysql_connect mysql_pconnect mysql_close mysql_select_db mysql_create_db mysql_drop_db mysql_query mysql_unbuffered_query mysql_db_query mysql_list_dbs mysql_list_tables mysql_list_fields mysql_list_processes mysql_error mysql_errno mysql_affected_rows mysql_insert_id mysql_result mysql_num_rows mysql_num_fields mysql_fetch_row mysql_fetch_array mysql_fetch_assoc mysql_fetch_object mysql_data_seek mysql_fetch_lengths mysql_fetch_field mysql_field_seek mysql_free_result mysql_field_name mysql_field_table mysql_field_len mysql_field_type mysql_field_flags mysql_escape_string mysql_real_escape_string mysql_stat mysql_thread_id mysql_client_encoding mysql_get_client_info mysql_get_host_info mysql_get_proto_info mysql_get_server_info mysql_info mysql mysql_fieldname mysql_fieldtable mysql_fieldlen mysql_fieldtype mysql_fieldflags mysql_selectdb mysql_createdb mysql_dropdb mysql_freeresult mysql_numfields mysql_numrows mysql_listdbs mysql_listtables mysql_listfields mysql_db_name mysql_dbname mysql_tablename mysql_table_name pg_connect pg_pconnect pg_close pg_connection_status pg_connection_busy pg_connection_reset pg_host pg_dbname pg_port pg_tty pg_options pg_ping pg_query pg_send_query pg_cancel_query pg_fetch_result pg_fetch_row pg_fetch_assoc pg_fetch_array pg_fetch_object pg_fetch_all pg_affected_rows pg_get_result pg_result_seek pg_result_status pg_free_result pg_last_oid pg_num_rows pg_num_fields pg_field_name pg_field_num pg_field_size pg_field_type pg_field_prtlen pg_field_is_null pg_get_notify pg_get_pid pg_result_error pg_last_error pg_last_notice pg_put_line pg_end_copy pg_copy_to pg_copy_from pg_trace pg_untrace pg_lo_create pg_lo_unlink pg_lo_open pg_lo_close pg_lo_read pg_lo_write pg_lo_read_all pg_lo_import pg_lo_export pg_lo_seek pg_lo_tell pg_escape_string pg_escape_bytea pg_unescape_bytea pg_client_encoding pg_set_client_encoding pg_meta_data pg_convert pg_insert pg_update pg_delete pg_select pg_exec pg_getlastoid pg_cmdtuples pg_errormessage pg_numrows pg_numfields pg_fieldname pg_fieldsize pg_fieldtype pg_fieldnum pg_fieldprtlen pg_fieldisnull pg_freeresult pg_result pg_loreadall pg_locreate pg_lounlink pg_loopen pg_loclose pg_loread pg_lowrite pg_loimport pg_loexport http_response_code get_declared_traits getimagesizefromstring socket_import_stream stream_set_chunk_size trait_exists header_register_callback class_uses session_status session_register_shutdown echo print global static exit array empty eval isset unset die include require include_once require_once"),
multiLineStrings: true,
hooks: {
"$": function(stream) {
diff --git a/mode/smartymixed/index.html b/mode/smartymixed/index.html
index 3de9477768..47c805faca 100644
--- a/mode/smartymixed/index.html
+++ b/mode/smartymixed/index.html
@@ -38,7 +38,7 @@
{$title|htmlspecialchars|truncate:30}
-
+
{* Multiline smarty
* comment, no {$variables} here
*}
diff --git a/mode/smartymixed/smartymixed.js b/mode/smartymixed/smartymixed.js
old mode 100644
new mode 100755
index c5d008885a..a033ab04fa
--- a/mode/smartymixed/smartymixed.js
+++ b/mode/smartymixed/smartymixed.js
@@ -58,7 +58,12 @@ CodeMirror.defineMode("smartymixed", function(config) {
parsers = {
html: function(stream, state) {
- if (!state.inLiteral && stream.match(regs.htmlHasLeftDelimeter, false)) {
+ if (!state.inLiteral && stream.match(regs.htmlHasLeftDelimeter, false) && state.htmlMixedState.htmlState.tagName === null) {
+ state.tokenize = parsers.smarty;
+ state.localMode = smartyMode;
+ state.localState = smartyMode.startState(htmlMixedMode.indent(state.htmlMixedState, ""));
+ return helpers.maybeBackup(stream, settings.leftDelimiter, smartyMode.token(stream, state.localState));
+ } else if (!state.inLiteral && stream.match(settings.leftDelimiter, false)) {
state.tokenize = parsers.smarty;
state.localMode = smartyMode;
state.localState = smartyMode.startState(htmlMixedMode.indent(state.htmlMixedState, ""));
diff --git a/mode/sql/index.html b/mode/sql/index.html
index 0dbdb6722c..e6882d79c1 100644
--- a/mode/sql/index.html
+++ b/mode/sql/index.html
@@ -51,7 +51,8 @@
text/x-mysql ,
text/x-mariadb ,
text/x-cassandra ,
- text/x-plsql .
+ text/x-plsql ,
+ text/x-mssql .