174 changes: 135 additions & 39 deletions addon/fold/xml-fold.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,160 @@
CodeMirror.tagRangeFinder = (function() {
(function() {
"use strict";

var Pos = CodeMirror.Pos;

var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");

return function(cm, start) {
var line = start.line, ch = start.ch, lineText = cm.getLine(line);
function Iter(cm, line, ch) {
this.line = line; this.ch = ch;
this.cm = cm; this.text = cm.getLine(line);
}

function nextLine() {
if (line >= cm.lastLine()) return;
ch = 0;
lineText = cm.getLine(++line);
return true;
}
function toTagEnd() {
for (;;) {
var gt = lineText.indexOf(">", ch);
if (gt == -1) { if (nextLine()) continue; else return; }
var lastSlash = lineText.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && /^\s*$/.test(lineText.slice(lastSlash + 1, gt));
ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
function tagAt(iter, ch) {
var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
return type && /\btag\b/.test(type);
}

function nextLine(iter) {
if (iter.line >= iter.cm.lastLine()) return;
iter.ch = 0;
iter.text = iter.cm.getLine(++iter.line);
return true;
}
function prevLine(iter) {
if (iter.line <= iter.cm.firstLine()) return;
iter.text = iter.cm.getLine(--iter.line);
iter.ch = iter.text.length;
return true;
}

function toTagEnd(iter) {
for (;;) {
var gt = iter.text.indexOf(">", iter.ch);
if (gt == -1) { if (nextLine(iter)) continue; else return; }
if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
var lastSlash = iter.text.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
iter.ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
function toNextTag() {
for (;;) {
xmlTagStart.lastIndex = ch;
var found = xmlTagStart.exec(lineText);
if (!found) { if (nextLine()) continue; else return; }
ch = found.index + found[0].length;
return found;
}
}
function toTagStart(iter) {
for (;;) {
var lt = iter.text.lastIndexOf("<", iter.ch - 1);
if (lt == -1) { if (prevLine(iter)) continue; else return; }
if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
xmlTagStart.lastIndex = lt;
iter.ch = lt;
var match = xmlTagStart.exec(iter.text);
if (match && match.index == lt) return match;
}
}

var stack = [], startCh;
function toNextTag(iter) {
for (;;) {
var openTag = toNextTag(), end;
if (!openTag || line != start.line || !(end = toTagEnd())) return;
if (!openTag[1] && end != "selfClose") {
stack.push(openTag[2]);
startCh = ch;
break;
}
xmlTagStart.lastIndex = iter.ch;
var found = xmlTagStart.exec(iter.text);
if (!found) { if (nextLine(iter)) continue; else return; }
if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
iter.ch = found.index + found[0].length;
return found;
}
}
function toPrevTag(iter) {
for (;;) {
var gt = iter.text.lastIndexOf(">", iter.ch - 1);
if (gt == -1) { if (prevLine(iter)) continue; else return; }
if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
var lastSlash = iter.text.lastIndexOf("/", gt);
var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
iter.ch = gt + 1;
return selfClose ? "selfClose" : "regular";
}
}

function findMatchingClose(iter, tag) {
var stack = [];
for (;;) {
var next = toNextTag(), end, tagLine = line, tagCh = ch - (next ? next[0].length : 0);
if (!next || !(end = toTagEnd())) return;
var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
if (!next || !(end = toTagEnd(iter))) return;
if (end == "selfClose") continue;
if (next[1]) { // closing tag
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
stack.length = i;
break;
}
if (!stack.length) return {
from: CodeMirror.Pos(start.line, startCh),
to: CodeMirror.Pos(tagLine, tagCh)
if (i < 0 && (!tag || tag == next[2])) return {
tag: next[2],
from: Pos(startLine, startCh),
to: Pos(iter.line, iter.ch)
};
} else { // opening tag
stack.push(next[2]);
}
}
}
function findMatchingOpen(iter, tag) {
var stack = [];
for (;;) {
var prev = toPrevTag(iter);
if (!prev) return;
if (prev == "selfClose") { toTagStart(iter); continue; }
var endLine = iter.line, endCh = iter.ch;
var start = toTagStart(iter);
if (!start) return;
if (start[1]) { // closing tag
stack.push(start[2]);
} else { // opening tag
for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
stack.length = i;
break;
}
if (i < 0 && (!tag || tag == start[2])) return {
tag: start[2],
from: Pos(iter.line, iter.ch),
to: Pos(endLine, endCh)
};
}
}
}

CodeMirror.tagRangeFinder = function(cm, start) {
var iter = new Iter(cm, start.line, 0);
for (;;) {
var openTag = toNextTag(iter), end;
if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return;
if (!openTag[1] && end != "selfClose") {
var start = Pos(iter.line, iter.ch);
var close = findMatchingClose(iter, openTag[2]);
return close && {from: start, to: close.from};
}
}
};

CodeMirror.findMatchingTag = function(cm, pos) {
var iter = new Iter(cm, pos.line, pos.ch);
var end = toTagEnd(iter), start = toTagStart(iter);
if (!end || end == "selfClose" || !start) return;

if (start[1]) { // closing tag
return findMatchingOpen(iter, start[2]);
} else { // opening tag
toTagEnd(iter);
return findMatchingClose(iter, start[2]);
}
};

CodeMirror.findEnclosingTag = function(cm, pos) {
var iter = new Iter(cm, pos.line, pos.ch);
for (;;) {
var open = findMatchingOpen(iter);
if (!open) break;
var forward = new Iter(cm, pos.line, pos.ch);
var close = findMatchingClose(forward, open.tag);
if (close) return {open: open, close: close};
}
};
})();
895 changes: 324 additions & 571 deletions addon/hint/html-hint.js

Large diffs are not rendered by default.

308 changes: 199 additions & 109 deletions addon/hint/show-hint.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,168 @@
CodeMirror.showHint = function(cm, getHints, options) {
if (!options) options = {};
var startCh = cm.getCursor().ch, continued = false;
var closeOn = options.closeCharacters || /[\s()\[\]{};:]/;
(function() {
"use strict";

function startHinting() {
CodeMirror.showHint = function(cm, getHints, options) {
// We want a single cursor position.
if (cm.somethingSelected()) return;

if (options.async)
getHints(cm, showHints, options);
if (cm.state.completionActive) cm.state.completionActive.close();

var completion = cm.state.completionActive = new Completion(cm, getHints, options || {});
CodeMirror.signal(cm, "startCompletion", cm);
if (completion.options.async)
getHints(cm, function(hints) { completion.showHints(hints); }, completion.options);
else
return showHints(getHints(cm, options));
return completion.showHints(getHints(cm, completion.options));
};

function Completion(cm, getHints, options) {
this.cm = cm;
this.getHints = getHints;
this.options = options;
this.widget = this.onClose = null;
}

Completion.prototype = {
close: function() {
if (!this.active()) return;

if (this.widget) this.widget.close();
if (this.onClose) this.onClose();
this.cm.state.completionActive = null;
CodeMirror.signal(this.cm, "endCompletion", this.cm);
},

active: function() {
return this.cm.state.completionActive == this;
},

pick: function(data, i) {
var completion = data.list[i];
if (completion.hint) completion.hint(this.cm, data, completion);
else this.cm.replaceRange(getText(completion), data.from, data.to);
this.close();
},

showHints: function(data) {
if (!data || !data.list.length || !this.active()) return this.close();

if (this.options.completeSingle != false && data.list.length == 1)
this.pick(data, 0);
else
this.showWidget(data);
},

showWidget: function(data) {
this.widget = new Widget(this, data);
CodeMirror.signal(data, "shown");

var debounce = null, completion = this, finished;
var closeOn = this.options.closeCharacters || /[\s()\[\]{};:>,]/;
var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;

function done() {
if (finished) return;
finished = true;
completion.close();
completion.cm.off("cursorActivity", activity);
CodeMirror.signal(data, "close");
}
function isDone() {
if (finished) return true;
if (!completion.widget) { done(); return true; }
}

function update() {
if (isDone()) return;
if (completion.options.async)
completion.getHints(completion.cm, finishUpdate, completion.options);
else
finishUpdate(completion.getHints(completion.cm, completion.options));
}
function finishUpdate(data) {
if (isDone()) return;
if (!data || !data.list.length) return done();
completion.widget.close();
completion.widget = new Widget(completion, data);
}

function activity() {
clearTimeout(debounce);
var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line);
if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch ||
pos.ch < startPos.ch || completion.cm.somethingSelected() ||
(pos.ch && closeOn.test(line.charAt(pos.ch - 1))))
completion.close();
else
debounce = setTimeout(update, 170);
}
this.cm.on("cursorActivity", activity);
this.onClose = done;
}
};

function getText(completion) {
if (typeof completion == "string") return completion;
else return completion.text;
}

function pickCompletion(cm, data, completion) {
if (completion.hint) completion.hint(cm, data, completion);
else cm.replaceRange(getText(completion), data.from, data.to);
function buildKeyMap(options, handle) {
var baseMap = {
Up: function() {handle.moveFocus(-1);},
Down: function() {handle.moveFocus(1);},
PageUp: function() {handle.moveFocus(-handle.menuSize());},
PageDown: function() {handle.moveFocus(handle.menuSize());},
Home: function() {handle.setFocus(0);},
End: function() {handle.setFocus(handle.length);},
Enter: handle.pick,
Tab: handle.pick,
Esc: handle.close
};
var ourMap = options.customKeys ? {} : baseMap;
function addBinding(key, val) {
var bound;
if (typeof val != "string")
bound = function(cm) { return val(cm, handle); };
// This mechanism is deprecated
else if (baseMap.hasOwnProperty(val))
bound = baseMap[val];
else
bound = val;
ourMap[key] = bound;
}
if (options.customKeys)
for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key))
addBinding(key, options.customKeys[key]);
if (options.extraKeys)
for (var key in options.extraKeys) if (options.extraKeys.hasOwnProperty(key))
addBinding(key, options.extraKeys[key]);
return ourMap;
}

function showHints(data) {
if (!data || !data.list.length) return;
var completions = data.list;
// When there is only one completion, use it directly.
if (!continued && options.completeSingle !== false && completions.length == 1) {
pickCompletion(cm, data, completions[0]);
CodeMirror.signal(data, "close");
return true;
}
function Widget(completion, data) {
this.completion = completion;
this.data = data;
var widget = this, cm = completion.cm, options = completion.options;

// Build the select widget
var hints = document.createElement("ul"), selectedHint = 0;
var hints = this.hints = document.createElement("ul");
hints.className = "CodeMirror-hints";
this.selectedHint = 0;

var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(document.createElement("li")), completion = completions[i];
var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
if (completion.className != null) className = completion.className + " " + className;
if (cur.className != null) className = cur.className + " " + className;
elt.className = className;
if (completion.render) completion.render(elt, data, completion);
else elt.appendChild(document.createTextNode(completion.displayText || getText(completion)));
if (cur.render) cur.render(elt, data, cur);
else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
elt.hintId = i;
}

var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
var left = pos.left, top = pos.bottom, below = true;
hints.style.left = left + "px";
hints.style.top = top + "px";
document.body.appendChild(hints);
CodeMirror.signal(data, "shown");

// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
Expand All @@ -75,106 +186,85 @@ CodeMirror.showHint = function(cm, getHints, options) {
}
hints.style.top = (top = pos.bottom - overlapY) + "px";
}
(options.container || document.body).appendChild(hints);

function changeActive(i) {
i = Math.max(0, Math.min(i, completions.length - 1));
if (selectedHint == i) return;
var node = hints.childNodes[selectedHint];
node.className = node.className.replace(" CodeMirror-hint-active", "");
node = hints.childNodes[selectedHint = i];
node.className += " CodeMirror-hint-active";
if (node.offsetTop < hints.scrollTop)
hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > hints.scrollTop + hints.clientHeight)
hints.scrollTop = node.offsetTop + node.offsetHeight - hints.clientHeight + 3;
CodeMirror.signal(data, "select", completions[selectedHint], node);
}
cm.addKeyMap(this.keyMap = buildKeyMap(options, {
moveFocus: function(n) { widget.changeActive(widget.selectedHint + n); },
setFocus: function(n) { widget.changeActive(n); },
menuSize: function() { return widget.screenAmount(); },
length: completions.length,
close: function() { completion.close(); },
pick: function() { widget.pick(); }
}));

function screenAmount() {
return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1;
if (options.closeOnUnfocus !== false) {
var closingOnBlur;
cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
}

var ourMap, baseMap = {
Up: function() {changeActive(selectedHint - 1);},
Down: function() {changeActive(selectedHint + 1);},
PageUp: function() {changeActive(selectedHint - screenAmount());},
PageDown: function() {changeActive(selectedHint + screenAmount());},
Home: function() {changeActive(0);},
End: function() {changeActive(completions.length - 1);},
Enter: pick,
Tab: pick,
Esc: close
};
if (options.customKeys) {
ourMap = {};
for (var key in options.customKeys) if (options.customKeys.hasOwnProperty(key)) {
var val = options.customKeys[key];
if (baseMap.hasOwnProperty(val)) val = baseMap[val];
ourMap[key] = val;
}
} else ourMap = baseMap;

cm.addKeyMap(ourMap);
cm.on("cursorActivity", cursorActivity);
var closingOnBlur;
function onBlur(){ closingOnBlur = setTimeout(close, 100); };
function onFocus(){ clearTimeout(closingOnBlur); };
cm.on("blur", onBlur);
cm.on("focus", onFocus);
var startScroll = cm.getScrollInfo();
function onScroll() {
cm.on("scroll", this.onScroll = function() {
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
var newTop = top + startScroll.top - curScroll.top, point = newTop;
var newTop = top + startScroll.top - curScroll.top;
var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
if (!below) point += hints.offsetHeight;
if (point <= editor.top || point >= editor.bottom) return close();
if (point <= editor.top || point >= editor.bottom) return completion.close();
hints.style.top = newTop + "px";
hints.style.left = (left + startScroll.left - curScroll.left) + "px";
}
cm.on("scroll", onScroll);
});

CodeMirror.on(hints, "dblclick", function(e) {
var t = e.target || e.srcElement;
if (t.hintId != null) {selectedHint = t.hintId; pick();}
if (t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
});
CodeMirror.on(hints, "click", function(e) {
var t = e.target || e.srcElement;
if (t.hintId != null) changeActive(t.hintId);
if (t.hintId != null) widget.changeActive(t.hintId);
});
CodeMirror.on(hints, "mousedown", function() {
setTimeout(function(){cm.focus();}, 20);
});

var done = false, once;
function close(willContinue) {
if (done) return;
done = true;
clearTimeout(once);
hints.parentNode.removeChild(hints);
cm.removeKeyMap(ourMap);
cm.off("cursorActivity", cursorActivity);
cm.off("blur", onBlur);
cm.off("focus", onFocus);
cm.off("scroll", onScroll);
if (willContinue !== true) CodeMirror.signal(data, "close");
}
function pick() {
pickCompletion(cm, data, completions[selectedHint]);
close();
}
var once, lastPos = cm.getCursor(), lastLen = cm.getLine(lastPos.line).length;
function cursorActivity() {
clearTimeout(once);

var pos = cm.getCursor(), line = cm.getLine(pos.line);
if (pos.line != lastPos.line || line.length - pos.ch != lastLen - lastPos.ch ||
pos.ch < startCh || cm.somethingSelected() ||
(pos.ch && closeOn.test(line.charAt(pos.ch - 1))))
close();
else
once = setTimeout(function(){close(true); continued = true; startHinting();}, 70);
}
CodeMirror.signal(data, "select", completions[0], hints.firstChild);
return true;
}

return startHinting();
};
Widget.prototype = {
close: function() {
if (this.completion.widget != this) return;
this.completion.widget = null;
this.hints.parentNode.removeChild(this.hints);
this.completion.cm.removeKeyMap(this.keyMap);

var cm = this.completion.cm;
if (this.completion.options.closeOnUnfocus !== false) {
cm.off("blur", this.onBlur);
cm.off("focus", this.onFocus);
}
cm.off("scroll", this.onScroll);
},

pick: function() {
this.completion.pick(this.data, this.selectedHint);
},

changeActive: function(i) {
i = Math.max(0, Math.min(i, this.data.list.length - 1));
if (this.selectedHint == i) return;
var node = this.hints.childNodes[this.selectedHint];
node.className = node.className.replace(" CodeMirror-hint-active", "");
node = this.hints.childNodes[this.selectedHint = i];
node.className += " CodeMirror-hint-active";
if (node.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
},

screenAmount: function() {
return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
}
};
})();
173 changes: 60 additions & 113 deletions addon/hint/xml-hint.js
Original file line number Diff line number Diff line change
@@ -1,118 +1,65 @@
(function() {

CodeMirror.xmlHints = [];

CodeMirror.xmlHint = function(cm) {

var cursor = cm.getCursor();

if (cursor.ch > 0) {

var text = cm.getRange(CodeMirror.Pos(0, 0), cursor);
var typed = '';
var simbol = '';
for(var i = text.length - 1; i >= 0; i--) {
if(text[i] == ' ' || text[i] == '<') {
simbol = text[i];
break;
}
else {
typed = text[i] + typed;
}
}

text = text.slice(0, text.length - typed.length);

var path = getActiveElement(text) + simbol;
var hints = CodeMirror.xmlHints[path];

if(typeof hints === 'undefined')
hints = [''];
else {
hints = hints.slice(0);
for (var i = hints.length - 1; i >= 0; i--) {
if(hints[i].indexOf(typed) != 0)
hints.splice(i, 1);
}
}

return {
list: hints,
from: CodeMirror.Pos(cursor.line, cursor.ch - typed.length),
to: cursor
};
"use strict";

var Pos = CodeMirror.Pos;

CodeMirror.xmlHint = function(cm, options) {
var tags = options && options.schemaInfo;
var quote = (options && options.quoteChar) || '"';
if (!tags) return;
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "xml") return;
var result = [], replaceToken = false, prefix;
var isTag = token.string.charAt(0) == "<";
if (!inner.state.tagName || isTag) { // Tag completion
if (isTag) {
prefix = token.string.slice(1);
replaceToken = true;
}
var cx = inner.state.context, curTag = cx && tags[cx.tagName];
var childList = cx ? curTag && curTag.children : tags["!top"];
if (childList) {
for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0)
result.push("<" + childList[i]);
} else {
for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0))
result.push("<" + name);
}
if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0))
result.push("</" + cx.tagName + ">");
} else {
// Attribute completion
var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs;
if (!attrs) return;
if (token.type == "string" || token.string == "=") { // A value
var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
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 (token.type == "string") {
prefix = token.string;
if (/['"]/.test(token.string.charAt(0))) {
quote = token.string.charAt(0);
prefix = token.string.slice(1);
}
replaceToken = true;
}
};

var getActiveElement = function(text) {

var element = '';

if(text.length >= 0) {

var regex = new RegExp('<([^!?][^\\s/>]*)[\\s\\S]*?>', 'g');

var matches = [];
var match;
while ((match = regex.exec(text)) != null) {
matches.push({
tag: match[1],
selfclose: (match[0].slice(match[0].length - 2) === '/>')
});
}

for (var i = matches.length - 1, skip = 0; i >= 0; i--) {

var item = matches[i];

if (item.tag[0] == '/')
{
skip++;
}
else if (item.selfclose == false)
{
if (skip > 0)
{
skip--;
}
else
{
element = '<' + item.tag + '>' + element;
}
}
}

element += getOpenTag(text);
for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0)
result.push(quote + atValues[i] + quote);
} else { // An attribute name
if (token.type == "attribute") {
prefix = token.string;
replaceToken = true;
}

return element;
for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0))
result.push(attr);
}
}
return {
list: result,
from: replaceToken ? Pos(cur.line, token.start) : cur,
to: replaceToken ? Pos(cur.line, token.end) : cur
};

var getOpenTag = function(text) {

var open = text.lastIndexOf('<');
var close = text.lastIndexOf('>');

if (close < open)
{
text = text.slice(open);

if(text != '<') {

var space = text.indexOf(' ');
if(space < 0)
space = text.indexOf('\t');
if(space < 0)
space = text.indexOf('\n');

if (space < 0)
space = text.length;

return text.slice(0, space);
}
}

return '';
};

};
})();
24 changes: 24 additions & 0 deletions addon/lint/coffeescript-lint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js

