145 changes: 94 additions & 51 deletions addon/hint/sql-hint.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,26 @@
return CodeMirror.resolveMode(mode).keywords;
}

function getText(item) {
return typeof item == "string" ? item : item.text;
}

function getItem(list, item) {
if (!list.slice) return list[item];
for (var i = list.length - 1; i >= 0; i--) if (getText(list[i]) == item)
return list[i];
}

function shallowClone(object) {
var result = {};
for (var key in object) if (object.hasOwnProperty(key))
result[key] = object[key];
return result;
}

function match(string, word) {
var len = string.length;
var sub = word.substr(0, len);
var sub = getText(word).substr(0, len);
return string.toUpperCase() === sub.toUpperCase();
}

Expand All @@ -44,53 +61,81 @@
}
}

function cleanName(name) {
// Get rid name from backticks(`) and preceding dot(.)
if (name.charAt(0) == ".") {
name = name.substr(1);
}
return name.replace(/`/g, "");
}

function insertBackticks(name) {
var nameParts = getText(name).split(".");
for (var i = 0; i < nameParts.length; i++)
nameParts[i] = "`" + nameParts[i] + "`";
var escaped = nameParts.join(".");
if (typeof name == "string") return escaped;
name = shallowClone(name);
name.text = escaped;
return name;
}

function nameCompletion(cur, token, result, editor) {
var useBacktick = (token.string.charAt(0) == "`");
var string = token.string.substr(1);
var prevToken = editor.getTokenAt(Pos(cur.line, token.start));
if (token.string.charAt(0) == "." || prevToken.string == "."){
//Suggest colunm names
if (prevToken.string == ".") {
var prevToken = editor.getTokenAt(Pos(cur.line, token.start - 1));
}
var table = prevToken.string;
//Check if backtick is used in table name. If yes, use it for columns too.
var useBacktickTable = false;
if (table.match(/`/g)) {
useBacktickTable = true;
table = table.replace(/`/g, "");
}
//Check if table is available. If not, find table by Alias
if (!tables.hasOwnProperty(table))
table = findTableByAlias(table, editor);
var columns = tables[table];
if (!columns) return;

if (useBacktick) {
addMatches(result, string, columns, function(w) {return "`" + w + "`";});
}
else if(useBacktickTable) {
addMatches(result, string, columns, function(w) {return ".`" + w + "`";});
}
else {
addMatches(result, string, columns, function(w) {return "." + w;});
// Try to complete table, colunm names and return start position of completion
var useBacktick = false;
var nameParts = [];
var start = token.start;
var cont = true;
while (cont) {
cont = (token.string.charAt(0) == ".");
useBacktick = useBacktick || (token.string.charAt(0) == "`");

start = token.start;
nameParts.unshift(cleanName(token.string));

token = editor.getTokenAt(Pos(cur.line, token.start));
if (token.string == ".") {
cont = true;
token = editor.getTokenAt(Pos(cur.line, token.start));
}
}
else {
//Suggest table names or colums in defaultTable
while (token.start && string.charAt(0) == ".") {
token = editor.getTokenAt(Pos(cur.line, token.start - 1));
string = token.string + string;
}
if (useBacktick) {
addMatches(result, string, tables, function(w) {return "`" + w + "`";});
addMatches(result, string, defaultTable, function(w) {return "`" + w + "`";});
}
else {
addMatches(result, string, tables, function(w) {return w;});
addMatches(result, string, defaultTable, function(w) {return w;});
}

// Try to complete table names
var string = nameParts.join(".");
addMatches(result, string, tables, function(w) {
return useBacktick ? insertBackticks(w) : w;
});

// Try to complete columns from defaultTable
addMatches(result, string, defaultTable, function(w) {
return useBacktick ? insertBackticks(w) : w;
});

// Try to complete columns
string = nameParts.pop();
var table = nameParts.join(".");

// Check if table is available. If not, find table by Alias
if (!getItem(tables, table))
table = findTableByAlias(table, editor);

var columns = getItem(tables, table);
if (columns && Array.isArray(tables) && columns.columns)
columns = columns.columns;

if (columns) {
addMatches(result, string, columns, function(w) {
if (typeof w == "string") {
w = table + "." + w;
} else {
w = shallowClone(w);
w.text = table + "." + w.text;
}
return useBacktick ? insertBackticks(w) : w;
});
}

return start;
}

function eachWord(lineText, f) {
Expand Down Expand Up @@ -150,12 +195,10 @@
var lineText = query[i];
eachWord(lineText, function(word) {
var wordUpperCase = word.toUpperCase();
if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
table = previousWord;
}
if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
if (wordUpperCase === aliasUpperCase && getItem(tables, previousWord))
table = previousWord;
if (wordUpperCase !== CONS.ALIAS_KEYWORD)
previousWord = word;
}
});
if (table) break;
}
Expand All @@ -165,7 +208,7 @@
CodeMirror.registerHelper("hint", "sql", function(editor, options) {
tables = (options && options.tables) || {};
var defaultTableName = options && options.defaultTable;
defaultTable = (defaultTableName && tables[defaultTableName] || []);
defaultTable = (defaultTableName && getItem(tables, defaultTableName)) || [];
keywords = keywords || getKeywords(editor);

var cur = editor.getCursor();
Expand All @@ -185,7 +228,7 @@
search = "";
}
if (search.charAt(0) == "." || search.charAt(0) == "`") {
nameCompletion(cur, token, result, editor);
start = nameCompletion(cur, token, result, editor);
} else {
addMatches(result, search, tables, function(w) {return w;});
addMatches(result, search, defaultTable, function(w) {return w;});
Expand Down
3 changes: 2 additions & 1 deletion addon/lint/lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
}
var poll = setInterval(function() {
if (tooltip) for (var n = node;; n = n.parentNode) {
if (n && n.nodeType == 11) n = n.host;
if (n == document.body) return;
if (!n) { hide(); break; }
}
Expand Down Expand Up @@ -119,7 +120,7 @@
function startLinting(cm) {
var state = cm.state.lint, options = state.options;
var passOptions = options.options || options; // Support deprecated passing of `options` property in options
if (options.async)
if (options.async || options.getAnnotations.async)
options.getAnnotations(cm.getValue(), updateLinting, passOptions, cm);
else
updateLinting(cm, options.getAnnotations(cm.getValue(), passOptions, cm));
Expand Down
334 changes: 211 additions & 123 deletions addon/merge/merge.js

Large diffs are not rendered by default.

48 changes: 36 additions & 12 deletions addon/scroll/annotatescrollbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,46 @@
})(function(CodeMirror) {
"use strict";

CodeMirror.defineExtension("annotateScrollbar", function(className) {
return new Annotation(this, className);
CodeMirror.defineExtension("annotateScrollbar", function(options) {
if (typeof options == "string") options = {className: options};
return new Annotation(this, options);
});

function Annotation(cm, className) {
CodeMirror.defineOption("scrollButtonHeight", 0);

function Annotation(cm, options) {
this.cm = cm;
this.className = className;
this.options = options;
this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
this.annotations = [];
this.doRedraw = this.doUpdate = null;
this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
this.computeScale();

function scheduleRedraw(delay) {
clearTimeout(self.doRedraw);
self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
}

var self = this;
cm.on("refresh", this.resizeHandler = function(){
if (self.computeScale()) self.redraw();
cm.on("refresh", this.resizeHandler = function() {
clearTimeout(self.doUpdate);
self.doUpdate = setTimeout(function() {
if (self.computeScale()) scheduleRedraw(20);
}, 100);
});
cm.on("markerAdded", this.resizeHandler);
cm.on("markerCleared", this.resizeHandler);
if (options.listenForChanges !== false)
cm.on("change", this.changeHandler = function() {
scheduleRedraw(250);
});
}

Annotation.prototype.computeScale = function() {
var cm = this.cm;
var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight) /
var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
cm.heightAtLine(cm.lastLine() + 1, "local");
if (hScale != this.hScale) {
this.hScale = hScale;
Expand All @@ -44,12 +63,12 @@
this.redraw();
};

Annotation.prototype.redraw = function() {
Annotation.prototype.redraw = function(compute) {
if (compute !== false) this.computeScale();
var cm = this.cm, hScale = this.hScale;
if (!cm.display.barWidth) return;

var frag = document.createDocumentFragment(), anns = this.annotations;
for (var i = 0, nextTop; i < anns.length; i++) {
if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
var ann = anns[i];
var top = nextTop || cm.charCoords(ann.from, "local").top * hScale;
var bottom = cm.charCoords(ann.to, "local").bottom * hScale;
Expand All @@ -59,18 +78,23 @@
ann = anns[++i];
bottom = cm.charCoords(ann.to, "local").bottom * hScale;
}
if (bottom == top) continue;
var height = Math.max(bottom - top, 3);

var elt = frag.appendChild(document.createElement("div"));
elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: " + top + "px; height: " + height + "px";
elt.className = this.className;
elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
+ (top + this.buttonHeight) + "px; height: " + height + "px";
elt.className = this.options.className;
}
this.div.textContent = "";
this.div.appendChild(frag);
};

Annotation.prototype.clear = function() {
this.cm.off("refresh", this.resizeHandler);
this.cm.off("markerAdded", this.resizeHandler);
this.cm.off("markerCleared", this.resizeHandler);
if (this.changeHandler) this.cm.off("change", this.changeHandler);
this.div.parentNode.removeChild(this.div);
};
});
13 changes: 9 additions & 4 deletions addon/search/matchesonscrollbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
})(function(CodeMirror) {
"use strict";

CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, className) {
return new SearchAnnotation(this, query, caseFold, className);
CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
if (typeof options == "string") options = {className: options};
if (!options) options = {};
return new SearchAnnotation(this, query, caseFold, options);
});

function SearchAnnotation(cm, query, caseFold, className) {
function SearchAnnotation(cm, query, caseFold, options) {
this.cm = cm;
this.annotation = cm.annotateScrollbar(className || "CodeMirror-search-match");
var annotateOptions = {listenForChanges: false};
for (var prop in options) annotateOptions[prop] = options[prop];
if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
this.annotation = cm.annotateScrollbar(annotateOptions);
this.query = query;
this.caseFold = caseFold;
this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
Expand Down
3 changes: 3 additions & 0 deletions addon/selection/selection-pointer.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
if (data) {
CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove);
CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout);
CodeMirror.off(window, "scroll", data.windowScroll);
cm.off("cursorActivity", reset);
cm.off("scroll", reset);
cm.state.selectionPointer = null;
Expand All @@ -26,12 +27,14 @@
value: typeof val == "string" ? val : "default",
mousemove: function(event) { mousemove(cm, event); },
mouseout: function(event) { mouseout(cm, event); },
windowScroll: function() { reset(cm); },
rects: null,
mouseX: null, mouseY: null,
willUpdate: false
};
CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove);
CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout);
CodeMirror.on(window, "scroll", data.windowScroll);
cm.on("cursorActivity", reset);
cm.on("scroll", reset);
}
Expand Down
35 changes: 31 additions & 4 deletions addon/tern/tern.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@
data = self.options.responseFilter(doc, query, request, error, data);
c(error, data);
});
},

destroy: function () {
if (this.worker) {
this.worker.terminate();
this.worker = null;
}
}
};