CodeMirror.coffeeValidator = function(text) {
var found = [];
var parseError = function(err) {
var loc = err.lineNumber;
found.push({from: CodeMirror.Pos(loc-1, 0),
to: CodeMirror.Pos(loc, 0),
severity: err.level,
message: err.message});
};
try {
var res = coffeelint.lint(text);
for(var i = 0; i < res.length; i++) {
parseError(res[i]);
}
} catch(e) {
found.push({from: CodeMirror.Pos(e.location.first_line, 0),
to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
severity: 'error',
message: e.message});
}
return found;
};
50 changes: 50 additions & 0 deletions addon/merge/dep/diff_match_patch.js

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions addon/merge/merge.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.CodeMirror-diff {
position: relative;
border: 1px solid #ddd;
}

.CodeMirror-diff, .CodeMirror-diff .CodeMirror {
height: 350px;
}

.CodeMirror-diff-2pane .CodeMirror-diff-pane { width: 47%; }
.CodeMirror-diff-2pane .CodeMirror-diff-gap { width: 6%; }
.CodeMirror-diff-3pane .CodeMirror-diff-pane { width: 31%; }
.CodeMirror-diff-3pane .CodeMirror-diff-gap { width: 3.5%; }

.CodeMirror-diff-pane {
float: left;
}

.CodeMirror-diff-gap {
float: left;
height: 100%;
box-sizing: border-box;
overflow: hidden;
border-left: 1px solid #ddd;
border-right: 1px solid #ddd;
position: relative;
background: #f8f8f8;
}

.CodeMirror-diff-scrolllock-wrap {
position: absolute;
bottom: 0; left: 50%;
}
.CodeMirror-diff-scrolllock {
position: relative;
left: -50%;
cursor: pointer;
color: #555;
line-height: 1;
}

.CodeMirror-diff-copybuttons-left, .CodeMirror-diff-copybuttons-right {
position: absolute;
left: 0; top: 0;
right: 0; bottom: 0;
line-height: 1;
}

.CodeMirror-diff-copy {
position: absolute;
cursor: pointer;
color: #44c;
}

.CodeMirror-diff-copybuttons-left .CodeMirror-diff-copy { left: 2px; }
.CodeMirror-diff-copybuttons-right .CodeMirror-diff-copy { right: 2px; }

.CodeMirror-diff-r-inserted, .CodeMirror-diff-l-inserted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}

.CodeMirror-diff-r-deleted, .CodeMirror-diff-l-deleted {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}

.CodeMirror-diff-r-chunk { background: #ffffe0; }
.CodeMirror-diff-r-chunk-start { border-top: 1px solid #ee8; }
.CodeMirror-diff-r-chunk-end { border-bottom: 1px solid #ee8; }
.CodeMirror-diff-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }

.CodeMirror-diff-l-chunk { background: #eef; }
.CodeMirror-diff-l-chunk-start { border-top: 1px solid #88e; }
.CodeMirror-diff-l-chunk-end { border-bottom: 1px solid #88e; }
.CodeMirror-diff-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }

.CodeMirror-diff-l-chunk.CodeMirror-diff-r-chunk { background: #dfd; }
.CodeMirror-diff-l-chunk-start.CodeMirror-diff-r-chunk-start { border-top: 1px solid #4e4; }
.CodeMirror-diff-l-chunk-end.CodeMirror-diff-r-chunk-end { border-bottom: 1px solid #4e4; }
431 changes: 431 additions & 0 deletions addon/merge/merge.js

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion addon/mode/multiplex.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CodeMirror.multiplexingMode = function(outer /*, others */) {
// Others should be {open, close, mode [, delimStyle]} objects
// Others should be {open, close, mode [, delimStyle] [, innerStyle]} objects
var others = Array.prototype.slice.call(arguments, 1);
var n_others = others.length;

Expand Down Expand Up @@ -58,6 +58,12 @@ CodeMirror.multiplexingMode = function(outer /*, others */) {
if (found > -1) stream.string = oldContent;
var cur = stream.current(), found = cur.indexOf(curInner.close);
if (found > -1) stream.backUp(cur.length - found);

if (curInner.innerStyle) {
if (innerToken) innerToken = innerToken + ' ' + curInner.innerStyle;
else innerToken = curInner.innerStyle;
}

return innerToken;
}
},
Expand Down
30 changes: 30 additions & 0 deletions addon/mode/multiplex_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
(function() {
CodeMirror.defineMode("markdown_with_stex", function(){
var inner = CodeMirror.getMode({}, "stex");
var outer = CodeMirror.getMode({}, "markdown");

var innerOptions = {
open: '$',
close: '$',
mode: inner,
delimStyle: 'delim',
innerStyle: 'inner'
};

return CodeMirror.multiplexingMode(outer, innerOptions);
});

var mode = CodeMirror.getMode({}, "markdown_with_stex");

function MT(name) {
test.mode(
name,
mode,
Array.prototype.slice.call(arguments, 1),
'multiplexing');
}

MT(
"stexInsideMarkdown",
"[strong **Equation:**] [delim $][inner&tag \\pi][delim $]");
})();
66 changes: 46 additions & 20 deletions addon/search/match-highlighter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,49 @@
// document.
//
// The option can be set to true to simply enable it, or to a
// {minChars, style} object to explicitly configure it. minChars is
// the minimum amount of characters that should be selected for the
// behavior to occur, and style is the token style to apply to the
// matches. This will be prefixed by "cm-" to create an actual CSS
// class name.
// {minChars, style, showToken} object to explicitly configure it.
// minChars is the minimum amount of characters that should be
// selected for the behavior to occur, and style is the token style to
// apply to the matches. This will be prefixed by "cm-" to create an
// actual CSS class name. showToken, when enabled, will cause the
// current token to be highlighted when nothing is selected.

(function() {
var DEFAULT_MIN_CHARS = 2;
var DEFAULT_TOKEN_STYLE = "matchhighlight";

function State(options) {
this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS;
this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE;
this.overlay = null;
if (typeof options == "object") {
this.minChars = options.minChars;
this.style = options.style;
this.showToken = options.showToken;
}
if (this.style == null) this.style = DEFAULT_TOKEN_STYLE;
if (this.minChars == null) this.minChars = DEFAULT_MIN_CHARS;
this.overlay = this.timeout = null;
}

CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
cm.state.matchHighlighter = new State(val);
cm.on("cursorActivity", highlightMatches);
} else if (!val && prev) {
if (old && old != CodeMirror.Init) {
var over = cm.state.matchHighlighter.overlay;
if (over) cm.removeOverlay(over);
clearTimeout(cm.state.matchHighlighter.timeout);
cm.state.matchHighlighter = null;
cm.off("cursorActivity", highlightMatches);
cm.off("cursorActivity", cursorActivity);
}
if (val) {
cm.state.matchHighlighter = new State(val);
highlightMatches(cm);
cm.on("cursorActivity", cursorActivity);
}
});

function cursorActivity(cm) {
var state = cm.state.matchHighlighter;
clearTimeout(state.timeout);
state.timeout = setTimeout(function() {highlightMatches(cm);}, 100);
}

function highlightMatches(cm) {
cm.operation(function() {
var state = cm.state.matchHighlighter;
Expand All @@ -42,17 +56,29 @@
state.overlay = null;
}

if (!cm.somethingSelected()) return;
if (!cm.somethingSelected() && state.showToken) {
var tok = cm.getTokenAt(cm.getCursor()).string;
if (/\w/.test(tok))
cm.addOverlay(state.overlay = makeOverlay(tok, true, state.style));
return;
}
if (cm.getCursor("head").line != cm.getCursor("anchor").line) return;
var selection = cm.getSelection().replace(/^\s+|\s+$/g, "");
if (selection.length < state.minChars) return;

cm.addOverlay(state.overlay = makeOverlay(selection, state.style));
if (selection.length >= state.minChars)
cm.addOverlay(state.overlay = makeOverlay(selection, false, state.style));
});
}

function makeOverlay(query, style) {
function boundariesAround(stream) {
return (stream.start || /.\b./.test(stream.string.slice(stream.start - 1, stream.start + 1))) &&
(stream.pos == stream.string.length || /.\b./.test(stream.string.slice(stream.pos - 1, stream.pos + 1)));
}

function makeOverlay(query, wordBoundaries, style) {
return {token: function(stream) {
if (stream.match(query)) return style;
if (stream.match(query) &&
(!wordBoundaries || boundariesAround(stream)))
return style;
stream.next();
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
}};
Expand Down
2 changes: 1 addition & 1 deletion addon/search/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
if (!query || state.query) return;
state.query = parseQuery(query);
cm.removeOverlay(state.overlay);
state.overlay = searchOverlay(query);
state.overlay = searchOverlay(state.query);
cm.addOverlay(state.overlay);
state.posFrom = state.posTo = cm.getCursor();
findNext(cm, rev);
Expand Down
14 changes: 9 additions & 5 deletions bin/lint
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

var lint = require("../test/lint/lint");

process.chdir(__dirname.slice(0, __dirname.lastIndexOf("/")));

lint.checkDir("mode");
lint.checkDir("lib");
lint.checkDir("addon");
if (process.argv.length > 2) {
lint.checkDir(process.argv[2]);
} else {
process.chdir(__dirname.slice(0, __dirname.lastIndexOf("/")));
lint.checkDir("lib");
lint.checkDir("mode");
lint.checkDir("addon");
lint.checkDir("keymap");
}

process.exit(lint.success() ? 0 : 1);
16 changes: 16 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "CodeMirror",
"version": "3.13.0",
"main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [
"**/.*",
"node_modules",
"components",
"bin",
"demo",
"doc",
"test",
"index.html",
"package.json"
]
}
6 changes: 6 additions & 0 deletions demo/emacs.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
<script src="../lib/codemirror.js"></script>
<script src="../mode/clike/clike.js"></script>
<script src="../keymap/emacs.js"></script>
<script src="../addon/edit/matchbrackets.js"></script>
<script src="../addon/comment/comment.js"></script>
<script src="../addon/dialog/dialog.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/search/search.js"></script>
<link rel="stylesheet" href="../addon/dialog/dialog.css">
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css">
Expand Down
18 changes: 9 additions & 9 deletions demo/folding.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,26 +43,26 @@ <h1>CodeMirror: Code Folding Demo</h1>
var te_html = document.getElementById("code-html");
te_html.value = "<html>\n " + document.documentElement.innerHTML + "\n</html>";

var foldFunc = CodeMirror.newFoldFunction(CodeMirror.braceRangeFinder);
function foldJS(cm, where) { cm.foldCode(where, CodeMirror.braceRangeFinder); }
window.editor = CodeMirror.fromTextArea(te, {
mode: "javascript",
lineNumbers: true,
lineWrapping: true,
extraKeys: {"Ctrl-Q": function(cm){foldFunc(cm, cm.getCursor().line);}}
extraKeys: {"Ctrl-Q": function(cm){foldJS(cm, cm.getCursor());}}
});
editor.on("gutterClick", foldFunc);
foldFunc(editor, 9);
editor.on("gutterClick", foldJS);
foldJS(editor, 9);

var foldFunc_html = CodeMirror.newFoldFunction(CodeMirror.tagRangeFinder);
function foldHTML(cm, where) { cm.foldCode(where, CodeMirror.tagRangeFinder); }
window.editor_html = CodeMirror.fromTextArea(te_html, {
mode: "text/html",
lineNumbers: true,
lineWrapping: true,
extraKeys: {"Ctrl-Q": function(cm){foldFunc_html(cm, cm.getCursor().line);}}
extraKeys: {"Ctrl-Q": function(cm){foldHTML(cm, cm.getCursor());}}
});
editor_html.on("gutterClick", foldFunc_html);
foldFunc_html(editor_html, 11);
foldFunc_html(editor_html, 1);
editor_html.on("gutterClick", foldHTML);
foldHTML(editor_html, 13);
foldHTML(editor_html, 1);
};
</script>
</body>
Expand Down
76 changes: 14 additions & 62 deletions demo/html5complete.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Close-Tag Demo</title>
<title>CodeMirror: HTML completion demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/show-hint.js"></script>
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<script src="../addon/edit/closetag.js"></script>
<script src="../addon/hint/xml-hint.js"></script>
<script src="../addon/hint/html-hint.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../mode/javascript/javascript.js"></script>
Expand All @@ -21,72 +21,24 @@
</head>
<body>

<h1>HTML5 code completation demo</h1>
<ul>
<li>Type an html tag. If you press Ctrl+Space a hint panel show the code suggest. You can type to autocomplete tags, attributes if your cursor are inner a tag or attribute values if your cursor are inner a attribute value.</li>
</ul>
<h1>HTML completion demo</h1>
<p>Shows the <a href="xmlcomplete.html">XML completer</a>
parameterized with information about the tags in HTML.
Press <strong>ctrl-space</strong> to activate completion.</p>

<form><textarea id="code" name="code">
<!DOCTYPE html>
<html lang="en">
<div id="code"></div>

<head>
<meta charset="utf-8">
<title>Home - W2S Web IDE</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
</head>

<body>

<div class="container">

<div class="masthead">
<h3 class="muted">HTML5 Autocomplete</h3>
<div class="navbar">
<div class="navbar-inner">
<div class="container">
<ul class="nav">
<li class=" active "><a href="#">Home</a></li>
<li class=""><a href="#">Features</a></li>
<li class=""><a href="#">Sign In</a></li>
</ul>
</div>
</div>
</div><!-- /.navbar -->
</div>

<!-- Jumbotron -->
<div class="jumbotron">
<img src="/Images/w2s.png" />
<h1>W2S Cloud IDE</h1>
</div>

<hr>


<hr>

<div class="footer">
Final of html5 autocomplete
</div>

</div> <!-- /container -->
</body>
</html>

</textarea></form>
<script type="text/javascript">
CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.htmlHint);
}

var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: 'text/html',
autoCloseTags: true,
extraKeys: {"Ctrl-Space": "autocomplete"}
});
window.onload = function() {
editor = CodeMirror(document.getElementById("code"), {
mode: "text/html",
extraKeys: {"Ctrl-Space": "autocomplete"},
value: "<!doctype html>\n<html>\n " + document.documentElement.innerHTML + "\n</html>"
});
};
</script>
</body>
</html>
63 changes: 63 additions & 0 deletions demo/merge.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!doctype html>
<head>
<meta charset="utf-8">
<link rel=stylesheet href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../addon/merge/dep/diff_match_patch.js"></script>
<link rel=stylesheet href="../addon/merge/merge.css">
<script src="../addon/merge/merge.js"></script>
<link rel="stylesheet" href="../doc/docs.css">
<title>CodeMirror: merge view demo</title>
<style>
.CodeMirror { line-height: 1.2; }
body { max-width: 80em; }
span.clicky {
cursor: pointer;
background: #d70;
color: white;
padding: 0 3px;
border-radius: 3px;
}
</style>
</head>

<h1>CodeMirror: merge view demo</h1>

<div id=view></div>

<p>The <a href="../doc/manual.html#addon_merge"><code>merge</code></a>
addon provides an interface for displaying and merging diffs,
either <span class=clicky onclick="initUI(2)">two-way</span>
or <span class=clicky onclick="initUI(3)">three-way</span>. The left
(or center) pane is editable, and the differences with the other
pane(s) are shown live as you edit it.</p>

<p>This addon depends on
the <a href="https://code.google.com/p/google-diff-match-patch/">google-diff-match-patch</a>
library to compute the diffs.</p>

<script>
var value, orig1, orig2, dv;

function initUI(panes) {
if (value == null) return;
var target = document.getElementById("view");
target.innerHTML = "";
dv = CodeMirror.MergeView(target, {
value: value,
origLeft: panes == 3 ? orig1 : null,
orig: orig2,
lineNumbers: true,
mode: "text/html"
});
}

window.onload = function() {
value = document.documentElement.innerHTML;
orig1 = 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(2);
};
</script>
6 changes: 6 additions & 0 deletions demo/multiplex.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,11 @@ <h1><< this is not <html >></h1>
the <a href="../addon/mode/multiplex.js">source</a> for more
information.</p>

<p>
<strong>Parsing/Highlighting Tests:</strong>
<a href="../test/index.html#multiplexing_*">normal</a>,
<a href="../test/index.html#verbose,multiplexing_*">verbose</a>.
</p>

</body>
</html>
1 change: 1 addition & 0 deletions demo/theme.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<link rel="stylesheet" href="../theme/twilight.css">
<link rel="stylesheet" href="../theme/midnight.css">
<script src="../mode/javascript/javascript.js"></script>
<script src="../keymap/extra.js"></script>
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css">
Expand Down
39 changes: 39 additions & 0 deletions demo/trailingspace.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Trailing Whitespace Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../addon/edit/trailingspace.js"></script>
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.cm-trailingspace {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==);
background-position: bottom left;
background-repeat: repeat-x;
}
</style>
</head>
<body>
<h1>CodeMirror: Trailing Whitespace Demo</h1>