Expand Down Expand Up @@ -252,7 +259,9 @@
tip.appendChild(document.createTextNode(" — " + data.doc));
if (data.url) {
tip.appendChild(document.createTextNode(" "));
tip.appendChild(elt("a", null, "[docs]")).href = data.url;
var child = tip.appendChild(elt("a", null, "[docs]"));
child.href = data.url;
child.target = "_blank";
}
}
tempTooltip(cm, tip);
Expand Down Expand Up @@ -582,15 +591,33 @@
// Tooltips

function tempTooltip(cm, content) {
if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
var where = cm.cursorCoords();
var tip = makeTooltip(where.right + 1, where.bottom, content);
var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content);
function maybeClear() {
old = true;
if (!mouseOnTip) clear();
}
function clear() {
cm.state.ternTooltip = null;
if (!tip.parentNode) return;
cm.off("cursorActivity", clear);
cm.off('blur', clear);
cm.off('scroll', clear);
fadeOut(tip);
}
setTimeout(clear, 1700);
var mouseOnTip = false, old = false;
CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
CodeMirror.on(tip, "mouseout", function(e) {
if (!CodeMirror.contains(tip, e.relatedTarget || e.toElement)) {
if (old) clear();
else mouseOnTip = false;
}
});
setTimeout(maybeClear, 1700);
cm.on("cursorActivity", clear);
cm.on('blur', clear);
cm.on('scroll', clear);
}