<form><textarea id="code" name="code">This text
has some
trailing whitespace!</textarea></form>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
showTrailingSpace: true
});
</script>

<p>Uses
the <a href="../doc/manual.html#addon_trailingspace">trailingspace</a>
addon to highlight trailing whitespace.</p>

</body>
</html>
125 changes: 75 additions & 50 deletions demo/xmlcomplete.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,75 +7,100 @@
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/show-hint.js"></script>
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<script src="../addon/edit/closetag.js"></script>
<script src="../addon/hint/xml-hint.js"></script>
<script src="../mode/xml/xml.js"></script>
<link rel="stylesheet" href="../doc/docs.css">
<style type="text/css">
.CodeMirror { border: 1px solid #eee; height: auto; }
.CodeMirror-scroll { overflow-y: hidden; overflow-x: auto; }
.CodeMirror { border: 1px solid #eee; }
</style>
</head>
<body>
<h1>CodeMirror: XML Autocomplete demo</h1>

<form><textarea id="code" name="code"></textarea></form>
<form><textarea id="code" name="code"><!-- write some xml below -->
</textarea></form>

<p>Type '&lt;' or space inside tag or
press <strong>ctrl-space</strong> to activate autocompletion. See
the code (<a href="../addon/hint/show-hint.js">here</a>
and <a href="../addon/hint/xml-hint.js">here</a>) to figure out how
it works.</p>
<p>Press <strong>ctrl-space</strong>, or type a '<' character to
activate autocompletion. This demo defines a simple schema that
guides completion. The schema can be customized—see
the <a href="../doc/manual.html#addon_xml-hint">manual</a>.</p>

<script>
CodeMirror.xmlHints['<'] = [
'levelTop',
'levelRoot',
'mainLevel'
];

CodeMirror.xmlHints['<levelTop '] =
CodeMirror.xmlHints['<levelRoot '] =
CodeMirror.xmlHints['<mainLevel '] = [
'property1111',
'property2222'
];
<p>Development of the <code>xml-hint</code> addon was kindly
sponsored
by <a href="http://www.xperiment.mobi">www.xperiment.mobi</a>.</p>

CodeMirror.xmlHints['<levelTop><'] =
CodeMirror.xmlHints['<levelRoot><'] =
CodeMirror.xmlHints['<mainLevel><'] = [
'second',
'two'
];
<script>
var dummy = {
attrs: {
color: ["red", "green", "blue", "purple", "white", "black", "yellow"],
size: ["large", "medium", "small"],
description: null
},
children: []
};

CodeMirror.xmlHints['<levelTop><second '] = [
'secondProperty'
];
var tags = {
"!top": ["top"],
top: {
attrs: {
lang: ["en", "de", "fr", "nl"],
freeform: null
},
children: ["animal", "plant"]
},
animal: {
attrs: {
name: null,
isduck: ["yes", "no"]
},
children: ["wings", "feet", "body", "head", "tail"]
},
plant: {
attrs: {name: null},
children: ["leaves", "stem", "flowers"]
},
wings: dummy, feet: dummy, body: dummy, head: dummy, tail: dummy,
leaves: dummy, stem: dummy, flowers: dummy
};

CodeMirror.xmlHints['<levelTop><second><'] = [
'three',
'x-three'
];
function completeAfter(cm, pred) {
var cur = cm.getCursor();
if (!pred || pred()) setTimeout(function() {
if (!cm.state.completionActive)
CodeMirror.showHint(cm, CodeMirror.xmlHint, {schemaInfo: tags, completeSingle: false});
}, 100);
return CodeMirror.Pass;
}

CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.xmlHint);
function completeIfAfterLt(cm) {
return completeAfter(cm, function() {
var cur = cm.getCursor();
return cm.getRange(CodeMirror.Pos(cur.line, cur.ch - 1), cur) == "<";
});
}
function passAndHint(cm) {
setTimeout(function() {cm.execCommand("autocomplete");}, 100);
return CodeMirror.Pass;

function completeIfInTag(cm) {
return completeAfter(cm, function() {
var tok = cm.getTokenAt(cm.getCursor());
if (tok.type == "string" && (!/['"]/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1)) return false;
var inner = CodeMirror.innerMode(cm.getMode(), tok.state).state;
return inner.tagName;
});
}

var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
value: '',
mode: 'text/html',
lineNumbers: true,
extraKeys: {
"' '": passAndHint,
"'<'": passAndHint,
"Ctrl-Space": "autocomplete"
},
autoCloseTags: true
});
mode: "xml",
lineNumbers: true,
extraKeys: {
"'<'": completeAfter,
"'/'": completeIfAfterLt,
"' '": completeIfInTag,
"'='": completeIfInTag,
"Ctrl-Space": function(cm) {
CodeMirror.showHint(cm, CodeMirror.xmlHint, {schemaInfo: tags});
}
}
});
</script>
</body>
</html>
2 changes: 2 additions & 0 deletions doc/compress.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<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=3.14.0;f=">3.14</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=3.13.0;f=">3.13</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v3.12;f=">3.12</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v3.11;f=">3.11</option>
Expand Down Expand Up @@ -169,6 +170,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<option value="http://codemirror.net/addon/lint/lint.js">lint.js</option>
<option value="http://codemirror.net/addon/lint/javascript-lint.js">javascript-lint.js</option>
<option value="http://codemirror.net/addon/lint/json-lint.js">json-lint.js</option>
<option value="http://codemirror.net/addon/merge/merge.js">merge.js</option>
</optgroup>
<optgroup label="Keymaps">
<option value="http://codemirror.net/keymap/emacs.js">emacs.js</option>
Expand Down
422 changes: 334 additions & 88 deletions doc/manual.html

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions doc/oldrelease.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
</pre>
</div>

<p class="rel">20-11-2012: <a href="http://codemirror.net/codemirror-2.36.zip">Version 2.36</a>:</p>

<ul class="rel-note">
<li>New mode: <a href="../mode/z80/index.html">Z80 assembly</a>.</li>
<li>New theme: <a href="../demo/theme.html?twilight">Twilight</a>.</li>
<li>Add command-line compression helper.</li>
<li>Make <a href="manual.html#scrollIntoView"><code>scrollIntoView</code></a> public.</li>
<li>Add <a href="manual.html#defaultTextHeight"><code>defaultTextHeight</code></a> method.</li>
<li>Various extensions to the vim keymap.</li>
<li>Make <a href="../mode/php/index.html">PHP mode</a> build on <a href="../mode/htmlmixed/index.html">mixed HTML mode</a>.</li>
<li>Add <a href="manual.html#addon_continuecomment">comment-continuing</a> add-on.</li>
<li>Full <a href="../https://github.com/marijnh/CodeMirror/compare/v2.35...v2.36">list of patches</a>.</li>
</ul>

<p class="rel">20-11-2012: <a href="http://codemirror.net/codemirror-3.0rc1.zip">Version 3.0, release candidate 1</a>:</p>

<ul class="rel-note">
Expand Down
2 changes: 2 additions & 0 deletions doc/realworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<li><a href="http://ql.io/">ql.io</a> (http API query helper)</li>
<li><a href="http://qyapp.com">QiYun web app platform</a></li>
<li><a href="http://ariya.ofilabs.com/2011/09/hybrid-webnative-desktop-codemirror.html">Qt+Webkit integration</a> (building a desktop CodeMirror app)</li>
<li><a href="http://www.quivive-file-manager.com">Quivive File Manager</a></li>
<li><a href="http://rascalmicro.com/docs/basic-tutorial-getting-started.html">Rascal</a> (tiny computer)</li>
<li><a href="https://www.realtime.io/">RealTime.io</a> (Internet-of-Things infrastructure)</li>
<li><a href="http://www.sketchpatch.net/labs/livecodelabIntro.html">sketchPatch Livecodelab</a></li>
Expand All @@ -101,6 +102,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<li><a href="http://cruise.eecs.uottawa.ca/umpleonline/">UmpleOnline</a> (model-oriented programming tool)</li>
<li><a href="https://upsource.jetbrains.com/#idea/view/923f30395f2603cd9f42a32bcafd13b6c28de0ff/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/style/ReplaceAbstractClassInstanceByMapIntention.java">Upsource</a> (code viewer)</li>
<li><a href="http://webglplayground.net/">WebGL playground</a></li>
<li><a href="https://www.webkit.org/blog/2518/state-of-web-inspector/#source-code">WebKit Web inspector</a></li>
<li><a href="http://www.wescheme.org/">WeScheme</a> (learning tool)</li>
<li><a href="http://wordpress.org/extend/plugins/codemirror-for-codeeditor/">WordPress plugin</a></li>
<li><a href="http://www.xosystem.org/home/applications_websites/xosystem_website/xoside_EN.php">XOSide</a> (online editor)</li>
Expand Down
59 changes: 35 additions & 24 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,9 @@ <h2>Real-world uses:</h2>
<ul>
<li><a href="http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/">Light Table</a></li>
<li><a href="http://brackets.io">Adobe Brackets</a></li>
<li><a href="https://www.webkit.org/blog/2518/state-of-web-inspector/#source-code">WebKit Web inspector</a></li>
<li><a href="http://jsbin.com">jsbin.com</a></li>
<li><a href="http://jsfiddle.com">jsfiddle.com</a></li>
<li><a href="http://jsfiddle.net">jsfiddle.net</a></li>
<li><a href="http://blog.bitbucket.org/2013/05/14/edit-your-code-in-the-cloud-with-bitbucket/">Bitbucket</a></li>
<li><a href="https://script.google.com/">Google Apps Script</a></li>
<li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript</a></li>
Expand Down Expand Up @@ -213,20 +214,17 @@ <h2 id="supported">Supported browsers</h2>
<li>Chrome, any version</li>
<li>Safari 5.2 or higher</li>
<li>Opera 9 or higher (with some key-handling problems on OS X)</li>
<li>Internet Explorer 8 or higher in standards mode<br>
<em>(<strong>Not</strong> quirks mode. But quasi-standards mode with a
transitional doctype is also flaky. <code>&lt;!doctype
html></code> is recommended.)</em></li>
<li>Internet Explorer 8 or higher<br>
<li>Internet Explorer 7 (standards mode) is usable, but buggy. It
has a <a href="http://therealcrisp.xs4all.nl/meuk/IE-zindexbug.html">z-index
bug</a> that prevents CodeMirror from working properly.</li>
</ul>

<p>I am not actively testing against every new browser release, and
vendors have a habit of introducing bugs all the time, so I am
relying on the community to tell me when something breaks.
See <a href="#support">here</a> for information on how to contact
me.</p>
<p>Note that CodeMirror is only supported in
<strong>standards</strong> mode. So not quirks mode,
but <em>also</em> not the quasi-standards mode that IE gives you
when you specify a transitional doctype. Simply using the
HTML5-style <code>&lt;!doctype html></code> is recommended.</li>

<p>Mobile browsers mostly kind of work, but, because of limitations
and their fundamentally different UI assumptions, show a lot of
Expand Down Expand Up @@ -287,6 +285,33 @@ <h2>Reading material</h2>

<h2 id=releases>Releases</h2>

<p class="rel">20-06-2013: <a href="http://codemirror.net/codemirror-3.14.zip">Version 3.14</a>:</p>

<ul class="rel-note">
<li>New
addons: <a href="doc/manual.html#addon_trailingspace">trailing
space highlight</a>, <a href="doc/manual.html#addon_xml-hint">XML
completion</a> (rewritten),
and <a href="doc/manual.html#addon_merge">diff merging</a>.</li>
<li><a href="doc/manual.html#markText"><code>markText</code></a>
and <a href="doc/manual.html#addLineWidget"><code>addLineWidget</code></a>
now take a <code>handleMouseEvents</code> option.</li>
<li>New methods: <a href="doc/manual.html#lineAtHeight"><code>lineAtHeight</code></a>,
<a href="doc/manual.html#getTokenTypeAt"><code>getTokenTypeAt</code></a>.</li>
<li>More precise cleanness-tracking
using <a href="doc/manual.html#changeGeneration"><code>changeGeneration</code></a>
and <a href="doc/manual.html#isClean"><code>isClean</code></a>.</li>
<li>Many extensions to <a href="demo/emacs.html">Emacs</a> mode
(prefixes, more navigation units, and more).</li>
<li>New
events <a href="doc/manual.html#event_keyHandled"><code>"keyHandled"</code></a>
and <a href="doc/manual.html#event_inputRead"><code>"inputRead"</code></a>.</li>
<li>Various improvements to <a href="mode/ruby/index.html">Ruby</a>,
<a href="mode/smarty/index.html">Smarty</a>, <a href="mode/sql/index.html">SQL</a>,
and <a href="demo/vim.html">Vim</a> modes.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/3.13.0...3.14.0">list of patches</a>.</li>
</ul>

<p class="rel">20-05-2013: <a href="http://codemirror.net/codemirror-3.13.zip">Version 3.13</a>:</p>

<ul class="rel-note">
Expand Down Expand Up @@ -441,20 +466,6 @@ <h2 id=releases>Releases</h2>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/v3.0rc1...v3.0rc2">list of patches</a>.</li>
</ul>

<p class="rel">20-11-2012: <a href="http://codemirror.net/codemirror-2.36.zip">Version 2.36</a>:</p>

<ul class="rel-note">
<li>New mode: <a href="mode/z80/index.html">Z80 assembly</a>.</li>
<li>New theme: <a href="demo/theme.html?twilight">Twilight</a>.</li>
<li>Add command-line compression helper.</li>
<li>Make <a href="doc/manual.html#scrollIntoView"><code>scrollIntoView</code></a> public.</li>
<li>Add <a href="doc/manual.html#defaultTextHeight"><code>defaultTextHeight</code></a> method.</li>
<li>Various extensions to the vim keymap.</li>
<li>Make <a href="mode/php/index.html">PHP mode</a> build on <a href="mode/htmlmixed/index.html">mixed HTML mode</a>.</li>
<li>Add <a href="doc/manual.html#addon_continuecomment">comment-continuing</a> add-on.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/v2.35...v2.36">list of patches</a>.</li>
</ul>

<p><a href="doc/oldrelease.html">Older releases...</a></p>

</div></div>
Expand Down
383 changes: 370 additions & 13 deletions keymap/emacs.js

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions keymap/extra.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// A number of additional default bindings that are too obscure to
// include in the core codemirror.js file.

(function() {
"use strict";

var Pos = CodeMirror.Pos;

function moveLines(cm, start, end, dist) {
if (!dist || start > end) return 0;

var from = cm.clipPos(Pos(start, 0)), to = cm.clipPos(Pos(end));
var text = cm.getRange(from, to);

if (start <= cm.firstLine())
cm.replaceRange("", from, Pos(to.line + 1, 0));
else
cm.replaceRange("", Pos(from.line - 1), to);
var target = from.line + dist;
if (target <= cm.firstLine()) {
cm.replaceRange(text + "\n", Pos(target, 0));
return cm.firstLine() - from.line;
} else {
var targetPos = cm.clipPos(Pos(target - 1));
cm.replaceRange("\n" + text, targetPos);
return targetPos.line + 1 - from.line;
}
}

function moveSelectedLines(cm, dist) {
var head = cm.getCursor("head"), anchor = cm.getCursor("anchor");
cm.operation(function() {
var moved = moveLines(cm, Math.min(head.line, anchor.line), Math.max(head.line, anchor.line), dist);
cm.setSelection(Pos(anchor.line + moved, anchor.ch), Pos(head.line + moved, head.ch));
});
}

CodeMirror.commands.moveLinesUp = function(cm) { moveSelectedLines(cm, -1); };
CodeMirror.commands.moveLinesDown = function(cm) { moveSelectedLines(cm, 1); };

CodeMirror.keyMap["default"]["Alt-Up"] = "moveLinesUp";
CodeMirror.keyMap["default"]["Alt-Down"] = "moveLinesDown";
})();
749 changes: 535 additions & 214 deletions keymap/vim.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
}

.CodeMirror-widget {
display: inline-block;
}

.CodeMirror-wrap .CodeMirror-scroll {
Expand Down
576 changes: 327 additions & 249 deletions lib/codemirror.js

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions mode/clike/clike.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,4 +302,60 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
}
}
});
mimes(["x-shader/x-vertex", "x-shader/x-fragment"], {
name: "clike",
keywords: words("float int bool void " +
"vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
"mat2 mat3 mat4 " +
"sampler1D sampler2D sampler3D samplerCube " +
"sampler1DShadow sampler2DShadow" +
"const attribute uniform varying " +
"break continue discard return " +
"for while do if else struct " +
"in out inout"),
blockKeywords: words("for while do if else struct"),
builtin: words("radians degrees sin cos tan asin acos atan " +
"pow exp log exp2 sqrt inversesqrt " +
"abs sign floor ceil fract mod min max clamp mix step smootstep " +
"length distance dot cross normalize ftransform faceforward " +
"reflect refract matrixCompMult " +
"lessThan lessThanEqual greaterThan greaterThanEqual " +
"equal notEqual any all not " +
"texture1D texture1DProj texture1DLod texture1DProjLod " +
"texture2D texture2DProj texture2DLod texture2DProjLod " +
"texture3D texture3DProj texture3DLod texture3DProjLod " +
"textureCube textureCubeLod " +
"shadow1D shadow2D shadow1DProj shadow2DProj " +
"shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
"dFdx dFdy fwidth " +
"noise1 noise2 noise3 noise4"),
atoms: words("true false " +
"gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
"gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
"gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
"gl_FogCoord " +
"gl_Position gl_PointSize gl_ClipVertex " +
"gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
"gl_TexCoord gl_FogFragCoord " +
"gl_FragCoord gl_FrontFacing " +
"gl_FragColor gl_FragData gl_FragDepth " +
"gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
"gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
"gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
"gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
"gl_ProjectionMatrixInverseTranspose " +
"gl_ModelViewProjectionMatrixInverseTranspose " +
"gl_TextureMatrixInverseTranspose " +
"gl_NormalScale gl_DepthRange gl_ClipPlane " +
"gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
"gl_FrontLightModelProduct gl_BackLightModelProduct " +
"gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
"gl_FogParameters " +
"gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
"gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
"gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
"gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
"gl_MaxDrawBuffers"),
hooks: {"#": cppHook}
});
}());
33 changes: 19 additions & 14 deletions mode/javascript/javascript.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