function makeTooltip(x, y, content) {
Expand Down Expand Up @@ -631,7 +658,7 @@
// Worker wrapper

function WorkerServer(ts) {
var worker = new Worker(ts.options.workerScript);
var worker = ts.worker = new Worker(ts.options.workerScript);
worker.postMessage({type: "init",
defs: ts.options.defs,
plugins: ts.options.plugins,
Expand Down
4 changes: 2 additions & 2 deletions bin/release
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ rewrite("doc/compress.html", function(cmp) {
});

rewrite("index.html", function(index) {
return index.replace(/<strong>version \d+\.\d+<\/strong>/,
"<strong>version " + simple + "</strong>");
return index.replace(/\.zip">\d+\.\d+<\/a>/,
".zip>" + simple + "</a>");
});
2 changes: 1 addition & 1 deletion bower.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"4.12.0",
"version":"4.13.0",
"main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [
"**/.*",
Expand Down
11 changes: 9 additions & 2 deletions demo/merge.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@
<link rel=stylesheet href="../addon/merge/merge.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/css/css.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/diff_match_patch/20121119/diff_match_patch.js"></script>
<script src="../addon/merge/merge.js"></script>
<style>
.CodeMirror { line-height: 1.2; }
@media screen and (min-width: 1300px) {
article { max-width: 1000px; }
#nav { border-right: 499px solid transparent; }
}
span.clicky {
cursor: pointer;
background: #d70;
Expand Down Expand Up @@ -64,7 +71,7 @@ <h2>merge view demo</h2>
target.innerHTML = "";
dv = CodeMirror.MergeView(target, {
value: value,
origLeft: panes == 3 && !collapse && !connect ? orig1 : null,
origLeft: panes == 3 ? orig1 : null,
orig: orig2,
lineNumbers: true,
mode: "text/html",
Expand All @@ -80,7 +87,7 @@ <h2>merge view demo</h2>

window.onload = function() {
value = document.documentElement.innerHTML;
orig1 = value.replace(/\.\.\//g, "codemirror/").replace("yellow", "orange");
orig1 = "<!doctype html>\n\n" + value.replace(/\.\.\//g, "codemirror/").replace("yellow", "orange");
orig2 = value.replace(/\u003cscript/g, "\u003cscript type=text/javascript ")
.replace("white", "purple;\n font: comic sans;\n text-decoration: underline;\n height: 15em");
initUI();
Expand Down
2 changes: 2 additions & 0 deletions demo/theme.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<link rel="stylesheet" href="../theme/base16-light.css">
<link rel="stylesheet" href="../theme/blackboard.css">
<link rel="stylesheet" href="../theme/cobalt.css">
<link rel="stylesheet" href="../theme/colorforth.css">
<link rel="stylesheet" href="../theme/eclipse.css">
<link rel="stylesheet" href="../theme/elegant.css">
<link rel="stylesheet" href="../theme/erlang-dark.css">
Expand Down Expand Up @@ -81,6 +82,7 @@ <h2>Theme Demo</h2>
<option>base16-light</option>
<option>blackboard</option>
<option>cobalt</option>
<option>colorforth</option>
<option>eclipse</option>
<option>elegant</option>
<option>erlang-dark</option>
Expand Down
3 changes: 3 additions & 0 deletions doc/compress.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ <h2>Script compression helper</h2>
<input type="hidden" id="download" name="download" value="codemirror-compressed.js"/>
<p>Version: <select id="version" onchange="setVersion(this);" style="padding: 1px;">
<option value="http://codemirror.net/">HEAD</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=4.13.0;f=">4.13</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=4.12.0;f=">4.12</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=4.11.0;f=">4.11</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=4.10.0;f=">4.10</option>
Expand Down Expand Up @@ -119,6 +120,7 @@ <h2>Script compression helper</h2>
<option value="http://codemirror.net/mode/ecl/ecl.js">ecl.js</option>
<option value="http://codemirror.net/mode/eiffel/eiffel.js">eiffel.js</option>
<option value="http://codemirror.net/mode/erlang/erlang.js">erlang.js</option>
<option value="http://codemirror.net/mode/forth/forth.js">forth.js</option>
<option value="http://codemirror.net/mode/fortran/fortran.js">fortran.js</option>
<option value="http://codemirror.net/mode/gfm/gfm.js">gfm.js</option>
<option value="http://codemirror.net/mode/gas/gas.js">gas.js</option>
Expand Down Expand Up @@ -173,6 +175,7 @@ <h2>Script compression helper</h2>
<option value="http://codemirror.net/mode/soy/soy.js">soy.js</option>
<option value="http://codemirror.net/mode/sparql/sparql.js">sparql.js</option>
<option value="http://codemirror.net/mode/spreadsheet/spreadsheet.js">spreadsheet.js</option>
<option value="http://codemirror.net/mode/stylus/stylus.js">stylus.js</option>
<option value="http://codemirror.net/mode/sql/sql.js">sql.js</option>
<option value="http://codemirror.net/mode/stex/stex.js">stex.js</option>
<option value="http://codemirror.net/mode/tcl/tcl.js">tcl.js</option>
Expand Down
88 changes: 59 additions & 29 deletions doc/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -148,49 +148,79 @@ section.first {
z-index: 25;
}

.bankinfo {
text-align: left;
display: none;
padding: 0 .5em;
.yinyang {
position: absolute;
border: 2px solid #aaa;
border-radius: 5px;
background: #eee;
top: 10px;
left: 30px;
top: -10px;
left: 0; right: 0;
margin: auto;
display: block;
height: 120px;
}

.actions {
margin: 1em 0 0;
min-height: 100px;
position: relative;
}

.bankinfo_close {
.actionspicture {
pointer-events: none;
position: absolute;
top: 0; right: 6px;
height: 100px;
top: 0; left: 0; right: 0;
}

.actionlink {
pointer-events: auto;
font-family: arial;
font-size: 80%;
font-weight: bold;
cursor: pointer;
position: absolute;
top: 0; bottom: 0;
line-height: 1;
height: 1em;
margin: auto;
}

.bigbutton {
cursor: pointer;
text-align: center;
padding: 0 1em;
display: inline-block;
.actionlink.download {
color: white;
position: relative;
line-height: 1.9;
color: white !important;
background: #A21313;
right: 50%;
margin-right: 13px;
text-shadow: -1px 1px 3px #b00, -1px -1px 3px #b00, 1px 0px 3px #b00;
}

.actionlink.fund {
color: #b00;
left: 50%;
margin-left: 15px;
}

.actionlink:hover {
text-decoration: underline;
}

.actionlink a {
color: inherit;
}

.bigbutton.right {
border-bottom-left-radius: 100px;
border-top-left-radius: 100px;
.actionsleft {
float: left;
}

.bigbutton.left {
border-bottom-right-radius: 100px;
border-top-right-radius: 100px;
.actionsright {
float: right;
text-align: right;
}

.bigbutton:hover {
background: #E30808;
@media screen and (max-width: 800px) {
.actions {
padding-top: 120px;
}
.actionsleft, .actionsright {
float: none;
text-align: left;
margin-bottom: 1em;
}
}

th {
Expand Down
2 changes: 1 addition & 1 deletion doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
<section class=first id=overview>
<h2 style="position: relative">
User manual and reference guide
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 4.12.0</span>
<span style="color: #888; font-size: 1rem; position: absolute; right: 0; bottom: 0">version 4.13.0</span>
</h2>

<p>CodeMirror is a code-editor component that can be embedded in
Expand Down
4 changes: 2 additions & 2 deletions doc/realworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
<h2>CodeMirror real-world uses</h2>

<p>Create a <a href="https://github.com/codemirror/codemirror">pull
request</a> or <a href="mailto:marijnh@gmail.com">email me</a> if
you'd like your project to be added to this list.</p>
request</a> if you'd like your project to be added to this list.</p>

<ul>
<li><a href="http://brackets.io">Adobe Brackets</a> (code editor)</li>
Expand Down Expand Up @@ -89,6 +88,7 @@ <h2>CodeMirror real-world uses</h2>
<li><a href="http://try.haxe.org">Haxe</a> (Haxe Playground) </li>
<li><a href="http://haxpad.com/">HaxPad</a> (editor for Win RT)</li>
<li><a href="http://megafonweblab.github.com/histone-javascript/">Histone template engine playground</a></li>
<li><a href="http://www.homegenie.it/docs/automation_getstarted.php">Homegenie</a> (home automation server)</li>
<li><a href="http://icecoder.net">ICEcoder</a> (web IDE)</li>
<li><a href="http://ipython.org/">IPython</a> (interactive computing shell)</li>
<li><a href="http://i-mos.org/imos/">i-MOS</a> (modeling and simulation platform)</li>
Expand Down
10 changes: 10 additions & 0 deletions doc/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ <h2>Release notes and version history</h2>

<h2>Version 4.x</h2>

<p class="rel">20-02-2015: <a href="http://codemirror.net/codemirror-4.13.zip">Version 4.13</a>:</p>

<ul class="rel-note">
<li>Fix the way the <a href="../demo/closetag.html"><code>closetag</code></a> demo handles the slash character.</li>
<li>New modes: <a href="../mode/forth/index.html">Forth</a>, <a href="../mode/stylus/index.html">Stylus</a>.</li>
<li>Make the <a href="../mode/css/index.html">CSS mode</a> understand some modern CSS extensions.</li>
<li>Have the <a href="../mode/clike/index.html">Scala mode</a> handle symbols and triple-quoted strings.</li>
<li>Full <a href="https://github.com/codemirror/CodeMirror/compare/4.12.0...4.13.0">list of patches</a>.</li>
</ul>

<p class="rel">22-01-2015: <a href="http://codemirror.net/codemirror-4.12.zip">Version 4.12</a>:</p>

<ul class="rel-note">
Expand Down
Binary file added doc/yinyang.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 27 additions & 31 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,46 +84,42 @@ <h2>This is CodeMirror</h2>
matchBrackets: true
});
</script>
<div style="position: relative; margin: 1em 0;">
<a class="bigbutton left" href="http://codemirror.net/codemirror.zip">DOWNLOAD LATEST RELEASE</a>
<div><strong>version 4.12</strong> (<a href="doc/releases.html">Release notes</a>)</div>
<div>or use the <a href="doc/compress.html">minification helper</a></div>
<div style="position: absolute; top: 0; right: 0; text-align: right">
<span class="bigbutton right" onclick="document.getElementById('paypal').submit();">DONATE WITH PAYPAL</span>
<div style="position: relative">
or <span onclick="document.getElementById('bankinfo').style.display = 'block';" class=quasilink>Bank</span>,
<span onclick="document.getElementById('bcinfo').style.display = 'block';" class=quasilink>Bitcoin</span>,
<a href="http://www.patreon.com/marijn">Patreon</a><br>
<a href="https://gratipay.com/marijnh/">Gratipay</a><br>
<div id=bankinfo class=bankinfo>
<span class=bankinfo_close onclick="document.getElementById('bankinfo').style.display = '';">×</span>
Bank: <i>Rabobank</i><br/>
Country: <i>Netherlands</i><br/>
SWIFT: <i>RABONL2U</i><br/>
Account: <i>147850770</i><br/>
Name: <i>Marijn Haverbeke</i><br/>
IBAN: <i>NL26 RABO 0147 8507 70</i>
</div>
<div id=bcinfo class=bankinfo>
<span class=bankinfo_close onclick="document.getElementById('bcinfo').style.display = '';">×</span>
Bitcoin address: 1HVnnU8E9yLPeFyNgNtUPB5deXBvUmZ6Nx
</div>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" id="paypal">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="3FVHS5FGUY7CC"/>
</form>

<div class=actions>
<div class=actionspicture>
<img src="doc/yinyang.png" class=yinyang>
<div class="actionlink download">
<a href="http://codemirror.net/codemirror.zip">DOWNLOAD</a>
</div>
<div>
Purchase <a href="http://codemirror.com">commercial support</a>
<div class="actionlink fund">
<a href="https://marijnhaverbeke.nl/fund/">FUND</a>
</div>
</div>
<div class=actionsleft>
Get the current version: <a href="http://codemirror.net/codemirror.zip>4.13</a>.<br>
You can see the <a href="https://github.com/codemirror/codemirror" title="Github repository">code</a> or<br>
read the <a href="doc/releases.html">release notes</a>.<br>
There is a <a href="doc/compress.html">minification helper</a>.
</div>
<div class=actionsright>
Software needs maintenance,<br>
maintainers need to subsist.<br>
Current funding status = <img src="https://marijnhaverbeke.nl/fund/status_s.png" title="Current maintainer happiness" style="vertical-align: middle; height: 16px; width: 16px"><br>
You can help <a href="https://marijnhaverbeke.nl/fund/" title="Set up a monthly contribution">per month</a> or
<a title="Donate with Paypal" href="javascript:document.getElementById('paypal').submit();">once</a>.
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" id="paypal">
<input type="hidden" name="cmd" value="_s-xclick"/>
<input type="hidden" name="hosted_button_id" value="3FVHS5FGUY7CC"/>
</form>
</div>
</div>

</section>

<section id=features>
<h2>Features</h2>
<ul>
<li>Support for <a href="mode/index.html">over 60 languages</a> out of the box
<li>Support for <a href="mode/index.html">over 90 languages</a> out of the box
<li>A powerful, <a href="mode/htmlmixed/index.html">composable</a> language mode <a href="doc/manual.html#modeapi">system</a>
<li><a href="doc/manual.html#addon_show-hint">Autocompletion</a> (<a href="demo/xmlcomplete.html">XML</a>)
<li><a href="doc/manual.html#addon_foldcode">Code folding</a>
Expand Down
53 changes: 46 additions & 7 deletions keymap/vim.js
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,16 @@
},
handleEx: function(cm, input) {
exCommandDispatcher.processCommand(cm, input);
}
},

defineMotion: defineMotion,
defineAction: defineAction,
defineOperator: defineOperator,
mapCommand: mapCommand,
_mapCommand: _mapCommand,

exitVisualMode: exitVisualMode,
exitInsertMode: exitInsertMode
};

// Represents the current input state.
Expand Down Expand Up @@ -1449,7 +1458,7 @@
var operatorMoveTo = operators[operator](
cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
if (vim.visualMode) {
exitVisualMode(cm);
exitVisualMode(cm, operatorMoveTo != null);
}
if (operatorMoveTo) {
cm.setCursor(operatorMoveTo);
Expand Down Expand Up @@ -1817,6 +1826,10 @@
}
};

function defineMotion(name, fn) {
motions[name] = fn;
}

function fillArray(val, times) {
var arr = [];
for (var i = 0; i < times; i++) {
Expand Down Expand Up @@ -1899,7 +1912,7 @@
vimGlobalState.registerController.pushText(
args.registerName, 'delete', text,
args.linewise, vim.visualBlock);
return finalHead;
return clipCursorToContent(cm, finalHead);
},
indent: function(cm, args, ranges) {
var vim = cm.state.vim;
Expand Down Expand Up @@ -1967,6 +1980,10 @@
}
};

function defineOperator(name, fn) {
operators[name] = fn;
}

var actions = {
jumpListWalk: function(cm, actionArgs, vim) {
if (vim.visualMode) {
Expand Down Expand Up @@ -2183,6 +2200,11 @@
if (vim.visualMode) {
curStart = cm.getCursor('anchor');
curEnd = cm.getCursor('head');
if (cursorIsBefore(curEnd, curStart)) {
var tmp = curEnd;
curEnd = curStart;
curStart = tmp;
}
curEnd.ch = lineLength(cm, curEnd.line) - 1;
} else {
// Repeat is the number of lines to join. Minimum 2 lines.
Expand All @@ -2201,10 +2223,10 @@
cm.replaceRange(text, curStart, tmp);
}
var curFinalPos = Pos(curStart.line, finalCh);
cm.setCursor(curFinalPos);
if (vim.visualMode) {
exitVisualMode(cm);
exitVisualMode(cm, false);
}
cm.setCursor(curFinalPos);
},
newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
vim.insertMode = true;
Expand Down Expand Up @@ -2367,7 +2389,7 @@
}
}
if (vim.visualMode) {
exitVisualMode(cm);
exitVisualMode(cm, false);
}
cm.setCursor(curPosFinal);
},
Expand Down Expand Up @@ -2425,7 +2447,7 @@
curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
selections[0].anchor : selections[0].head;
cm.setCursor(curStart);
exitVisualMode(cm);
exitVisualMode(cm, false);
} else {
cm.setCursor(offsetCursor(curEnd, 0, -1));
}
Expand Down Expand Up @@ -2473,6 +2495,10 @@
exitInsertMode: exitInsertMode
};

function defineAction(name, fn) {
actions[name] = fn;
}

/*
* Below are miscellaneous utility functions used by vim.js
*/
Expand Down Expand Up @@ -4627,6 +4653,19 @@
}
}

function _mapCommand(command) {
defaultKeymap.push(command);
}

function mapCommand(keys, type, name, args, extra) {
var command = {keys: keys, type: type};
command[type] = name;
command[type + "Args"] = args;
for (var key in extra)
command[key] = extra[key];
_mapCommand(command);
}

// The timeout in milliseconds for the two-character ESC keymap should be
// adjusted according to your typing speed to prevent false positives.
defineOption('insertModeEscKeysTimeout', 200, 'number');
Expand Down
67 changes: 45 additions & 22 deletions lib/codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@
for (var opt in optionHandlers) if (optionHandlers.hasOwnProperty(opt))
optionHandlers[opt](this, options[opt], Init);
maybeUpdateLineNumberWidth(this);
if (options.finishInit) options.finishInit(this);
for (var i = 0; i < initHooks.length; ++i) initHooks[i](this);
endOperation(this);
// Suppress optimizelegibility in Webkit, since it breaks text
Expand Down Expand Up @@ -649,8 +650,18 @@
this.oldDisplayWidth = displayWidth(cm);
this.force = force;
this.dims = getDimensions(cm);
this.events = [];
}

DisplayUpdate.prototype.signal = function(emitter, type) {
if (hasHandler(emitter, type))
this.events.push(arguments);
};
DisplayUpdate.prototype.finish = function() {
for (var i = 0; i < this.events.length; i++)
signal.apply(null, this.events[i]);
};

function maybeClipScrollbars(cm) {
var display = cm.display;
if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
Expand Down Expand Up @@ -761,9 +772,9 @@
updateScrollbars(cm, barMeasure);
}

signalLater(cm, "update", cm);
update.signal(cm, "update", cm);
if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
signalLater(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
}
}
Expand All @@ -777,6 +788,7 @@
updateSelection(cm);
setDocumentHeight(cm, barMeasure);
updateScrollbars(cm, barMeasure);
update.finish();
}
}

Expand Down Expand Up @@ -2237,6 +2249,8 @@
// Fire change events, and delayed event handlers
if (op.changeObjs)
signal(cm, "changes", cm, op.changeObjs);
if (op.update)
op.update.finish();
}

// Run the given function in an operation
Expand Down Expand Up @@ -2611,8 +2625,10 @@
}

function focusInput(cm) {
if (cm.options.readOnly != "nocursor" && (!mobile || activeElt() != cm.display.input))
cm.display.input.focus();
if (cm.options.readOnly != "nocursor" && (!mobile || activeElt() != cm.display.input)) {
try { cm.display.input.focus(); }
catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
}
}

function ensureFocus(cm) {
Expand Down Expand Up @@ -5121,7 +5137,7 @@
// FROMTEXTAREA

CodeMirror.fromTextArea = function(textarea, options) {
if (!options) options = {};
options = options ? copyObj(options) : {};
options.value = textarea.value;
if (!options.tabindex && textarea.tabindex)
options.tabindex = textarea.tabindex;
Expand Down Expand Up @@ -5152,23 +5168,26 @@
}
}

options.finishInit = function(cm) {
cm.save = save;
cm.getTextArea = function() { return textarea; };
cm.toTextArea = function() {
cm.toTextArea = isNaN; // Prevent this from being ran twice
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
if (textarea.form) {
off(textarea.form, "submit", save);
if (typeof textarea.form.submit == "function")
textarea.form.submit = realSubmit;
}
};
};

textarea.style.display = "none";
var cm = CodeMirror(function(node) {
textarea.parentNode.insertBefore(node, textarea.nextSibling);
}, options);
cm.save = save;
cm.getTextArea = function() { return textarea; };
cm.toTextArea = function() {
cm.toTextArea = isNaN; // Prevent this from being ran twice
save();
textarea.parentNode.removeChild(cm.getWrapperElement());
textarea.style.display = "";
if (textarea.form) {
off(textarea.form, "submit", save);
if (typeof textarea.form.submit == "function")
textarea.form.submit = realSubmit;
}
};
return cm;
};

Expand Down Expand Up @@ -6179,6 +6198,7 @@
function defaultSpecialCharPlaceholder(ch) {
var token = elt("span", "\u2022", "cm-invalidchar");
token.title = "\\u" + ch.charCodeAt(0).toString(16);
token.setAttribute("aria-label", token.title);
return token;
}

Expand Down Expand Up @@ -6212,6 +6232,7 @@
if (m[0] == "\t") {
var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
var txt = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
txt.setAttribute("role", "presentation");
builder.col += tabWidth;
} else {
var txt = builder.cm.options.specialCharPlaceholder(m[0]);
Expand Down Expand Up @@ -7576,12 +7597,14 @@
return removeChildren(parent).appendChild(e);
}

function contains(parent, child) {
var contains = CodeMirror.contains = function(parent, child) {
if (parent.contains)
return parent.contains(child);
while (child = child.parentNode)
while (child = child.parentNode) {
if (child.nodeType == 11) child = child.host;
if (child == parent) return true;
}
}
};

function activeElt() { return document.activeElement; }
// Older versions of IE throws unspecified error when touching
Expand Down Expand Up @@ -8039,7 +8062,7 @@

// THE END

CodeMirror.version = "4.12.0";
CodeMirror.version = "4.13.0";

return CodeMirror;
});
6 changes: 5 additions & 1 deletion mode/clike/clike.js
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
state.tokenize = null;
break;
}
escaped = stream.next() != "\\" && !escaped;
escaped = stream.next() == "\\" && !escaped;
}
return "string";
}
Expand Down Expand Up @@ -398,6 +398,10 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
if (!stream.match('""')) return false;
state.tokenize = tokenTripleString;
return state.tokenize(stream, state);
},
"'": function(stream) {
stream.eatWhile(/[\w\$_\xa1-\uffff]/);
return "atom";
}
}
});
Expand Down
159 changes: 104 additions & 55 deletions mode/css/css.js

Large diffs are not rendered by default.

62 changes: 61 additions & 1 deletion mode/css/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,70 @@
MT("parens",
"[qualifier .foo] {",
" [property background-image]: [variable fade]([atom #000], [number 20%]);",
" [property border-image]: [variable linear-gradient](",
" [property border-image]: [atom linear-gradient](",
" [atom to] [atom bottom],",
" [variable fade]([atom #000], [number 20%]) [number 0%],",
" [variable fade]([atom #000], [number 20%]) [number 100%]",
" );",
"}");

MT("css_variable",
":[variable-3 root] {",
" [variable-2 --main-color]: [atom #06c];",
"}",
"[tag h1][builtin #foo] {",
" [property color]: [atom var]([variable-2 --main-color]);",
"}");

MT("supports",
"[def @supports] ([keyword not] (([property text-align-last]: [atom justify]) [keyword or] ([meta -moz-][property text-align-last]: [atom justify])) {",
" [property text-align-last]: [atom justify];",
"}");

MT("document",
"[def @document] [tag url]([string http://blah]),",
" [tag url-prefix]([string https://]),",
" [tag domain]([string blah.com]),",
" [tag regexp]([string \".*blah.+\"]) {",
" [builtin #id] {",
" [property background-color]: [keyword white];",
" }",
" [tag foo] {",
" [property font-family]: [variable Verdana], [atom sans-serif];",
" }",
" }");

MT("document_url",
"[def @document] [tag url]([string http://blah]) { [qualifier .class] { } }");

MT("document_urlPrefix",
"[def @document] [tag url-prefix]([string https://]) { [builtin #id] { } }");

MT("document_domain",
"[def @document] [tag domain]([string blah.com]) { [tag foo] { } }");

MT("document_regexp",
"[def @document] [tag regexp]([string \".*blah.+\"]) { [builtin #id] { } }");