CodeMirror.defineMode("javascript", function(config, parserConfig) {
var indentUnit = config.indentUnit;
var statementIndent = parserConfig.statementIndent;
var jsonMode = parserConfig.json;
var isTS = parserConfig.typescript;

Expand Down Expand Up @@ -226,8 +227,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function pushlex(type, info) {
var result = function() {
var state = cx.state;
state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
var state = cx.state, indent = state.indented;
if (state.lexical.type == "stat") indent = state.lexical.indented;
state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
};
result.lex = true;
return result;
Expand Down Expand Up @@ -270,17 +272,18 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
return pass(pushlex("stat"), expression, expect(";"), poplex);
}
function expression(type) {
return expressionInner(type, maybeoperatorComma);
return expressionInner(type, false);
}
function expressionNoComma(type) {
return expressionInner(type, maybeoperatorNoComma);
return expressionInner(type, true);
}
function expressionInner(type, maybeop) {
function expressionInner(type, noComma) {
var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
if (type == "function") return cont(functiondef);
if (type == "keyword c") return cont(maybeexpression);
if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
if (type == "operator") return cont(expression);
if (type == "operator") return cont(noComma ? expressionNoComma : expression);
if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop);
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop);
return cont();
Expand All @@ -289,9 +292,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expression);
}
function maybeexpressionNoComma(type) {
if (type.match(/[;\}\)\],]/)) return pass();
return pass(expressionNoComma);
}

function maybeoperatorComma(type, value) {
if (type == ",") return pass();
if (type == ",") return cont(expression);
return maybeoperatorNoComma(type, value, maybeoperatorComma);
}
function maybeoperatorNoComma(type, value, me) {
Expand Down Expand Up @@ -435,18 +442,16 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
if (state.tokenize != jsTokenBase) return 0;
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
lexical = lexical.prev;
var type = lexical.type, closing = firstChar == type;
if (parserConfig.statementIndent != null) {
if (type == ")" && lexical.prev && lexical.prev.type == "stat") lexical = lexical.prev;
if (lexical.type == "stat") return lexical.indented + parserConfig.statementIndent;
}

if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0);
else if (type == "form" && firstChar == "{") return lexical.indented;
else if (type == "form") return lexical.indented + indentUnit;
else if (type == "stat")
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0);
else if (lexical.info == "switch" && !closing)
return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0);
else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
else return lexical.indented + (closing ? 0 : indentUnit);
Expand Down
4 changes: 2 additions & 2 deletions mode/markdown/markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,11 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
return type;
}

if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) {
if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
return switchInline(stream, state, inlineElement(linkinline, '>'));
}

if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, true)) {
if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
return switchInline(stream, state, inlineElement(linkemail, '>'));
}

Expand Down
6 changes: 6 additions & 0 deletions mode/markdown/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,9 +533,15 @@
MT("linkWeb",
"[link <http://example.com/>] foo");

MT("linkWebDouble",
"[link <http://example.com/>] foo [link <http://example.com/>]");

MT("linkEmail",
"[link <user@example.com>] foo");

MT("linkEmailDouble",
"[link <user@example.com>] foo [link <user@example.com>]");

MT("emAsterisk",
"[em *foo*] bar");

Expand Down
83 changes: 67 additions & 16 deletions mode/ruby/ruby.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ CodeMirror.defineMode("ruby", function(config) {
"redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
"until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc",
"caller", "lambda", "proc", "public", "protected", "private", "require", "load",
"require_relative", "extend", "autoload"
"require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__"
]);
var indentWords = wordObj(["def", "class", "case", "for", "while", "do", "module", "then",
"catch", "loop", "proc", "begin"]);
Expand All @@ -31,14 +31,16 @@ CodeMirror.defineMode("ruby", function(config) {
}
if (stream.eatSpace()) return null;
var ch = stream.next(), m;
if (ch == "`" || ch == "'" || ch == '"' ||
(ch == "/" && !stream.eol() && stream.peek() != " ")) {
if (ch == "`" || ch == "'" || ch == '"') {
return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
} else if (ch == "/" && !stream.eol() && stream.peek() != " ") {
return chain(readQuoted(ch, "string-2", true), stream, state);
} else if (ch == "%") {
var style, embed = false;
var style = "string", embed = false;
if (stream.eat("s")) style = "atom";
else if (stream.eat(/[WQ]/)) { style = "string"; embed = true; }
else if (stream.eat(/[wxqr]/)) style = "string";
else if (stream.eat(/[r]/)) { style = "string-2"; embed = true; }
else if (stream.eat(/[wxq]/)) style = "string";
var delim = stream.eat(/[^\w\s]/);
if (!delim) return "operator";
if (matching.propertyIsEnumerable(delim)) delim = matching[delim];
Expand All @@ -64,18 +66,42 @@ CodeMirror.defineMode("ruby", function(config) {
} else if (ch == ":") {
if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state);
if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state);
stream.eatWhile(/[\w\?]/);
return "atom";
} else if (ch == "@") {

// :> :>> :< :<< are valid symbols
if (stream.eat(/[\<\>]/)) {
stream.eat(/[\<\>]/);
return "atom";
}

// :+ :- :/ :* :| :& :! are valid symbols
if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
return "atom";
}

// Symbols can't start by a digit
if (stream.eat(/[a-zA-Z$@_]/)) {
stream.eatWhile(/[\w]/);
// Only one ? ! = is allowed and only as the last character
stream.eat(/[\?\!\=]/);
return "atom";
}
return "operator";
} else if (ch == "@" && stream.match(/^@?[a-zA-Z_]/)) {
stream.eat("@");
stream.eatWhile(/[\w\?]/);
stream.eatWhile(/[\w]/);
return "variable-2";
} else if (ch == "$") {
stream.next();
stream.eatWhile(/[\w\?]/);
if (stream.eat(/[a-zA-Z_]/)) {
stream.eatWhile(/[\w]/);
} else if (stream.eat(/\d/)) {
stream.eat(/\d/);
} else {
stream.next(); // Must be a special global like $: or $!
}
return "variable-3";
} else if (/\w/.test(ch)) {
stream.eatWhile(/[\w\?]/);
} else if (/[a-zA-Z_]/.test(ch)) {
stream.eatWhile(/[\w]/);
stream.eat(/[\?\!]/);
if (stream.eat(":")) return "atom";
return "ident";
} else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) {
Expand Down Expand Up @@ -109,17 +135,42 @@ CodeMirror.defineMode("ruby", function(config) {
return tokenBase(stream, state);
};
}
function tokenBaseOnce() {
var alreadyCalled = false;
return function(stream, state) {
if (alreadyCalled) {
state.tokenize.pop();
return state.tokenize[state.tokenize.length-1](stream, state);
}
alreadyCalled = true;
return tokenBase(stream, state);
};
}
function readQuoted(quote, style, embed, unescaped) {
return function(stream, state) {
var escaped = false, ch;

if (state.context.type === 'read-quoted-paused') {
state.context = state.context.prev;
stream.eat("}");
}

while ((ch = stream.next()) != null) {
if (ch == quote && (unescaped || !escaped)) {
state.tokenize.pop();
break;
}
if (embed && ch == "#" && !escaped && stream.eat("{")) {
state.tokenize.push(tokenBaseUntilBrace(arguments.callee));
break;
if (embed && ch == "#" && !escaped) {
if (stream.eat("{")) {
if (quote == "}") {
state.context = {prev: state.context, type: 'read-quoted-paused'};
}
state.tokenize.push(tokenBaseUntilBrace());
break;
} else if (/[@\$]/.test(stream.peek())) {
state.tokenize.push(tokenBaseOnce());
break;
}
}
escaped = !escaped && ch == "\\";
}
Expand Down
45 changes: 44 additions & 1 deletion mode/smarty/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<body>
<h1>CodeMirror: Smarty mode</h1>

<h3>Default settings (Smarty 2, <b>{</b> and <b>}</b> delimiters)</h3>
<form><textarea id="code" name="code">
{extends file="parent.tpl"}
{include file="template.tpl"}
Expand Down Expand Up @@ -43,6 +44,7 @@ <h1>CodeMirror: Smarty mode</h1>

<br />

<h3>Smarty 2, custom delimiters</h3>
<form><textarea id="code2" name="code2">
{--extends file="parent.tpl"--}
{--include file="template.tpl"--}
Expand Down Expand Up @@ -76,7 +78,48 @@ <h1>CodeMirror: Smarty mode</h1>
});
</script>

<p>A plain text/Smarty mode which allows for custom delimiter tags (defaults to <b>{</b> and <b>}</b>).</p>
<br />

<h3>Smarty 3</h3>

<textarea id="code3" name="code3">
Nested tags {$foo={counter one=1 two={inception}}+3} are now valid in Smarty 3.

<script>
function test() {
console.log("Smarty 3 permits single curly braces followed by whitespace to NOT slip into Smarty mode.");
}
</script>

{assign var=foo value=[1,2,3]}
{assign var=foo value=['y'=>'yellow','b'=>'blue']}
{assign var=foo value=[1,[9,8],3]}

{$foo=$bar+2} {* a comment *}
{$foo.bar=1} {* another comment *}
{$foo = myfunct(($x+$y)*3)}
{$foo = strlen($bar)}
{$foo.bar.baz=1}, {$foo[]=1}

Smarty "dot" syntax (note: embedded {} are used to address ambiguities):

{$foo.a.b.c} => $foo['a']['b']['c']
{$foo.a.$b.c} => $foo['a'][$b]['c']
{$foo.a.{$b+4}.c} => $foo['a'][$b+4]['c']
{$foo.a.{$b.c}} => $foo['a'][$b['c']]

{$object->method1($x)->method2($y)}</textarea>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code3"), {
lineNumbers: true,
mode: "smarty",
smartyVersion: 3
});
</script>