MT("counter-style",
"[def @counter-style] [variable binary] {",
" [property system]: [atom numeric];",
" [property symbols]: [number 0] [number 1];",
" [property suffix]: [string \".\"];",
" [property range]: [atom infinite];",
" [property speak-as]: [atom numeric];",
"}");

MT("counter-style-additive-symbols",
"[def @counter-style] [variable simple-roman] {",
" [property system]: [atom additive];",
" [property additive-symbols]: [number 10] [variable X], [number 5] [variable V], [number 1] [variable I];",
" [property range]: [number 1] [number 49];",
"}");

MT("counter-style-use",
"[tag ol][qualifier .roman] { [property list-style]: [variable simple-roman]; }");

MT("counter-style-symbols",
"[tag ol] { [property list-style]: [atom symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }");
})();
2 changes: 1 addition & 1 deletion mode/cypher/cypher.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
};
var indentUnit = config.indentUnit;
var curPunc;
var funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "right", "round", "rtrim", "shortestPath", "sign", "sin", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "trim", "type", "upper"]);
var funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "keys", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "right", "round", "rtrim", "shortestPath", "sign", "sin", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "trim", "type", "upper"]);
var preds = wordRegexp(["all", "and", "any", "has", "in", "none", "not", "or", "single", "xor"]);
var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "distinct", "drop", "else", "end", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with"]);
var operatorChars = /[*+\-<>=&|~%^]/;
Expand Down
180 changes: 180 additions & 0 deletions mode/forth/forth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Author: Aliaksei Chapyzhenka

(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";

function toWordList(words) {
var ret = [];
words.split(' ').forEach(function(e){
ret.push({name: e});
});
return ret;
}

var coreWordList = toWordList(
'INVERT AND OR XOR\
2* 2/ LSHIFT RSHIFT\
0= = 0< < > U< MIN MAX\
2DROP 2DUP 2OVER 2SWAP ?DUP DEPTH DROP DUP OVER ROT SWAP\
>R R> R@\
+ - 1+ 1- ABS NEGATE\
S>D * M* UM*\
FM/MOD SM/REM UM/MOD */ */MOD / /MOD MOD\
HERE , @ ! CELL+ CELLS C, C@ C! CHARS 2@ 2!\
ALIGN ALIGNED +! ALLOT\
CHAR [CHAR] [ ] BL\
FIND EXECUTE IMMEDIATE COUNT LITERAL STATE\
; DOES> >BODY\
EVALUATE\
SOURCE >IN\
<# # #S #> HOLD SIGN BASE >NUMBER HEX DECIMAL\
FILL MOVE\
. CR EMIT SPACE SPACES TYPE U. .R U.R\
ACCEPT\
TRUE FALSE\
<> U> 0<> 0>\
NIP TUCK ROLL PICK\
2>R 2R@ 2R>\
WITHIN UNUSED MARKER\
I J\
TO\
COMPILE, [COMPILE]\
SAVE-INPUT RESTORE-INPUT\
PAD ERASE\
2LITERAL DNEGATE\
D- D+ D0< D0= D2* D2/ D< D= DMAX DMIN D>S DABS\
M+ M*/ D. D.R 2ROT DU<\
CATCH THROW\
FREE RESIZE ALLOCATE\
CS-PICK CS-ROLL\
GET-CURRENT SET-CURRENT FORTH-WORDLIST GET-ORDER SET-ORDER\
PREVIOUS SEARCH-WORDLIST WORDLIST FIND ALSO ONLY FORTH DEFINITIONS ORDER\
-TRAILING /STRING SEARCH COMPARE CMOVE CMOVE> BLANK SLITERAL');

var immediateWordList = toWordList('IF ELSE THEN BEGIN WHILE REPEAT UNTIL RECURSE [IF] [ELSE] [THEN] ?DO DO LOOP +LOOP UNLOOP LEAVE EXIT AGAIN CASE OF ENDOF ENDCASE');

CodeMirror.defineMode('forth', function() {
function searchWordList (wordList, word) {
var i;
for (i = wordList.length - 1; i >= 0; i--) {
if (wordList[i].name === word.toUpperCase()) {
return wordList[i];
}
}
return undefined;
}
return {
startState: function() {
return {
state: '',
base: 10,
coreWordList: coreWordList,
immediateWordList: immediateWordList,
wordList: []
};
},
token: function (stream, stt) {
var mat;
if (stream.eatSpace()) {
return null;
}
if (stt.state === '') { // interpretation
if (stream.match(/^(\]|:NONAME)(\s|$)/i)) {
stt.state = ' compilation';
return 'builtin compilation';
}
mat = stream.match(/^(\:)\s+(\S+)(\s|$)+/);
if (mat) {
stt.wordList.push({name: mat[2].toUpperCase()});
stt.state = ' compilation';
return 'def' + stt.state;
}
mat = stream.match(/^(VARIABLE|2VARIABLE|CONSTANT|2CONSTANT|CREATE|POSTPONE|VALUE|WORD)\s+(\S+)(\s|$)+/i);
if (mat) {
stt.wordList.push({name: mat[2].toUpperCase()});
return 'def' + stt.state;
}
mat = stream.match(/^(\'|\[\'\])\s+(\S+)(\s|$)+/);
if (mat) {
return 'builtin' + stt.state;
}
} else { // compilation
// ; [
if (stream.match(/^(\;|\[)(\s)/)) {
stt.state = '';
stream.backUp(1);
return 'builtin compilation';
}
if (stream.match(/^(\;|\[)($)/)) {
stt.state = '';
return 'builtin compilation';
}
if (stream.match(/^(POSTPONE)\s+\S+(\s|$)+/)) {
return 'builtin';
}
}

// dynamic wordlist
mat = stream.match(/^(\S+)(\s+|$)/);
if (mat) {
if (searchWordList(stt.wordList, mat[1]) !== undefined) {
return 'variable' + stt.state;
}

// comments
if (mat[1] === '\\') {
stream.skipToEnd();
return 'comment' + stt.state;
}

// core words
if (searchWordList(stt.coreWordList, mat[1]) !== undefined) {
return 'builtin' + stt.state;
}
if (searchWordList(stt.immediateWordList, mat[1]) !== undefined) {
return 'keyword' + stt.state;
}

if (mat[1] === '(') {
stream.eatWhile(function (s) { return s !== ')'; });
stream.eat(')');
return 'comment' + stt.state;
}

// // strings
if (mat[1] === '.(') {
stream.eatWhile(function (s) { return s !== ')'; });
stream.eat(')');
return 'string' + stt.state;
}
if (mat[1] === 'S"' || mat[1] === '."' || mat[1] === 'C"') {
stream.eatWhile(function (s) { return s !== '"'; });
stream.eat('"');
return 'string' + stt.state;
}

// numbers
if (mat[1] - 0xfffffffff) {
return 'number' + stt.state;
}
// if (mat[1].match(/^[-+]?[0-9]+\.[0-9]*/)) {
// return 'number' + stt.state;
// }

return 'atom' + stt.state;
}
}
};
});
CodeMirror.defineMIME("text/x-forth", "forth");
});
75 changes: 75 additions & 0 deletions mode/forth/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!doctype html>

<title>CodeMirror: Forth mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">

<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel=stylesheet href="../../theme/colorforth.css">
<script src="../../lib/codemirror.js"></script>
<script src="forth.js"></script>
<style>
.CodeMirror {
font-family: 'Droid Sans Mono', monospace;
font-size: 14px;
}
</style>
<div id=nav>
<a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>

<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
<li><a href="https://github.com/codemirror/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">Forth</a>
</ul>
</div>

<article>

<h2>Forth mode</h2>

<form><textarea id="code" name="code">
\ Insertion sort

: cell- 1 cells - ;

: insert ( start end -- start )
dup @ >r ( r: v )
begin
2dup <
while
r@ over cell- @ <
while
cell-
dup @ over cell+ !
repeat then
r> swap ! ;

: sort ( array len -- )
1 ?do
dup i cells + insert
loop drop ;</textarea>
</form>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
lineWrapping: true,
indentUnit: 2,
tabSize: 2,
autofocus: true,
theme: "colorforth",
mode: "text/x-forth"
});
</script>

<p>Simple mode that handle Forth-Syntax (<a href="http://en.wikipedia.org/wiki/Forth_%28programming_language%29">Forth on WikiPedia</a>).</p>

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

</article>
1 change: 1 addition & 0 deletions mode/go/go.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ CodeMirror.defineMode("go", function(config) {
return state.context = new Context(state.indented, col, type, null, state.context);
}
function popContext(state) {
if (!state.context.prev) return;
var t = state.context.type;
if (t == ")" || t == "]" || t == "}")
state.indented = state.context.indented;
Expand Down
2 changes: 1 addition & 1 deletion mode/idl/idl.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@

// Handle non-detected items
stream.next();
return 'error';
return null;
};

CodeMirror.defineMode('idl', function() {
Expand Down
2 changes: 2 additions & 0 deletions mode/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ <h2>Language modes</h2>
<li><a href="ecl/index.html">ECL</a></li>
<li><a href="eiffel/index.html">Eiffel</a></li>
<li><a href="erlang/index.html">Erlang</a></li>
<li><a href="forth/index.html">Forth</a></li>
<li><a href="fortran/index.html">Fortran</a></li>
<li><a href="mllike/index.html">F#</a></li>
<li><a href="gas/index.html">Gas</a> (AT&amp;T-style assembly)</li>
Expand Down Expand Up @@ -108,6 +109,7 @@ <h2>Language modes</h2>
<li><a href="smartymixed/index.html">Smarty/HTML mixed</a></li>
<li><a href="solr/index.html">Solr</a></li>
<li><a href="soy/index.html">Soy</a></li>
<li><a href="stylus/index.html">Stylus</a></li>
<li><a href="sql/index.html">SQL</a> (several dialects)</li>
<li><a href="sparql/index.html">SPARQL</a></li>
<li><a href="stex/index.html">sTeX, LaTeX</a></li>
Expand Down
2 changes: 1 addition & 1 deletion mode/javascript/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
} else if (state.lastType == "operator" || state.lastType == "keyword c" ||
state.lastType == "sof" || /^[\[{}\(,;:]$/.test(state.lastType)) {
readRegexp(stream);
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
return ret("regexp", "string-2");
} else {
stream.eatWhile(isOperatorChar);
Expand Down
1 change: 1 addition & 0 deletions mode/meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
{name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
{name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
{name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
{name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
{name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
{name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
{name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
Expand Down
2 changes: 1 addition & 1 deletion mode/sql/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {

indent: function(state, textAfter) {
var cx = state.context;
if (!cx) return 0;
if (!cx) return CodeMirror.Pass;
var closing = textAfter.charAt(0) == cx.type;
if (cx.align) return cx.col + (closing ? 0 : 1);
else return cx.indent + (closing ? 0 : config.indentUnit);
Expand Down
104 changes: 104 additions & 0 deletions mode/stylus/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<!doctype html>

<title>CodeMirror: Stylus mode</title>
<meta charset="utf-8"/>
<link rel=stylesheet href="../../doc/docs.css">
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel="stylesheet" href="../../addon/hint/show-hint.css">
<script src="../../lib/codemirror.js"></script>
<script src="stylus.js"></script>
<script src="../../addon/hint/show-hint.js"></script>
<script src="../../addon/hint/css-hint.js"></script>
<style>.CodeMirror {background: #f8f8f8;} form{margin-bottom: .7em;}</style>
<div id=nav>
<a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>

<ul>
<li><a href="../../index.html">Home</a>
<li><a href="../../doc/manual.html">Manual</a>
<li><a href="https://github.com/codemirror/codemirror">Code</a>
</ul>
<ul>
<li><a href="../index.html">Language modes</a>
<li><a class=active href="#">Stylus</a>
</ul>
</div>

<article>
<h2>Stylus mode</h2>
<form><textarea id="code" name="code">
/* Stylus mode */
#id
.class
article
font-family Arial, sans-serif

#id,
.class,
article {
font-family: Arial, sans-serif;
}

// Variables
font-size-base = 16px
line-height-base = 1.5
font-family-base = "Helvetica Neue", Helvetica, Arial, sans-serif
text-color = lighten(#000, 20%)

body
font font-size-base/line-height-base font-family-base
color text-color

body {
font: 400 16px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #333;
}

// Variables
link-color = darken(#428bca, 6.5%)
link-hover-color = darken(link-color, 15%)
link-decoration = none
link-hover-decoration = false

// Mixin
tab-focus()
outline thin dotted
outline 5px auto -webkit-focus-ring-color
outline-offset -2px

a
color link-color
if link-decoration
text-decoration link-decoration
&:hover
&:focus
color link-hover-color
if link-hover-decoration
text-decoration link-hover-decoration
&:focus
tab-focus()

a {
color: #3782c4;
text-decoration: none;
}
a:hover,
a:focus {
color: #2f6ea7;
}
a:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
</textarea>
</form>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
extraKeys: {"Ctrl-Space": "autocomplete"},
});
</script>

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

</article>
444 changes: 444 additions & 0 deletions mode/stylus/stylus.js

Large diffs are not rendered by default.

211 changes: 192 additions & 19 deletions mode/verilog/verilog.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
dontAlignCalls = parserConfig.dontAlignCalls,
noIndentKeywords = parserConfig.noIndentKeywords || [],
multiLineStrings = parserConfig.multiLineStrings;
multiLineStrings = parserConfig.multiLineStrings,
hooks = parserConfig.hooks || {};

function words(str) {
var obj = {}, words = str.split(" ");
Expand Down Expand Up @@ -107,7 +108,11 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
var statementKeywords = words("always always_comb always_ff always_latch assert assign assume else export for foreach forever if import initial repeat while");

function tokenBase(stream, state) {
var ch = stream.peek();
var ch = stream.peek(), style;
if (hooks[ch] && (style = hooks[ch](stream, state)) != false) return style;
if (hooks.tokenBase && (style = hooks.tokenBase(stream, state)) != false)
return style;

if (/[,;:\.]/.test(ch)) {
curPunc = stream.next();
return null;
Expand Down Expand Up @@ -280,12 +285,14 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
electricInput: buildElectricInputRegEx(),

startState: function(basecolumn) {
return {
var state = {
tokenize: null,
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
indented: 0,
startOfLine: true
};
if (hooks.startState) hooks.startState(state);
return state;
},

token: function(stream, state) {
Expand All @@ -295,6 +302,7 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
state.indented = stream.indentation();
state.startOfLine = true;
}
if (hooks.token) hooks.token(stream, state);
if (stream.eatSpace()) return null;
curPunc = null;
curKeyword = null;
Expand All @@ -304,17 +312,19 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {

if (curPunc == ctx.type) {
popContext(state);
}
else if ((curPunc == ";" && ctx.type == "statement") ||
} else if ((curPunc == ";" && ctx.type == "statement") ||
(ctx.type && isClosing(curKeyword, ctx.type))) {
ctx = popContext(state);
while (ctx && ctx.type == "statement") ctx = popContext(state);
}
else if (curPunc == "{") { pushContext(state, stream.column(), "}"); }
else if (curPunc == "[") { pushContext(state, stream.column(), "]"); }
else if (curPunc == "(") { pushContext(state, stream.column(), ")"); }
else if (ctx && ctx.type == "endcase" && curPunc == ":") { pushContext(state, stream.column(), "statement"); }
else if (curPunc == "newstatement") {
} else if (curPunc == "{") {
pushContext(state, stream.column(), "}");
} else if (curPunc == "[") {
pushContext(state, stream.column(), "]");
} else if (curPunc == "(") {
pushContext(state, stream.column(), ")");
} else if (ctx && ctx.type == "endcase" && curPunc == ":") {
pushContext(state, stream.column(), "statement");
} else if (curPunc == "newstatement") {
pushContext(state, stream.column(), "statement");
} else if (curPunc == "newblock") {
if (curKeyword == "function" && ctx && (ctx.type == "statement" || ctx.type == "endgroup")) {
Expand All @@ -335,13 +345,16 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {

indent: function(state, textAfter) {
if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
if (hooks.indent) {
var fromHook = hooks.indent(state);
if (fromHook >= 0) return fromHook;
}
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
var closing = false;
var possibleClosing = textAfter.match(closingBracketOrWord);
if (possibleClosing) {
if (possibleClosing)
closing = isClosing(possibleClosing[0], ctx.type);
}
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
else if (closingBracket.test(ctx.type) && ctx.align && !dontAlignCalls) return ctx.column + (closing ? 0 : 1);
else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
Expand All @@ -354,11 +367,171 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
};
});

CodeMirror.defineMIME("text/x-verilog", {
name: "verilog"
});
CodeMirror.defineMIME("text/x-systemverilog", {
name: "systemverilog"
});
CodeMirror.defineMIME("text/x-verilog", {
name: "verilog"
});

CodeMirror.defineMIME("text/x-systemverilog", {
name: "verilog"
});

// SVXVerilog mode

var svxchScopePrefixes = {
">": "property", "->": "property", "-": "hr", "|": "link", "?$": "qualifier", "?*": "qualifier",
"@-": "variable-3", "@": "variable-3", "?": "qualifier"
};

function svxGenIndent(stream, state) {
var svxindentUnit = 2;
var rtnIndent = -1, indentUnitRq = 0, curIndent = stream.indentation();
switch (state.svxCurCtlFlowChar) {
case "\\":
curIndent = 0;
break;
case "|":
if (state.svxPrevPrevCtlFlowChar == "@") {
indentUnitRq = -2; //-2 new pipe rq after cur pipe
break;
}
if (svxchScopePrefixes[state.svxPrevCtlFlowChar])
indentUnitRq = 1; // +1 new scope
break;
case "M": // m4
if (state.svxPrevPrevCtlFlowChar == "@") {
indentUnitRq = -2; //-2 new inst rq after pipe
break;
}
if (svxchScopePrefixes[state.svxPrevCtlFlowChar])
indentUnitRq = 1; // +1 new scope
break;
case "@":
if (state.svxPrevCtlFlowChar == "S")
indentUnitRq = -1; // new pipe stage after stmts
if (state.svxPrevCtlFlowChar == "|")
indentUnitRq = 1; // 1st pipe stage
break;
case "S":
if (state.svxPrevCtlFlowChar == "@")
indentUnitRq = 1; // flow in pipe stage
if (svxchScopePrefixes[state.svxPrevCtlFlowChar])
indentUnitRq = 1; // +1 new scope
break;
}
var statementIndentUnit = svxindentUnit;
rtnIndent = curIndent + (indentUnitRq*statementIndentUnit);
return rtnIndent >= 0 ? rtnIndent : curIndent;
}

CodeMirror.defineMIME("text/x-svx", {
name: "verilog",
hooks: {
"\\": function(stream, state) {
var vxIndent = 0, style = false;
var curPunc = stream.string;
if ((stream.sol()) && (/\\SV/.test(stream.string))) {
curPunc = (/\\SVX_version/.test(stream.string))
? "\\SVX_version" : stream.string;
stream.skipToEnd();
if (curPunc == "\\SV" && state.vxCodeActive) {state.vxCodeActive = false;};
if ((/\\SVX/.test(curPunc) && !state.vxCodeActive)
|| (curPunc=="\\SVX_version" && state.vxCodeActive)) {state.vxCodeActive = true;};
style = "keyword";
state.svxCurCtlFlowChar = state.svxPrevPrevCtlFlowChar
= state.svxPrevCtlFlowChar = "";
if (state.vxCodeActive == true) {
state.svxCurCtlFlowChar = "\\";
vxIndent = svxGenIndent(stream, state);
}
state.vxIndentRq = vxIndent;
}
return style;
},
tokenBase: function(stream, state) {
var vxIndent = 0, style = false;
var svxisOperatorChar = /[\[\]=:]/;
var svxkpScopePrefixs = {
"**":"variable-2", "*":"variable-2", "$$":"variable", "$":"variable",
"^^":"attribute", "^":"attribute"};
var ch = stream.peek();
var vxCurCtlFlowCharValueAtStart = state.svxCurCtlFlowChar;
if (state.vxCodeActive == true) {
if (/[\[\]{}\(\);\:]/.test(ch)) {
// bypass nesting and 1 char punc
style = "meta";
stream.next();
} else if (ch == "/") {
stream.next();
if (stream.eat("/")) {
stream.skipToEnd();
style = "comment";
state.svxCurCtlFlowChar = "S";
} else {
stream.backUp(1);
}
} else if (ch == "@") {
// pipeline stage
style = svxchScopePrefixes[ch];
state.svxCurCtlFlowChar = "@";
stream.next();
stream.eatWhile(/[\w\$_]/);
} else if (stream.match(/\b[mM]4+/, true)) { // match: function(pattern, consume, caseInsensitive)
// m4 pre proc
stream.skipTo("(");
style = "def";
state.svxCurCtlFlowChar = "M";
} else if (ch == "!" && stream.sol()) {
// v stmt in svx region
// state.svxCurCtlFlowChar = "S";
style = "comment";
stream.next();
} else if (svxisOperatorChar.test(ch)) {
// operators
stream.eatWhile(svxisOperatorChar);
style = "operator";
} else if (ch == "#") {
// phy hier
state.svxCurCtlFlowChar = (state.svxCurCtlFlowChar == "")
? ch : state.svxCurCtlFlowChar;
stream.next();
stream.eatWhile(/[+-]\d/);
style = "tag";
} else if (svxkpScopePrefixs.propertyIsEnumerable(ch)) {
// special SVX operators
style = svxkpScopePrefixs[ch];
state.svxCurCtlFlowChar = state.svxCurCtlFlowChar == "" ? "S" : state.svxCurCtlFlowChar; // stmt
stream.next();
stream.match(/[a-zA-Z_0-9]+/);
} else if (style = svxchScopePrefixes[ch] || false) {
// special SVX operators
state.svxCurCtlFlowChar = state.svxCurCtlFlowChar == "" ? ch : state.svxCurCtlFlowChar;
stream.next();
stream.match(/[a-zA-Z_0-9]+/);
}
if (state.svxCurCtlFlowChar != vxCurCtlFlowCharValueAtStart) { // flow change
vxIndent = svxGenIndent(stream, state);
state.vxIndentRq = vxIndent;
}
}
return style;
},
token: function(stream, state) {
if (state.vxCodeActive == true && stream.sol() && state.svxCurCtlFlowChar != "") {
state.svxPrevPrevCtlFlowChar = state.svxPrevCtlFlowChar;
state.svxPrevCtlFlowChar = state.svxCurCtlFlowChar;
state.svxCurCtlFlowChar = "";
}
},
indent: function(state) {
return (state.vxCodeActive == true) ? state.vxIndentRq : -1;
},
startState: function(state) {
state.svxCurCtlFlowChar = "";
state.svxPrevCtlFlowChar = "";
state.svxPrevPrevCtlFlowChar = "";
state.vxCodeActive = true;
state.vxIndentRq = 0;
}
}
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codemirror",
"version":"4.12.0",
"version":"4.13.0",
"main": "lib/codemirror.js",
"description": "In-browser code editing made bearable",
"licenses": [{"type": "MIT",
Expand Down
52 changes: 28 additions & 24 deletions test/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!doctype html>

<title>CodeMirror: Test Suite</title>
<meta charset="utf-8"/>
<title>CodeMirror: Test Suite</title>
<link rel=stylesheet href="../doc/docs.css">

<link rel="stylesheet" href="../lib/codemirror.css">
Expand All @@ -13,14 +13,29 @@
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/dialog/dialog.js"></script>
<script src="../addon/edit/matchbrackets.js"></script>
<script src="../addon/hint/sql-hint.js"></script>
<script src="../addon/comment/comment.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/css/css.js"></script>
<script src="../mode/clike/clike.js"></script>
<!-- clike must be after css or vim and sublime tests will fail -->
<script src="../mode/gfm/gfm.js"></script>
<script src="../mode/haml/haml.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/php/php.js"></script>
<script src="../mode/ruby/ruby.js"></script>
<script src="../mode/shell/shell.js"></script>
<script src="../mode/slim/slim.js"></script>
<script src="../mode/sql/sql.js"></script>
<script src="../mode/stex/stex.js"></script>
<script src="../mode/textile/textile.js"></script>
<script src="../mode/verilog/verilog.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../keymap/vim.js"></script>
<script src="../mode/xquery/xquery.js"></script>
<script src="../keymap/emacs.js"></script>
<script src="../keymap/sublime.js"></script>
<script src="../keymap/vim.js"></script>

<style type="text/css">
.ok {color: #090;}
Expand Down Expand Up @@ -80,39 +95,28 @@ <h2>Test Suite</h2>
<script src="comment_test.js"></script>
<script src="search_test.js"></script>
<script src="mode_test.js"></script>
<script src="../mode/javascript/test.js"></script>
<script src="../mode/php/test.js"></script>
<script src="../mode/css/css.js"></script>

<script src="../mode/css/test.js"></script>
<script src="../mode/css/scss_test.js"></script>
<script src="../mode/css/less_test.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/xml/test.js"></script>
<script src="../mode/htmlmixed/htmlmixed.js"></script>
<script src="../mode/ruby/ruby.js"></script>
<script src="../mode/ruby/test.js"></script>
<script src="../mode/haml/haml.js"></script>
<script src="../mode/gfm/test.js"></script>
<script src="../mode/haml/test.js"></script>
<script src="../mode/markdown/markdown.js"></script>
<script src="../mode/javascript/test.js"></script>
<script src="../mode/markdown/test.js"></script>
<script src="../mode/slim/slim.js"></script>
<script src="../mode/slim/test.js"></script>
<script src="../mode/gfm/gfm.js"></script>
<script src="../mode/gfm/test.js"></script>
<script src="../mode/shell/shell.js"></script>
<script src="../mode/php/test.js"></script>
<script src="../mode/ruby/test.js"></script>
<script src="../mode/shell/test.js"></script>
<script src="../mode/stex/stex.js"></script>
<script src="../mode/slim/test.js"></script>
<script src="../mode/stex/test.js"></script>
<script src="../mode/textile/textile.js"></script>
<script src="../mode/textile/test.js"></script>
<script src="../mode/verilog/verilog.js"></script>
<script src="../mode/verilog/test.js"></script>
<script src="../mode/xquery/xquery.js"></script>
<script src="../mode/xml/test.js"></script>
<script src="../mode/xquery/test.js"></script>
<script src="../addon/mode/multiplex_test.js"></script>
<script src="vim_test.js"></script>
<script src="emacs_test.js"></script>
<script src="sql-hint-test.js"></script>
<script src="sublime_test.js"></script>
<script src="vim_test.js"></script>
<script>
window.onload = runHarness;
CodeMirror.on(window, 'hashchange', runHarness);
Expand Down Expand Up @@ -149,7 +153,7 @@ <h2>Test Suite</h2>
if (s === "verbose")
verbose = true;
else
filters.push(parseTestFilter(decodeURIComponent(s)));;
filters.push(parseTestFilter(decodeURIComponent(s)));
}
}
quit = false;
Expand Down
170 changes: 170 additions & 0 deletions test/sql-hint-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

(function() {
var Pos = CodeMirror.Pos;

var simpleTables = {
"users": ["name", "score", "birthDate"],
"xcountries": ["name", "population", "size"]
};

var schemaTables = {
"schema.users": ["name", "score", "birthDate"],
"schema.countries": ["name", "population", "size"]
};

var displayTextTables = [{
text: "mytable",
displayText: "mytable | The main table",
columns: [{text: "id", displayText: "id | Unique ID"},
{text: "name", displayText: "name | The name"}]
}];

namespace = "sql-hint_";

function test(name, spec) {
testCM(name, function(cm) {
cm.setValue(spec.value);
cm.setCursor(spec.cursor);
var completion = CodeMirror.hint.sql(cm, {tables: spec.tables});
if (!deepCompare(completion.list, spec.list))
throw new Failure("Wrong completion results " + JSON.stringify(completion.list) + " vs " + JSON.stringify(spec.list));
eqPos(completion.from, spec.from);
eqPos(completion.to, spec.to);
}, {
value: spec.value,
mode: "text/x-mysql"
});
}

test("keywords", {
value: "SEL",
cursor: Pos(0, 3),
list: ["SELECT"],
from: Pos(0, 0),
to: Pos(0, 3)
});

test("from", {
value: "SELECT * fr",
cursor: Pos(0, 11),
list: ["FROM"],
from: Pos(0, 9),
to: Pos(0, 11)
});

test("table", {
value: "SELECT xc",
cursor: Pos(0, 9),
tables: simpleTables,
list: ["xcountries"],
from: Pos(0, 7),
to: Pos(0, 9)
});

test("columns", {
value: "SELECT users.",
cursor: Pos(0, 13),
tables: simpleTables,
list: ["users.name", "users.score", "users.birthDate"],
from: Pos(0, 7),
to: Pos(0, 13)
});

test("singlecolumn", {
value: "SELECT users.na",
cursor: Pos(0, 15),
tables: simpleTables,
list: ["users.name"],
from: Pos(0, 7),
to: Pos(0, 15)
});

test("quoted", {
value: "SELECT `users`.`na",
cursor: Pos(0, 18),
tables: simpleTables,
list: ["`users`.`name`"],
from: Pos(0, 7),
to: Pos(0, 18)
});

test("quotedcolumn", {
value: "SELECT users.`na",
cursor: Pos(0, 16),
tables: simpleTables,
list: ["`users`.`name`"],
from: Pos(0, 7),
to: Pos(0, 16)
});

test("schema", {
value: "SELECT schem",
cursor: Pos(0, 12),
tables: schemaTables,
list: ["schema.users", "schema.countries",
"SCHEMA", "SCHEMA_NAME", "SCHEMAS"],
from: Pos(0, 7),
to: Pos(0, 12)
});

test("schemaquoted", {
value: "SELECT `sch",
cursor: Pos(0, 11),
tables: schemaTables,
list: ["`schema`.`users`", "`schema`.`countries`"],
from: Pos(0, 7),
to: Pos(0, 11)
});

test("schemacolumn", {
value: "SELECT schema.users.",
cursor: Pos(0, 20),
tables: schemaTables,
list: ["schema.users.name",
"schema.users.score",
"schema.users.birthDate"],
from: Pos(0, 7),
to: Pos(0, 20)
});

test("schemacolumnquoted", {
value: "SELECT `schema`.`users`.",
cursor: Pos(0, 24),
tables: schemaTables,
list: ["`schema`.`users`.`name`",
"`schema`.`users`.`score`",
"`schema`.`users`.`birthDate`"],
from: Pos(0, 7),
to: Pos(0, 24)
});

test("displayText_table", {
value: "SELECT myt",
cursor: Pos(0, 10),
tables: displayTextTables,
list: displayTextTables,
from: Pos(0, 7),
to: Pos(0, 10)
});

test("displayText_column", {
value: "SELECT mytable.",
cursor: Pos(0, 15),
tables: displayTextTables,
list: [{text: "mytable.id", displayText: "id | Unique ID"},
{text: "mytable.name", displayText: "name | The name"}],
from: Pos(0, 7),
to: Pos(0, 15)
});

function deepCompare(a, b) {
if (!a || typeof a != "object")
return a === b;
if (!b || typeof b != "object")
return false;
for (var prop in a) if (!deepCompare(a[prop], b[prop])) return false;
return true;
}
})();
17 changes: 11 additions & 6 deletions test/vim_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ testVim('dl_eol', function(cm, vim, helpers) {
var register = helpers.getRegisterController().getRegister();
eq(' ', register.toString());
is(!register.linewise);
helpers.assertCursorAt(0, 6);
helpers.assertCursorAt(0, 5);
}, { value: ' word1 ' });
testVim('dl_repeat', function(cm, vim, helpers) {
var curStart = makeCursor(0, 0);
Expand Down Expand Up @@ -688,7 +688,7 @@ testVim('dw_only_word', function(cm, vim, helpers) {
var register = helpers.getRegisterController().getRegister();
eq('word1 ', register.toString());
is(!register.linewise);
helpers.assertCursorAt(0, 1);
helpers.assertCursorAt(0, 0);
}, { value: ' word1 ' });
testVim('dw_eol', function(cm, vim, helpers) {
// Assert that dw does not delete the newline if last word to delete is at end
Expand All @@ -699,7 +699,7 @@ testVim('dw_eol', function(cm, vim, helpers) {
var register = helpers.getRegisterController().getRegister();
eq('word1', register.toString());
is(!register.linewise);
helpers.assertCursorAt(0, 1);
helpers.assertCursorAt(0, 0);
}, { value: ' word1\nword2' });
testVim('dw_eol_with_multiple_newlines', function(cm, vim, helpers) {
// Assert that dw does not delete the newline if last word to delete is at end
Expand All @@ -710,7 +710,7 @@ testVim('dw_eol_with_multiple_newlines', function(cm, vim, helpers) {
var register = helpers.getRegisterController().getRegister();
eq('word1', register.toString());
is(!register.linewise);
helpers.assertCursorAt(0, 1);
helpers.assertCursorAt(0, 0);
}, { value: ' word1\n\nword2' });
testVim('dw_empty_line_followed_by_whitespace', function(cm, vim, helpers) {
cm.setCursor(0, 0);
Expand Down Expand Up @@ -756,7 +756,7 @@ testVim('dw_repeat', function(cm, vim, helpers) {
var register = helpers.getRegisterController().getRegister();
eq('word1\nword2', register.toString());
is(!register.linewise);
helpers.assertCursorAt(0, 1);
helpers.assertCursorAt(0, 0);
}, { value: ' word1\nword2' });
testVim('de_word_start_and_empty_lines', function(cm, vim, helpers) {
cm.setCursor(0, 0);
Expand Down Expand Up @@ -1283,7 +1283,7 @@ testVim('D', function(cm, vim, helpers) {
var register = helpers.getRegisterController().getRegister();
eq('rd1', register.toString());
is(!register.linewise);
helpers.assertCursorAt(0, 3);
helpers.assertCursorAt(0, 2);
}, { value: ' word1\nword2\n word3' });
testVim('C', function(cm, vim, helpers) {
var curStart = makeCursor(0, 3);
Expand Down Expand Up @@ -1964,6 +1964,11 @@ testVim('visual_join', function(cm, vim, helpers) {
eq(' 1 2 3\n 4\n 5', cm.getValue());
is(!vim.visualMode);
}, { value: ' 1\n 2\n 3\n 4\n 5' });
testVim('visual_join_2', function(cm, vim, helpers) {
helpers.doKeys('G', 'V', 'g', 'g', 'J');
eq('1 2 3 4 5 6 ', cm.getValue());
is(!vim.visualMode);
}, { value: '1\n2\n3\n4\n5\n6\n'});
testVim('visual_blank', function(cm, vim, helpers) {
helpers.doKeys('v', 'k');
eq(vim.visualMode, true);
Expand Down
33 changes: 33 additions & 0 deletions theme/colorforth.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.cm-s-colorforth.CodeMirror { background: #000000; color: #f8f8f8; }
.cm-s-colorforth .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; }
.cm-s-colorforth .CodeMirror-guttermarker { color: #FFBD40; }
.cm-s-colorforth .CodeMirror-guttermarker-subtle { color: #78846f; }
.cm-s-colorforth .CodeMirror-linenumber { color: #bababa; }
.cm-s-colorforth .CodeMirror-cursor { border-left: 1px solid white !important; }

.cm-s-colorforth span.cm-comment { color: #ededed; }
.cm-s-colorforth span.cm-def { color: #ff1c1c; font-weight:bold; }
.cm-s-colorforth span.cm-keyword { color: #ffd900; }
.cm-s-colorforth span.cm-builtin { color: #00d95a; }
.cm-s-colorforth span.cm-variable { color: #73ff00; }
.cm-s-colorforth span.cm-string { color: #007bff; }
.cm-s-colorforth span.cm-number { color: #00c4ff; }
.cm-s-colorforth span.cm-atom { color: #606060; }

.cm-s-colorforth span.cm-variable-2 { color: #EEE; }
.cm-s-colorforth span.cm-variable-3 { color: #DDD; }
.cm-s-colorforth span.cm-property {}
.cm-s-colorforth span.cm-operator {}

.cm-s-colorforth span.cm-meta { color: yellow; }
.cm-s-colorforth span.cm-qualifier { color: #FFF700; }
.cm-s-colorforth span.cm-bracket { color: #cc7; }
.cm-s-colorforth span.cm-tag { color: #FFBD40; }
.cm-s-colorforth span.cm-attribute { color: #FFF700; }
.cm-s-colorforth span.cm-error { color: #f00; }

.cm-s-colorforth .CodeMirror-selected { background: #333d53 !important; }

.cm-s-colorforth span.cm-compilation { background: rgba(255, 255, 255, 0.12); }

.cm-s-colorforth .CodeMirror-activeline-background {background: #253540 !important;}