<p>A plain text/Smarty version 2 or 3 mode, which allows for custom delimiter tags.</p>

<p><strong>MIME types defined:</strong> <code>text/x-smarty</code></p>
</body>
Expand Down
277 changes: 167 additions & 110 deletions mode/smarty/smarty.js
Original file line number Diff line number Diff line change
@@ -1,140 +1,197 @@
/**
* Smarty 2 and 3 mode.
*/
CodeMirror.defineMode("smarty", function(config) {
var keyFuncs = ["debug", "extends", "function", "include", "literal"];
"use strict";

// our default settings; check to see if they're overridden
var settings = {
rightDelimiter: '}',
leftDelimiter: '{',
smartyVersion: 2 // for backward compatibility
};
if (config.hasOwnProperty("leftDelimiter")) {
settings.leftDelimiter = config.leftDelimiter;
}
if (config.hasOwnProperty("rightDelimiter")) {
settings.rightDelimiter = config.rightDelimiter;
}
if (config.hasOwnProperty("smartyVersion") && config.smartyVersion === 3) {
settings.smartyVersion = 3;
}

var keyFunctions = ["debug", "extends", "function", "include", "literal"];
var last;
var regs = {
operatorChars: /[+\-*&%=<>!?]/,
validIdentifier: /[a-zA-Z0-9\_]/,
stringChar: /[\'\"]/
validIdentifier: /[a-zA-Z0-9_]/,
stringChar: /['"]/
};
var leftDelim = (typeof config.mode.leftDelimiter != 'undefined') ? config.mode.leftDelimiter : "{";
var rightDelim = (typeof config.mode.rightDelimiter != 'undefined') ? config.mode.rightDelimiter : "}";
function ret(style, lst) { last = lst; return style; }


function tokenizer(stream, state) {
function chain(parser) {
var helpers = {
cont: function(style, lastType) {
last = lastType;
return style;
},
chain: function(stream, state, parser) {
state.tokenize = parser;
return parser(stream, state);
}
};

if (stream.match(leftDelim, true)) {
if (stream.eat("*")) {
return chain(inBlock("comment", "*" + rightDelim));
}
else {
state.tokenize = inSmarty;
return "tag";
}
}
else {
// I'd like to do an eatWhile() here, but I can't get it to eat only up to the rightDelim string/char
stream.next();
return null;
}
}

function inSmarty(stream, state) {
if (stream.match(rightDelim, true)) {
state.tokenize = tokenizer;
return ret("tag", null);
}
// our various parsers
var parsers = {

var ch = stream.next();
if (ch == "$") {
stream.eatWhile(regs.validIdentifier);
return ret("variable-2", "variable");
}
else if (ch == ".") {
return ret("operator", "property");
}
else if (regs.stringChar.test(ch)) {
state.tokenize = inAttribute(ch);
return ret("string", "string");
}
else if (regs.operatorChars.test(ch)) {
stream.eatWhile(regs.operatorChars);
return ret("operator", "operator");
}
else if (ch == "[" || ch == "]") {
return ret("bracket", "bracket");
}
else if (/\d/.test(ch)) {
stream.eatWhile(/\d/);
return ret("number", "number");
}
else {
if (state.last == "variable") {
if (ch == "@") {
stream.eatWhile(regs.validIdentifier);
return ret("property", "property");
// the main tokenizer
tokenizer: function(stream, state) {
if (stream.match(settings.leftDelimiter, true)) {
if (stream.eat("*")) {
return helpers.chain(stream, state, parsers.inBlock("comment", "*" + settings.rightDelimiter));
} else {
// Smarty 3 allows { and } surrounded by whitespace to NOT slip into Smarty mode
state.depth++;
var isEol = stream.eol();
var isFollowedByWhitespace = /\s/.test(stream.peek());
if (settings.smartyVersion === 3 && settings.leftDelimiter === "{" && (isEol || isFollowedByWhitespace)) {
state.depth--;
return null;
} else {
state.tokenize = parsers.smarty;
last = "startTag";
return "tag";
}
}
else if (ch == "|") {
stream.eatWhile(regs.validIdentifier);
return ret("qualifier", "modifier");
}
}
else if (state.last == "whitespace") {
stream.eatWhile(regs.validIdentifier);
return ret("attribute", "modifier");
}
else if (state.last == "property") {
stream.eatWhile(regs.validIdentifier);
return ret("property", null);
}
else if (/\s/.test(ch)) {
last = "whitespace";
} else {
stream.next();
return null;
}
},

var str = "";
if (ch != "/") {
str += ch;
}
var c = "";
while ((c = stream.eat(regs.validIdentifier))) {
str += c;
}
var i, j;
for (i=0, j=keyFuncs.length; i<j; i++) {
if (keyFuncs[i] == str) {
return ret("keyword", "keyword");
// parsing Smarty content
smarty: function(stream, state) {
if (stream.match(settings.rightDelimiter, true)) {
if (settings.smartyVersion === 3) {
state.depth--;
if (state.depth <= 0) {
state.tokenize = parsers.tokenizer;
}
} else {
state.tokenize = parsers.tokenizer;
}
return helpers.cont("tag", null);
}
if (/\s/.test(ch)) {
return null;

if (stream.match(settings.leftDelimiter, true)) {
state.depth++;
return helpers.cont("tag", "startTag");
}
return ret("tag", "tag");
}
}

function inAttribute(quote) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.next() == quote) {
state.tokenize = inSmarty;
break;
var ch = stream.next();
if (ch == "$") {
stream.eatWhile(regs.validIdentifier);
return helpers.cont("variable-2", "variable");
} else if (ch == "|") {
return helpers.cont("operator", "pipe");
} else if (ch == ".") {
return helpers.cont("operator", "property");
} else if (regs.stringChar.test(ch)) {
state.tokenize = parsers.inAttribute(ch);
return helpers.cont("string", "string");
} else if (regs.operatorChars.test(ch)) {
stream.eatWhile(regs.operatorChars);
return helpers.cont("operator", "operator");
} else if (ch == "[" || ch == "]") {
return helpers.cont("bracket", "bracket");
} else if (ch == "(" || ch == ")") {
return helpers.cont("bracket", "operator");
} else if (/\d/.test(ch)) {
stream.eatWhile(/\d/);
return helpers.cont("number", "number");
} else {

if (state.last == "variable") {
if (ch == "@") {
stream.eatWhile(regs.validIdentifier);
return helpers.cont("property", "property");
} else if (ch == "|") {
stream.eatWhile(regs.validIdentifier);
return helpers.cont("qualifier", "modifier");
}
} else if (state.last == "pipe") {
stream.eatWhile(regs.validIdentifier);
return helpers.cont("qualifier", "modifier");
} else if (state.last == "whitespace") {
stream.eatWhile(regs.validIdentifier);
return helpers.cont("attribute", "modifier");
} if (state.last == "property") {
stream.eatWhile(regs.validIdentifier);
return helpers.cont("property", null);
} else if (/\s/.test(ch)) {
last = "whitespace";
return null;
}
}
return "string";
};
}

function inBlock(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = tokenizer;
break;
var str = "";
if (ch != "/") {
str += ch;
}
stream.next();
var c = null;
while (c = stream.eat(regs.validIdentifier)) {
str += c;
}
for (var i=0, j=keyFunctions.length; i<j; i++) {
if (keyFunctions[i] == str) {
return helpers.cont("keyword", "keyword");
}
}
if (/\s/.test(ch)) {
return null;
}
return helpers.cont("tag", "tag");
}
return style;
};
}
},

inAttribute: function(quote) {
return function(stream, state) {
var prevChar = null;
var currChar = null;
while (!stream.eol()) {
currChar = stream.peek();
if (stream.next() == quote && prevChar !== '\\') {
state.tokenize = parsers.smarty;
break;
}
prevChar = currChar;
}
return "string";
};
},

inBlock: function(style, terminator) {
return function(stream, state) {
while (!stream.eol()) {
if (stream.match(terminator)) {
state.tokenize = parsers.tokenizer;
break;
}
stream.next();
}
return style;
};
}
};


// the public API for CodeMirror
return {
startState: function() {
return { tokenize: tokenizer, mode: "smarty", last: null };
return {
tokenize: parsers.tokenizer,
mode: "smarty",
last: null,
depth: 0
};
},
token: function(stream, state) {
var style = state.tokenize(stream, state);
Expand Down
5 changes: 3 additions & 2 deletions mode/sql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<title>SQL Mode for CodeMirror</title>
<link rel="stylesheet" href="../../lib/codemirror.css" />
<script src="../../lib/codemirror.js"></script>
<script src="../../lib/codemirror.js"></script>
<script src="sql.js"></script>
<style>
.CodeMirror {
Expand Down Expand Up @@ -43,7 +42,8 @@ <h1>SQL Mode for CodeMirror</h1>
1.1 AS `float_val`, .14 AS `another_float`, 0.09e3 AS `int_with_esp`,
0xFA5 AS `hex`, x'fa5' AS `hex2`, 0b101 AS `bin`, b'101' AS `bin2`,
DATE '1994-01-01' AS `sql_date`, { T "1994-01-01" } AS `odbc_date`,
'myString', UNKNOWN
'my string', _utf8'your string', N'her string',
TRUE, FALSE, UNKNOWN
FROM DUAL
-- space needed after '--'
# 1 line comment
Expand All @@ -56,6 +56,7 @@ <h1>SQL Mode for CodeMirror</h1>
<code><a href="?mime=text/x-sql">text/x-sql</a></code>,
<code><a href="?mime=text/x-mysql">text/x-mysql</a></code>,
<code><a href="?mime=text/x-mariadb">text/x-mariadb</a></code>,
<code><a href="?mime=text/x-cassandra">text/x-cassandra</a></code>,
<code><a href="?mime=text/x-plsql">text/x-plsql</a></code>.
</p>
<p>
Expand Down
112 changes: 97 additions & 15 deletions mode/sql/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,43 +19,64 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
if (result !== false) return result;
}

if ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
|| (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/)) {
if (support.hexNumber == true &&
((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
|| (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
// hex
// ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
return "number";
} else if (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
|| (ch == "0" && stream.match(/^b[01]+/))) {
} else if (support.binaryNumber == true &&
(((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
|| (ch == "0" && stream.match(/^b[01]+/)))) {
// bitstring
// ref: http://dev.mysql.com/doc/refman/5.5/en/bit-field-literals.html
return "number";
} else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
// numbers
stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
// ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
support.decimallessFloat == true && stream.eat('.');
return "number";
} else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
// placeholders
return "variable-3";
} else if (ch == '"' || ch == "'") {
} else if (ch == "'" || (ch == '"' && support.doubleQuote)) {
// strings
// ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
state.tokenize = tokenLiteral(ch);
return state.tokenize(stream, state);
} else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
|| (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
&& (stream.peek() == "'" || stream.peek() == '"'))) {
// charset casting: _utf8'str', N'str', n'str'
// ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
return "keyword";
} else if (/^[\(\),\;\[\]]/.test(ch)) {
// no highlightning
return null;
} else if (ch == "#" || (ch == "-" && stream.eat("-") && stream.eat(" "))) {
} else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
// 1-line comment
stream.skipToEnd();
return "comment";
} else if ((support.commentHash && ch == "#")
|| (ch == "-" && stream.eat("-") && (!support.commentSpaceRequired || stream.eat(" ")))) {
// 1-line comments
// ref: https://kb.askmonty.org/en/comment-syntax/
stream.skipToEnd();
return "comment";
} else if (ch == "/" && stream.eat("*")) {
// multi-line comments
// ref: https://kb.askmonty.org/en/comment-syntax/
state.tokenize = tokenComment;
return state.tokenize(stream, state);
} else if (ch == ".") {
// .1 for 0.1
if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e\d*)?|\d*e\d+)/i)) {
if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
return "number";
}
// .table_name (ODBC)
if (stream.match(/^[a-zA-Z_]+/) && support.ODBCdotTable == true) {
// // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
return "variable-2";
}
} else if (operatorChars.test(ch)) {
Expand All @@ -65,11 +86,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
} else if (ch == '{' &&
(stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
// dates (weird ODBC syntax)
// ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
return "number";
} else {
stream.eatWhile(/^[_\w\d]/);
var word = stream.current().toLowerCase();
// dates (standard SQL syntax)
// ref: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-literals.html
if (dateSQL.hasOwnProperty(word) && (stream.match(/^( )+'[^']*'/) || stream.match(/^( )+"[^"]*"/)))
return "number";
if (atoms.hasOwnProperty(word)) return "atom";
Expand Down Expand Up @@ -166,6 +189,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {

// `identifier`
function hookIdentifier(stream) {
// MySQL/MariaDB identifiers
// ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
var ch;
while ((ch = stream.next()) != null) {
if (ch == "`" && !stream.eat("`")) return "variable-2";
Expand All @@ -176,7 +201,9 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
// variable token
function hookVar(stream) {
// variables
// @@ and prefix
// @@prefix.varName @varName
// varName can be quoted with ` or ' or "
// ref: http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
if (stream.eat("@")) {
stream.match(/^session\./);
stream.match(/^local\./);
Expand All @@ -200,26 +227,35 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {

// short client keyword token
function hookClient(stream) {
// \N means NULL
// ref: http://dev.mysql.com/doc/refman/5.5/en/null-values.html
if (stream.eat("N")) {
return "atom";
}
// \g, etc
return stream.match(/^[a-zA-Z]\b/) ? "variable-2" : null;
// ref: http://dev.mysql.com/doc/refman/5.5/en/mysql-commands.html
return stream.match(/^[a-zA-Z.#!?]/) ? "variable-2" : null;
}

// these keywords are used by all SQL dialects (however, a mode can still overwrite it)
var sqlKeywords = "alter and as asc between by count create delete desc distinct drop from having in insert into is join like not on or order select set table union update values where ";

// turn a space-separated list into an array
function set(str) {
var obj = {}, words = str.split(" ");
for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
return obj;
}

// A generic SQL Mode. It's not a standard, it just try to support what is generally supported
CodeMirror.defineMIME("text/x-sql", {
name: "sql",
keywords: set(sqlKeywords + "begin"),
builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"),
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=]/,
dateSQL: set("date time timestamp"),
support: set("ODBCdotTable")
support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
});

CodeMirror.defineMIME("text/x-mysql", {
Expand All @@ -230,7 +266,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=&|^]/,
dateSQL: set("date time timestamp"),
support: set("ODBCdotTable zerolessFloat"),
support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
hooks: {
"@": hookVar,
"`": hookIdentifier,
Expand All @@ -246,14 +282,28 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=&|^]/,
dateSQL: set("date time timestamp"),
support: set("ODBCdotTable zerolessFloat"),
support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber doubleQuote nCharCast charsetCast commentHash commentSpaceRequired"),
hooks: {
"@": hookVar,
"`": hookIdentifier,
"\\": hookClient
}
});

// the query language used by Apache Cassandra is called CQL, but this mime type
// is called Cassandra to avoid confusion with Contextual Query Language
CodeMirror.defineMIME("text/x-cassandra", {
name: "sql",
client: { },
keywords: set("use select from using consistency where limit first reversed first and in insert into values using consistency ttl update set delete truncate begin batch apply create keyspace with columnfamily primary key index on drop alter type add any one quorum all local_quorum each_quorum"),
builtin: set("ascii bigint blob boolean counter decimal double float int text timestamp uuid varchar varint"),
atoms: set("false true"),
operatorChars: /^[<>=]/,
dateSQL: { },
support: set("commentSlashSlash decimallessFloat"),
hooks: { }
});

// this is based on Peter Raganitsch's 'plsql' mode
CodeMirror.defineMIME("text/x-plsql", {
name: "sql",
Expand All @@ -262,6 +312,38 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
functions: set("abs acos add_months ascii asin atan atan2 average bfilename ceil chartorowid chr concat convert cos cosh count decode deref dual dump dup_val_on_index empty error exp false floor found glb greatest hextoraw initcap instr instrb isopen last_day least lenght lenghtb ln lower lpad ltrim lub make_ref max min mod months_between new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null nvl others power rawtohex reftohex round rowcount rowidtochar rpad rtrim sign sin sinh soundex sqlcode sqlerrm sqrt stddev substr substrb sum sysdate tan tanh to_char to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid upper user userenv variance vsize"),
builtin: set("bfile blob character clob dec float int integer mlslabel natural naturaln nchar nclob number numeric nvarchar2 real rowtype signtype smallint string varchar varchar2"),
operatorChars: /^[*+\-%<>!=~]/,
dateSQL: set("date time timestamp")
dateSQL: set("date time timestamp"),
support: set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
});
}());

/*
How Properties of Mime Types are used by SQL Mode
=================================================
keywords:
A list of keywords you want to be highlighted.
functions:
A list of function names you want to be highlighted.
builtin:
A list of builtin types you want to be highlighted (if you want types to be of class "builtin" instead of "keyword").
operatorChars:
All characters that must be handled as operators.
client:
Commands parsed and executed by the client (not the server).
support:
A list of supported syntaxes which are not common, but are supported by more than 1 DBMS.
* ODBCdotTable: .tableName
* zerolessFloat: .1
* doubleQuote
* nCharCast: N'string'
* charsetCast: _utf8'string'
* commentHash: use # char for comments
* commentSlashSlash: use // for comments
* commentSpaceRequired: require a space after -- for comments
atoms:
Keywords that must be highlighted as atoms,. Some DBMS's support more atoms than others:
UNKNOWN, INFINITY, UNDERFLOW, NaN...
dateSQL:
Used for date/time SQL standard syntax, because not all DBMS's support same temporal types.
*/
Loading