diff --git a/addon/comment/comment.js b/addon/comment/comment.js
index 4f590f2870..3c76744654 100644
--- a/addon/comment/comment.js
+++ b/addon/comment/comment.js
@@ -30,7 +30,7 @@
if (firstLine == null) return;
var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
var pad = options.padding == null ? " " : options.padding;
- var blankLines = options.commentBlankLines;
+ var blankLines = options.commentBlankLines || from.line == to.line;
self.operation(function() {
if (options.indent) {
@@ -90,14 +90,14 @@
// Try finding line comments
var lineString = options.lineComment || mode.lineComment, lines = [];
- var pad = options.padding == null ? " " : options.padding;
- lineComment: for(;;) {
- if (!lineString) break;
+ var pad = options.padding == null ? " " : options.padding, didSomething;
+ lineComment: {
+ if (!lineString) break lineComment;
for (var i = start; i <= end; ++i) {
var line = self.getLine(i);
var found = line.indexOf(lineString);
if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment;
- if (i != start && nonWS.test(line.slice(0, found))) break lineComment;
+ if (i != start && found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
lines.push(line);
}
self.operation(function() {
@@ -106,10 +106,11 @@
var pos = line.indexOf(lineString), endPos = pos + lineString.length;
if (pos < 0) continue;
if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
+ didSomething = true;
self.replaceRange("", Pos(i, pos), Pos(i, endPos));
}
});
- return true;
+ if (didSomething) return true;
}
// Try block comments
diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js
index 2abc8c5fe6..6fbf38ae67 100644
--- a/addon/edit/closebrackets.js
+++ b/addon/edit/closebrackets.js
@@ -1,23 +1,36 @@
(function() {
var DEFAULT_BRACKETS = "()[]{}''\"\"";
+ var DEFAULT_EXPLODE_ON_ENTER = "[]{}";
var SPACE_CHAR_REGEX = /\s/;
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
- var wasOn = old && old != CodeMirror.Init;
- if (val && !wasOn)
- cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS));
- else if (!val && wasOn)
+ if (old != CodeMirror.Init && old)
cm.removeKeyMap("autoCloseBrackets");
+ if (!val) return;
+ var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER;
+ if (typeof val == "string") pairs = val;
+ else if (typeof val == "object") {
+ if (val.pairs != null) pairs = val.pairs;
+ if (val.explode != null) explode = val.explode;
+ }
+ var map = buildKeymap(pairs);
+ if (explode) map.Enter = buildExplodeHandler(explode);
+ cm.addKeyMap(map);
});
+ function charsAround(cm, pos) {
+ var str = cm.getRange(CodeMirror.Pos(pos.line, pos.ch - 1),
+ CodeMirror.Pos(pos.line, pos.ch + 1));
+ return str.length == 2 ? str : null;
+ }
+
function buildKeymap(pairs) {
var map = {
name : "autoCloseBrackets",
Backspace: function(cm) {
if (cm.somethingSelected()) return CodeMirror.Pass;
- var cur = cm.getCursor(), line = cm.getLine(cur.line);
- if (cur.ch && cur.ch < line.length &&
- pairs.indexOf(line.slice(cur.ch - 1, cur.ch + 1)) % 2 == 0)
+ var cur = cm.getCursor(), around = charsAround(cm, cur);
+ if (around && pairs.indexOf(around) % 2 == 0)
cm.replaceRange("", CodeMirror.Pos(cur.line, cur.ch - 1), CodeMirror.Pos(cur.line, cur.ch + 1));
else
return CodeMirror.Pass;
@@ -51,4 +64,17 @@
})(pairs.charAt(i), pairs.charAt(i + 1));
return map;
}
+
+ function buildExplodeHandler(pairs) {
+ return function(cm) {
+ var cur = cm.getCursor(), around = charsAround(cm, cur);
+ if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ cm.operation(function() {
+ var newPos = CodeMirror.Pos(cur.line + 1, 0);
+ cm.replaceSelection("\n\n", {anchor: newPos, head: newPos}, "+input");
+ cm.indentLine(cur.line + 1, null, true);
+ cm.indentLine(cur.line + 2, null, true);
+ });
+ };
+ }
})();
diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js
index e4ff914c6b..131fe831fd 100644
--- a/addon/edit/matchbrackets.js
+++ b/addon/edit/matchbrackets.js
@@ -5,25 +5,26 @@
var Pos = CodeMirror.Pos;
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
- function findMatchingBracket(cm) {
- var maxScanLen = cm.state._matchBrackets.maxScanLineLength || 10000;
+ function findMatchingBracket(cm, where, strict) {
+ var state = cm.state.matchBrackets;
+ var maxScanLen = (state && state.maxScanLineLength) || 10000;
- var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+ var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
if (!match) return null;
var forward = match.charAt(1) == ">", d = forward ? 1 : -1;
- var style = cm.getTokenAt(Pos(cur.line, pos + 1)).type;
+ if (strict && forward != (pos == cur.ch)) return null;
+ var style = cm.getTokenTypeAt(Pos(cur.line, pos + 1));
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
function scan(line, lineNo, start) {
if (!line.text) return;
var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
if (line.text.length > maxScanLen) return null;
- var checkTokenStyles = line.text.length < 1000;
if (start != null) pos = start + d;
for (; pos != end; pos += d) {
var ch = line.text.charAt(pos);
- if (re.test(ch) && (!checkTokenStyles || cm.getTokenAt(Pos(lineNo, pos + 1)).type == style)) {
+ if (re.test(ch) && cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style) {
var match = matching[ch];
if (match.charAt(1) == ">" == forward) stack.push(ch);
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
@@ -36,12 +37,13 @@
else found = scan(cm.getLineHandle(i), i);
if (found) break;
}
- return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos), match: found && found.match};
+ return {from: Pos(cur.line, pos), to: found && Pos(i, found.pos),
+ match: found && found.match, forward: forward};
}
function matchBrackets(cm, autoclear) {
// Disable brace matching in long lines, since it'll cause hugely slow updates
- var maxHighlightLen = cm.state._matchBrackets.maxHighlightLineLength || 1000;
+ var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
var found = findMatchingBracket(cm);
if (!found || cm.getLine(found.from.line).length > maxHighlightLen ||
found.to && cm.getLine(found.to.line).length > maxHighlightLen)
@@ -72,11 +74,13 @@
if (old && old != CodeMirror.Init)
cm.off("cursorActivity", doMatchBrackets);
if (val) {
- cm.state._matchBrackets = typeof val == "object" ? val : {};
+ cm.state.matchBrackets = typeof val == "object" ? val : {};
cm.on("cursorActivity", doMatchBrackets);
}
});
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
- CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
+ CodeMirror.defineExtension("findMatchingBracket", function(pos, strict){
+ return findMatchingBracket(this, pos, strict);
+ });
})();
diff --git a/addon/edit/trailingspace.js b/addon/edit/trailingspace.js
new file mode 100644
index 0000000000..f6bb02645d
--- /dev/null
+++ b/addon/edit/trailingspace.js
@@ -0,0 +1,15 @@
+CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
+ if (prev == CodeMirror.Init) prev = false;
+ if (prev && !val)
+ cm.removeOverlay("trailingspace");
+ else if (!prev && val)
+ cm.addOverlay({
+ token: function(stream) {
+ for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
+ if (i > stream.pos) { stream.pos = i; return null; }
+ stream.pos = l;
+ return "trailingspace";
+ },
+ name: "trailingspace"
+ });
+});
diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js
index efdffb877c..e35115b8b9 100644
--- a/addon/fold/brace-fold.js
+++ b/addon/fold/brace-fold.js
@@ -1,23 +1,33 @@
CodeMirror.braceRangeFinder = function(cm, start) {
var line = start.line, lineText = cm.getLine(line);
- var at = lineText.length, startChar, tokenType;
- for (; at > 0;) {
- var found = lineText.lastIndexOf("{", at);
- var startToken = '{', endToken = '}';
- if (found < start.ch) {
- found = lineText.lastIndexOf("[", at);
- if (found < start.ch) break;
- startToken = '['; endToken = ']';
+ var startCh, tokenType;
+
+ function findOpening(openCh) {
+ for (var at = start.ch, pass = 0;;) {
+ var found = lineText.lastIndexOf(openCh, at - 1);
+ if (found == -1) {
+ if (pass == 1) break;
+ pass = 1;
+ at = lineText.length;
+ continue;
+ }
+ if (pass == 1 && found < start.ch) break;
+ tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
+ if (!/^(comment|string)/.test(tokenType)) return found + 1;
+ at = found - 1;
}
+ }
- tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
- if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
- at = found - 1;
+ var startToken = "{", endToken = "}", startCh = findOpening("{");
+ if (startCh == null) {
+ startToken = "[", endToken = "]";
+ startCh = findOpening("[");
}
- if (startChar == null || lineText.lastIndexOf(startToken) > startChar) return;
- var count = 1, lastLine = cm.lineCount(), end, endCh;
- outer: for (var i = line + 1; i < lastLine; ++i) {
- var text = cm.getLine(i), pos = 0;
+
+ if (startCh == null) return;
+ var count = 1, lastLine = cm.lastLine(), end, endCh;
+ outer: for (var i = line; i <= lastLine; ++i) {
+ var text = cm.getLine(i), pos = i == line ? startCh : 0;
for (;;) {
var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
if (nextOpen < 0) nextOpen = text.length;
@@ -31,7 +41,50 @@ CodeMirror.braceRangeFinder = function(cm, start) {
++pos;
}
}
- if (end == null || end == line + 1) return;
- return {from: CodeMirror.Pos(line, startChar + 1),
+ if (end == null || line == end && endCh == startCh) return;
+ return {from: CodeMirror.Pos(line, startCh),
to: CodeMirror.Pos(end, endCh)};
};
+
+CodeMirror.importRangeFinder = function(cm, start) {
+ function hasImport(line) {
+ if (line < cm.firstLine() || line > cm.lastLine()) return null;
+ var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
+ if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
+ if (start.type != "keyword" || start.string != "import") return null;
+ // Now find closing semicolon, return its position
+ for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
+ var text = cm.getLine(i), semi = text.indexOf(";");
+ if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
+ }
+ }
+
+ var start = start.line, has = hasImport(start), prev;
+ if (!has || hasImport(start - 1) || ((prev = hasImport(start - 2)) && prev.end.line == start - 1))
+ return null;
+ for (var end = has.end;;) {
+ var next = hasImport(end.line + 1);
+ if (next == null) break;
+ end = next.end;
+ }
+ return {from: cm.clipPos(CodeMirror.Pos(start, has.startCh + 1)), to: end};
+};
+
+CodeMirror.includeRangeFinder = function(cm, start) {
+ function hasInclude(line) {
+ if (line < cm.firstLine() || line > cm.lastLine()) return null;
+ var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
+ if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
+ if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
+ }
+
+ var start = start.line, has = hasInclude(start);
+ if (has == null || hasInclude(start - 1) != null) return null;
+ for (var end = start;;) {
+ var next = hasInclude(end + 1);
+ if (next == null) break;
+ ++end;
+ }
+ return {from: CodeMirror.Pos(start, has + 1),
+ to: cm.clipPos(CodeMirror.Pos(end))};
+};
diff --git a/addon/fold/foldcode.js b/addon/fold/foldcode.js
index b8b4b0da9e..2743d3e2c9 100644
--- a/addon/fold/foldcode.js
+++ b/addon/fold/foldcode.js
@@ -1,32 +1,68 @@
-CodeMirror.newFoldFunction = function(rangeFinder, widget) {
- if (widget == null) widget = "\u2194";
- if (typeof widget == "string") {
- var text = document.createTextNode(widget);
- widget = document.createElement("span");
- widget.appendChild(text);
- widget.className = "CodeMirror-foldmarker";
- }
+(function() {
+ "use strict";
- return function(cm, pos) {
+ function doFold(cm, pos, options) {
+ var finder = options.call ? options : (options && options.rangeFinder);
+ if (!finder) return;
if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
- var range = rangeFinder(cm, pos);
- if (!range) return;
-
- var present = cm.findMarksAt(range.from), cleared = 0;
- for (var i = 0; i < present.length; ++i) {
- if (present[i].__isFold) {
- ++cleared;
- present[i].clear();
+ var minSize = options && options.minFoldSize || 0;
+
+ function getRange(allowFolded) {
+ var range = finder(cm, pos);
+ if (!range || range.to.line - range.from.line < minSize) return null;
+ var marks = cm.findMarksAt(range.from);
+ for (var i = 0; i < marks.length; ++i) {
+ if (marks[i].__isFold) {
+ if (!allowFolded) return null;
+ range.cleared = true;
+ marks[i].clear();
+ }
}
+ return range;
+ }
+
+ var range = getRange(true);
+ if (options && options.scanUp) while (!range && pos.line > cm.firstLine()) {
+ pos = CodeMirror.Pos(pos.line - 1, 0);
+ range = getRange(false);
}
- if (cleared) return;
+ if (!range || range.cleared) return;
- var myWidget = widget.cloneNode(true);
+ var myWidget = makeWidget(options);
CodeMirror.on(myWidget, "mousedown", function() {myRange.clear();});
var myRange = cm.markText(range.from, range.to, {
replacedWith: myWidget,
clearOnEnter: true,
__isFold: true
});
+ }
+
+ function makeWidget(options) {
+ var widget = (options && options.widget) || "\u2194";
+ if (typeof widget == "string") {
+ var text = document.createTextNode(widget);
+ widget = document.createElement("span");
+ widget.appendChild(text);
+ widget.className = "CodeMirror-foldmarker";
+ }
+ return widget;
+ }
+
+ // Clumsy backwards-compatible interface
+ CodeMirror.newFoldFunction = function(rangeFinder, widget) {
+ return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
+ };
+
+ // New-style interface
+ CodeMirror.defineExtension("foldCode", function(pos, options) { doFold(this, pos, options); });
+
+ CodeMirror.combineRangeFinders = function() {
+ var funcs = Array.prototype.slice.call(arguments, 0);
+ return function(cm, start) {
+ for (var i = 0; i < funcs.length; ++i) {
+ var found = funcs[i](cm, start);
+ if (found) return found;
+ }
+ };
};
-};
+})();
diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js
index 79c524d485..b764bc0190 100644
--- a/addon/fold/xml-fold.js
+++ b/addon/fold/xml-fold.js
@@ -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};
+ }
};
})();
diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js
index 8b5dc6f002..23238df054 100755
--- a/addon/hint/html-hint.js
+++ b/addon/hint/html-hint.js
@@ -1,582 +1,335 @@
(function () {
- function htmlHint(editor, htmlStructure, getToken) {
- var cur = editor.getCursor();
- var token = getToken(editor, cur);
- var keywords = [];
- var i = 0;
- var j = 0;
- var k = 0;
- var from = {line: cur.line, ch: cur.ch};
- var to = {line: cur.line, ch: cur.ch};
- var flagClean = true;
+ var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
+ var targets = ["_blank", "_self", "_top", "_parent"];
+ var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
+ var methods = ["get", "post", "put", "delete"];
+ var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
+ var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
+ "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
+ "orientation:landscape", "device-height: [X]", "device-width: [X]"];
+ var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
- var text = editor.getRange({line: 0, ch: 0}, cur);
-
- var open = text.lastIndexOf('<');
- var close = text.lastIndexOf('>');
- var tokenString = token.string.replace("<","");
-
- if(open > close) {
- var last = editor.getRange({line: cur.line, ch: cur.ch - 1}, cur);
- if(last == "<") {
- for(i = 0; i < htmlStructure.length; i++) {
- keywords.push(htmlStructure[i].tag);
- }
- from.ch = token.start + 1;
- } else {
- var counter = 0;
- var found = function(token, type, position) {
- counter++;
- if(counter > 50) return;
- if(token.type == type) {
- return token;
- } else {
- position.ch = token.start;
- var newToken = editor.getTokenAt(position);
- return found(newToken, type, position);
- }
- };
-
- var nodeToken = found(token, "tag", {line: cur.line, ch: cur.ch});
- var node = nodeToken.string.substring(1);
-
- if(token.type === null && token.string.trim() === "") {
- for(i = 0; i < htmlStructure.length; i++) {
- if(htmlStructure[i].tag == node) {
- for(j = 0; j < htmlStructure[i].attr.length; j++) {
- keywords.push(htmlStructure[i].attr[j].key + "=\"\" ");
- }
-
- for(k = 0; k < globalAttributes.length; k++) {
- keywords.push(globalAttributes[k].key + "=\"\" ");
- }
- }
- }
- } else if(token.type == "string") {
- tokenString = tokenString.substring(1, tokenString.length - 1);
- var attributeToken = found(token, "attribute", {line: cur.line, ch: cur.ch});
- var attribute = attributeToken.string;
-
- for(i = 0; i < htmlStructure.length; i++) {
- if(htmlStructure[i].tag == node) {
- for(j = 0; j < htmlStructure[i].attr.length; j++) {
- if(htmlStructure[i].attr[j].key == attribute) {
- for(k = 0; k < htmlStructure[i].attr[j].values.length; k++) {
- keywords.push(htmlStructure[i].attr[j].values[k]);
- }
- }
- }
-
- for(j = 0; j < globalAttributes.length; j++) {
- if(globalAttributes[j].key == attribute) {
- for(k = 0; k < globalAttributes[j].values.length; k++) {
- keywords.push(globalAttributes[j].values[k]);
- }
- }
- }
- }
- }
- from.ch = token.start + 1;
- } else if(token.type == "attribute") {
- for(i = 0; i < htmlStructure.length; i++) {
- if(htmlStructure[i].tag == node) {
- for(j = 0; j < htmlStructure[i].attr.length; j++) {
- keywords.push(htmlStructure[i].attr[j].key + "=\"\" ");
- }
-
- for(k = 0; k < globalAttributes.length; k++) {
- keywords.push(globalAttributes[k].key + "=\"\" ");
- }
- }
- }
- from.ch = token.start;
- } else if(token.type == "tag") {
- for(i = 0; i < htmlStructure.length; i++) {
- keywords.push(htmlStructure[i].tag);
- }
-
- from.ch = token.start + 1;
- }
+ var data = {
+ a: {
+ attrs: {
+ href: null, ping: null, type: null,
+ media: media,
+ target: targets,
+ hreflang: langs
}
- } else {
- for(i = 0; i < htmlStructure.length; i++) {
- keywords.push("<" + htmlStructure[i].tag);
+ },
+ abbr: s,
+ acronym: s,
+ address: s,
+ applet: s,
+ area: {
+ attrs: {
+ alt: null, coords: null, href: null, target: null, ping: null,
+ media: media, hreflang: langs, type: null,
+ shape: ["default", "rect", "circle", "poly"]
}
-
- tokenString = ("<" + tokenString).trim();
- from.ch = token.start;
- }
-
- if(flagClean === true && tokenString.trim() === "") {
- flagClean = false;
- }
-
- if(flagClean) {
- keywords = cleanResults(tokenString, keywords);
- }
-
- return {list: keywords, from: from, to: to};
- }
-
-
- var cleanResults = function(text, keywords) {
- var results = [];
- var i = 0;
-
- for(i = 0; i < keywords.length; i++) {
- if(keywords[i].substring(0, text.length) == text) {
- results.push(keywords[i]);
+ },
+ article: s,
+ aside: s,
+ audio: {
+ attrs: {
+ src: null, mediagroup: null,
+ crossorigin: ["anonymous", "use-credentials"],
+ preload: ["none", "metadata", "auto"],
+ autoplay: ["", "autoplay"],
+ loop: ["", "loop"],
+ controls: ["", "controls"]
}
- }
-
- return results;
+ },
+ b: s,
+ base: { attrs: { href: null, target: targets } },
+ basefont: s,
+ bdi: s,
+ bdo: s,
+ big: s,
+ blockquote: { attrs: { cite: null } },
+ body: s,
+ br: s,
+ button: {
+ attrs: {
+ form: null, formaction: null, name: null, value: null,
+ autofocus: ["", "autofocus"],
+ disabled: ["", "autofocus"],
+ formenctype: encs,
+ formmethod: methods,
+ formnovalidate: ["", "novalidate"],
+ formtarget: targets,
+ type: ["submit", "reset", "button"]
+ }
+ },
+ canvas: { attrs: { width: null, height: null } },
+ caption: s,
+ center: s,
+ cite: s,
+ code: s,
+ col: { attrs: { span: null } },
+ colgroup: { attrs: { span: null } },
+ command: {
+ attrs: {
+ type: ["command", "checkbox", "radio"],
+ label: null, icon: null, radiogroup: null, command: null, title: null,
+ disabled: ["", "disabled"],
+ checked: ["", "checked"]
+ }
+ },
+ data: { attrs: { value: null } },
+ datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
+ datalist: { attrs: { data: null } },
+ dd: s,
+ del: { attrs: { cite: null, datetime: null } },
+ details: { attrs: { open: ["", "open"] } },
+ dfn: s,
+ dir: s,
+ div: s,
+ dl: s,
+ dt: s,
+ em: s,
+ embed: { attrs: { src: null, type: null, width: null, height: null } },
+ eventsource: { attrs: { src: null } },
+ fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
+ figcaption: s,
+ figure: s,
+ font: s,
+ footer: s,
+ form: {
+ attrs: {
+ action: null, name: null,
+ "accept-charset": charsets,
+ autocomplete: ["on", "off"],
+ enctype: encs,
+ method: methods,
+ novalidate: ["", "novalidate"],
+ target: targets
+ }
+ },
+ frame: s,
+ frameset: s,
+ h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
+ head: {
+ attrs: {},
+ children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
+ },
+ header: s,
+ hgroup: s,
+ hr: s,
+ html: {
+ attrs: { manifest: null },
+ children: ["head", "body"]
+ },
+ i: s,
+ iframe: {
+ attrs: {
+ src: null, srcdoc: null, name: null, width: null, height: null,
+ sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
+ seamless: ["", "seamless"]
+ }
+ },
+ img: {
+ attrs: {
+ alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
+ crossorigin: ["anonymous", "use-credentials"]
+ }
+ },
+ input: {
+ attrs: {
+ alt: null, dirname: null, form: null, formaction: null,
+ height: null, list: null, max: null, maxlength: null, min: null,
+ name: null, pattern: null, placeholder: null, size: null, src: null,
+ step: null, value: null, width: null,
+ accept: ["audio/*", "video/*", "image/*"],
+ autocomplete: ["on", "off"],
+ autofocus: ["", "autofocus"],
+ checked: ["", "checked"],
+ disabled: ["", "disabled"],
+ formenctype: encs,
+ formmethod: methods,
+ formnovalidate: ["", "novalidate"],
+ formtarget: targets,
+ multiple: ["", "multiple"],
+ readonly: ["", "readonly"],
+ required: ["", "required"],
+ type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
+ "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
+ "file", "submit", "image", "reset", "button"]
+ }
+ },
+ ins: { attrs: { cite: null, datetime: null } },
+ kbd: s,
+ keygen: {
+ attrs: {
+ challenge: null, form: null, name: null,
+ autofocus: ["", "autofocus"],
+ disabled: ["", "disabled"],
+ keytype: ["RSA"]
+ }
+ },
+ label: { attrs: { "for": null, form: null } },
+ legend: s,
+ li: { attrs: { value: null } },
+ link: {
+ attrs: {
+ href: null, type: null,
+ hreflang: langs,
+ media: media,
+ sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
+ }
+ },
+ map: { attrs: { name: null } },
+ mark: s,
+ menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
+ meta: {
+ attrs: {
+ content: null,
+ charset: charsets,
+ name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
+ "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
+ }
+ },
+ meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
+ nav: s,
+ noframes: s,
+ noscript: s,
+ object: {
+ attrs: {
+ data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
+ typemustmatch: ["", "typemustmatch"]
+ }
+ },
+ ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
+ optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
+ option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
+ output: { attrs: { "for": null, form: null, name: null } },
+ p: s,
+ param: { attrs: { name: null, value: null } },
+ pre: s,
+ progress: { attrs: { value: null, max: null } },
+ q: { attrs: { cite: null } },
+ rp: s,
+ rt: s,
+ ruby: s,
+ s: s,
+ samp: s,
+ script: {
+ attrs: {
+ type: ["text/javascript"],
+ src: null,
+ async: ["", "async"],
+ defer: ["", "defer"],
+ charset: charsets
+ }
+ },
+ section: s,
+ select: {
+ attrs: {
+ form: null, name: null, size: null,
+ autofocus: ["", "autofocus"],
+ disabled: ["", "disabled"],
+ multiple: ["", "multiple"]
+ }
+ },
+ small: s,
+ source: { attrs: { src: null, type: null, media: null } },
+ span: s,
+ strike: s,
+ strong: s,
+ style: {
+ attrs: {
+ type: ["text/css"],
+ media: media,
+ scoped: null
+ }
+ },
+ sub: s,
+ summary: s,
+ sup: s,
+ table: s,
+ tbody: s,
+ td: { attrs: { colspan: null, rowspan: null, headers: null } },
+ textarea: {
+ attrs: {
+ dirname: null, form: null, maxlength: null, name: null, placeholder: null,
+ rows: null, cols: null,
+ autofocus: ["", "autofocus"],
+ disabled: ["", "disabled"],
+ readonly: ["", "readonly"],
+ required: ["", "required"],
+ wrap: ["soft", "hard"]
+ }
+ },
+ tfoot: s,
+ th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
+ thead: s,
+ time: { attrs: { datetime: null } },
+ title: s,
+ tr: s,
+ track: {
+ attrs: {
+ src: null, label: null, "default": null,
+ kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
+ srclang: langs
+ }
+ },
+ tt: s,
+ u: s,
+ ul: s,
+ "var": s,
+ video: {
+ attrs: {
+ src: null, poster: null, width: null, height: null,
+ crossorigin: ["anonymous", "use-credentials"],
+ preload: ["auto", "metadata", "none"],
+ autoplay: ["", "autoplay"],
+ mediagroup: ["movie"],
+ muted: ["", "muted"],
+ controls: ["", "controls"]
+ }
+ },
+ wbr: s
};
- var htmlStructure = [
- {tag: '!DOCTYPE', attr: []},
- {tag: 'a', attr: [
- {key: 'href', values: ["#"]},
- {key: 'target', values: ["_blank","_self","_top","_parent"]},
- {key: 'ping', values: [""]},
- {key: 'media', values: ["#"]},
- {key: 'hreflang', values: ["en","es"]},
- {key: 'type', values: []}
- ]},
- {tag: 'abbr', attr: []},
- {tag: 'acronym', attr: []},
- {tag: 'address', attr: []},
- {tag: 'applet', attr: []},
- {tag: 'area', attr: [
- {key: 'alt', values: [""]},
- {key: 'coords', values: ["rect: left, top, right, bottom","circle: center-x, center-y, radius","poly: x1, y1, x2, y2, ..."]},
- {key: 'shape', values: ["default","rect","circle","poly"]},
- {key: 'href', values: ["#"]},
- {key: 'target', values: ["#"]},
- {key: 'ping', values: []},
- {key: 'media', values: []},
- {key: 'hreflang', values: []},
- {key: 'type', values: []}
-
- ]},
- {tag: 'article', attr: []},
- {tag: 'aside', attr: []},
- {tag: 'audio', attr: [
- {key: 'src', values: []},
- {key: 'crossorigin', values: ["anonymous","use-credentials"]},
- {key: 'preload', values: ["none","metadata","auto"]},
- {key: 'autoplay', values: ["","autoplay"]},
- {key: 'mediagroup', values: []},
- {key: 'loop', values: ["","loop"]},
- {key: 'controls', values: ["","controls"]}
- ]},
- {tag: 'b', attr: []},
- {tag: 'base', attr: [
- {key: 'href', values: ["#"]},
- {key: 'target', values: ["_blank","_self","_top","_parent"]}
- ]},
- {tag: 'basefont', attr: []},
- {tag: 'bdi', attr: []},
- {tag: 'bdo', attr: []},
- {tag: 'big', attr: []},
- {tag: 'blockquote', attr: [
- {key: 'cite', values: ["http://"]}
- ]},
- {tag: 'body', attr: []},
- {tag: 'br', attr: []},
- {tag: 'button', attr: [
- {key: 'autofocus', values: ["","autofocus"]},
- {key: 'disabled', values: ["","disabled"]},
- {key: 'form', values: []},
- {key: 'formaction', values: []},
- {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},
- {key: 'formmethod', values: ["get","post","put","delete"]},
- {key: 'formnovalidate', values: ["","novalidate"]},
- {key: 'formtarget', values: ["_blank","_self","_top","_parent"]},
- {key: 'name', values: []},
- {key: 'type', values: ["submit","reset","button"]},
- {key: 'value', values: []}
- ]},
- {tag: 'canvas', attr: [
- {key: 'width', values: []},
- {key: 'height', values: []}
- ]},
- {tag: 'caption', attr: []},
- {tag: 'center', attr: []},
- {tag: 'cite', attr: []},
- {tag: 'code', attr: []},
- {tag: 'col', attr: [
- {key: 'span', values: []}
- ]},
- {tag: 'colgroup', attr: [
- {key: 'span', values: []}
- ]},
- {tag: 'command', attr: [
- {key: 'type', values: ["command","checkbox","radio"]},
- {key: 'label', values: []},
- {key: 'icon', values: []},
- {key: 'disabled', values: ["","disabled"]},
- {key: 'checked', values: ["","checked"]},
- {key: 'radiogroup', values: []},
- {key: 'command', values: []},
- {key: 'title', values: []}
- ]},
- {tag: 'data', attr: [
- {key: 'value', values: []}
- ]},
- {tag: 'datagrid', attr: [
- {key: 'disabled', values: ["","disabled"]},
- {key: 'multiple', values: ["","multiple"]}
- ]},
- {tag: 'datalist', attr: [
- {key: 'data', values: []}
- ]},
- {tag: 'dd', attr: []},
- {tag: 'del', attr: [
- {key: 'cite', values: []},
- {key: 'datetime', values: []}
- ]},
- {tag: 'details', attr: [
- {key: 'open', values: ["","open"]}
- ]},
- {tag: 'dfn', attr: []},
- {tag: 'dir', attr: []},
- {tag: 'div', attr: [
- {key: 'id', values: []},
- {key: 'class', values: []},
- {key: 'style', values: []}
- ]},
- {tag: 'dl', attr: []},
- {tag: 'dt', attr: []},
- {tag: 'em', attr: []},
- {tag: 'embed', attr: [
- {key: 'src', values: []},
- {key: 'type', values: []},
- {key: 'width', values: []},
- {key: 'height', values: []}
- ]},
- {tag: 'eventsource', attr: [
- {key: 'src', values: []}
- ]},
- {tag: 'fieldset', attr: [
- {key: 'disabled', values: ["","disabled"]},
- {key: 'form', values: []},
- {key: 'name', values: []}
- ]},
- {tag: 'figcaption', attr: []},
- {tag: 'figure', attr: []},
- {tag: 'font', attr: []},
- {tag: 'footer', attr: []},
- {tag: 'form', attr: [
- {key: 'accept-charset', values: ["UNKNOWN","utf-8"]},
- {key: 'action', values: []},
- {key: 'autocomplete', values: ["on","off"]},
- {key: 'enctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},
- {key: 'method', values: ["get","post","put","delete","dialog"]},
- {key: 'name', values: []},
- {key: 'novalidate', values: ["","novalidate"]},
- {key: 'target', values: ["_blank","_self","_top","_parent"]}
- ]},
- {tag: 'frame', attr: []},
- {tag: 'frameset', attr: []},
- {tag: 'h1', attr: []},
- {tag: 'h2', attr: []},
- {tag: 'h3', attr: []},
- {tag: 'h4', attr: []},
- {tag: 'h5', attr: []},
- {tag: 'h6', attr: []},
- {tag: 'head', attr: []},
- {tag: 'header', attr: []},
- {tag: 'hgroup', attr: []},
- {tag: 'hr', attr: []},
- {tag: 'html', attr: [
- {key: 'manifest', values: []}
- ]},
- {tag: 'i', attr: []},
- {tag: 'iframe', attr: [
- {key: 'src', values: []},
- {key: 'srcdoc', values: []},
- {key: 'name', values: []},
- {key: 'sandbox', values: ["allow-top-navigation","allow-same-origin","allow-forms","allow-scripts"]},
- {key: 'seamless', values: ["","seamless"]},
- {key: 'width', values: []},
- {key: 'height', values: []}
- ]},
- {tag: 'img', attr: [
- {key: 'alt', values: []},
- {key: 'src', values: []},
- {key: 'crossorigin', values: ["anonymous","use-credentials"]},
- {key: 'ismap', values: []},
- {key: 'usemap', values: []},
- {key: 'width', values: []},
- {key: 'height', values: []}
- ]},
- {tag: 'input', attr: [
- {key: 'accept', values: ["audio/*","video/*","image/*"]},
- {key: 'alt', values: []},
- {key: 'autocomplete', values: ["on","off"]},
- {key: 'autofocus', values: ["","autofocus"]},
- {key: 'checked', values: ["","checked"]},
- {key: 'disabled', values: ["","disabled"]},
- {key: 'dirname', values: []},
- {key: 'form', values: []},
- {key: 'formaction', values: []},
- {key: 'formenctype', values: ["application/x-www-form-urlencoded","multipart/form-data","text/plain"]},
- {key: 'formmethod', values: ["get","post","put","delete"]},
- {key: 'formnovalidate', values: ["","novalidate"]},
- {key: 'formtarget', values: ["_blank","_self","_top","_parent"]},
- {key: 'height', values: []},
- {key: 'list', values: []},
- {key: 'max', values: []},
- {key: 'maxlength', values: []},
- {key: 'min', values: []},
- {key: 'multiple', values: ["","multiple"]},
- {key: 'name', values: []},
- {key: 'pattern', values: []},
- {key: 'placeholder', values: []},
- {key: 'readonly', values: ["","readonly"]},
- {key: 'required', values: ["","required"]},
- {key: 'size', values: []},
- {key: 'src', values: []},
- {key: 'step', values: []},
- {key: 'type', values: [
- "hidden","text","search","tel","url","email","password","datetime","date","month","week","time","datetime-local",
- "number","range","color","checkbox","radio","file","submit","image","reset","button"
- ]},
- {key: 'value', values: []},
- {key: 'width', values: []}
- ]},
- {tag: 'ins', attr: [
- {key: 'cite', values: []},
- {key: 'datetime', values: []}
- ]},
- {tag: 'kbd', attr: []},
- {tag: 'keygen', attr: [
- {key: 'autofocus', values: ["","autofocus"]},
- {key: 'challenge', values: []},
- {key: 'disabled', values: ["","disabled"]},
- {key: 'form', values: []},
- {key: 'keytype', values: ["RSA"]},
- {key: 'name', values: []}
- ]},
- {tag: 'label', attr: [
- {key: 'for', values: []},
- {key: 'form', values: []}
- ]},
- {tag: 'legend', attr: []},
- {tag: 'li', attr: [
- {key: 'value', values: []}
- ]},
- {tag: 'link', attr: [
- {key: 'href', values: []},
- {key: 'hreflang', values: ["en","es"]},
- {key: 'media', values: [
- "all","screen","print","embossed","braille","handheld","print","projection","screen","tty","tv","speech","3d-glasses",
- "resolution [>][<][=] [X]dpi","resolution [>][<][=] [X]dpcm","device-aspect-ratio: 16/9","device-aspect-ratio: 4/3",
- "device-aspect-ratio: 32/18","device-aspect-ratio: 1280/720","device-aspect-ratio: 2560/1440","orientation:portrait",
- "orientation:landscape","device-height: [X]px","device-width: [X]px","-webkit-min-device-pixel-ratio: 2"
- ]},
- {key: 'type', values: []},
- {key: 'sizes', values: ["all","16x16","16x16 32x32","16x16 32x32 64x64"]}
- ]},
- {tag: 'map', attr: [
- {key: 'name', values: []}
- ]},
- {tag: 'mark', attr: []},
- {tag: 'menu', attr: [
- {key: 'type', values: ["list","context","toolbar"]},
- {key: 'label', values: []}
- ]},
- {tag: 'meta', attr: [
- {key: 'charset', attr: ["utf-8"]},
- {key: 'name', attr: ["viewport","application-name","author","description","generator","keywords"]},
- {key: 'content', attr: ["","width=device-width","initial-scale=1, maximum-scale=1, minimun-scale=1, user-scale=no"]},
- {key: 'http-equiv', attr: ["content-language","content-type","default-style","refresh"]}
- ]},
- {tag: 'meter', attr: [
- {key: 'value', values: []},
- {key: 'min', values: []},
- {key: 'low', values: []},
- {key: 'high', values: []},
- {key: 'max', values: []},
- {key: 'optimum', values: []}
- ]},
- {tag: 'nav', attr: []},
- {tag: 'noframes', attr: []},
- {tag: 'noscript', attr: []},
- {tag: 'object', attr: [
- {key: 'data', values: []},
- {key: 'type', values: []},
- {key: 'typemustmatch', values: ["","typemustmatch"]},
- {key: 'name', values: []},
- {key: 'usemap', values: []},
- {key: 'form', values: []},
- {key: 'width', values: []},
- {key: 'height', values: []}
- ]},
- {tag: 'ol', attr: [
- {key: 'reversed', values: ["", "reversed"]},
- {key: 'start', values: []},
- {key: 'type', values: ["1","a","A","i","I"]}
- ]},
- {tag: 'optgroup', attr: [
- {key: 'disabled', values: ["","disabled"]},
- {key: 'label', values: []}
- ]},
- {tag: 'option', attr: [
- {key: 'disabled', values: ["", "disabled"]},
- {key: 'label', values: []},
- {key: 'selected', values: ["", "selected"]},
- {key: 'value', values: []}
- ]},
- {tag: 'output', attr: [
- {key: 'for', values: []},
- {key: 'form', values: []},
- {key: 'name', values: []}
- ]},
- {tag: 'p', attr: []},
- {tag: 'param', attr: [
- {key: 'name', values: []},
- {key: 'value', values: []}
- ]},
- {tag: 'pre', attr: []},
- {tag: 'progress', attr: [
- {key: 'value', values: []},
- {key: 'max', values: []}
- ]},
- {tag: 'q', attr: [
- {key: 'cite', values: []}
- ]},
- {tag: 'rp', attr: []},
- {tag: 'rt', attr: []},
- {tag: 'ruby', attr: []},
- {tag: 's', attr: []},
- {tag: 'samp', attr: []},
- {tag: 'script', attr: [
- {key: 'type', values: ["text/javascript"]},
- {key: 'src', values: []},
- {key: 'async', values: ["","async"]},
- {key: 'defer', values: ["","defer"]},
- {key: 'charset', values: ["utf-8"]}
- ]},
- {tag: 'section', attr: []},
- {tag: 'select', attr: [
- {key: 'autofocus', values: ["", "autofocus"]},
- {key: 'disabled', values: ["", "disabled"]},
- {key: 'form', values: []},
- {key: 'multiple', values: ["", "multiple"]},
- {key: 'name', values: []},
- {key: 'size', values: []}
- ]},
- {tag: 'small', attr: []},
- {tag: 'source', attr: [
- {key: 'src', values: []},
- {key: 'type', values: []},
- {key: 'media', values: []}
- ]},
- {tag: 'span', attr: []},
- {tag: 'strike', attr: []},
- {tag: 'strong', attr: []},
- {tag: 'style', attr: [
- {key: 'type', values: ["text/css"]},
- {key: 'media', values: ["all","braille","print","projection","screen","speech"]},
- {key: 'scoped', values: []}
- ]},
- {tag: 'sub', attr: []},
- {tag: 'summary', attr: []},
- {tag: 'sup', attr: []},
- {tag: 'table', attr: [
- {key: 'border', values: []}
- ]},
- {tag: 'tbody', attr: []},
- {tag: 'td', attr: [
- {key: 'colspan', values: []},
- {key: 'rowspan', values: []},
- {key: 'headers', values: []}
- ]},
- {tag: 'textarea', attr: [
- {key: 'autofocus', values: ["","autofocus"]},
- {key: 'disabled', values: ["","disabled"]},
- {key: 'dirname', values: []},
- {key: 'form', values: []},
- {key: 'maxlength', values: []},
- {key: 'name', values: []},
- {key: 'placeholder', values: []},
- {key: 'readonly', values: ["","readonly"]},
- {key: 'required', values: ["","required"]},
- {key: 'rows', values: []},
- {key: 'cols', values: []},
- {key: 'wrap', values: ["soft","hard"]}
- ]},
- {tag: 'tfoot', attr: []},
- {tag: 'th', attr: [
- {key: 'colspan', values: []},
- {key: 'rowspan', values: []},
- {key: 'headers', values: []},
- {key: 'scope', values: ["row","col","rowgroup","colgroup"]}
- ]},
- {tag: 'thead', attr: []},
- {tag: 'time', attr: [
- {key: 'datetime', values: []}
- ]},
- {tag: 'title', attr: []},
- {tag: 'tr', attr: []},
- {tag: 'track', attr: [
- {key: 'kind', values: ["subtitles","captions","descriptions","chapters","metadata"]},
- {key: 'src', values: []},
- {key: 'srclang', values: ["en","es"]},
- {key: 'label', values: []},
- {key: 'default', values: []}
- ]},
- {tag: 'tt', attr: []},
- {tag: 'u', attr: []},
- {tag: 'ul', attr: []},
- {tag: 'var', attr: []},
- {tag: 'video', attr: [
- {key: "src", values: []},
- {key: "crossorigin", values: ["anonymous","use-credentials"]},
- {key: "poster", values: []},
- {key: "preload", values: ["auto","metadata","none"]},
- {key: "autoplay", values: ["","autoplay"]},
- {key: "mediagroup", values: ["movie"]},
- {key: "loop", values: ["","loop"]},
- {key: "muted", values: ["","muted"]},
- {key: "controls", values: ["","controls"]},
- {key: "width", values: []},
- {key: "height", values: []}
- ]},
- {tag: 'wbr', attr: []}
- ];
+ var globalAttrs = {
+ accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
+ "class": null,
+ contenteditable: ["true", "false"],
+ contextmenu: null,
+ dir: ["ltr", "rtl", "auto"],
+ draggable: ["true", "false", "auto"],
+ dropzone: ["copy", "move", "link", "string:", "file:"],
+ hidden: ["hidden"],
+ id: null,
+ inert: ["inert"],
+ itemid: null,
+ itemprop: null,
+ itemref: null,
+ itemscope: ["itemscope"],
+ itemtype: null,
+ lang: ["en", "es"],
+ spellcheck: ["true", "false"],
+ style: null,
+ tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
+ title: null,
+ translate: ["yes", "no"],
+ onclick: null,
+ rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
+ };
+ function populate(obj) {
+ for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
+ obj.attrs[attr] = globalAttrs[attr];
+ }
- var globalAttributes = [
- {key: "accesskey", values: ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9"]},
- {key: "class", values: []},
- {key: "contenteditable", values: ["true", "false"]},
- {key: "contextmenu", values: []},
- {key: "dir", values: ["ltr","rtl","auto"]},
- {key: "draggable", values: ["true","false","auto"]},
- {key: "dropzone", values: ["copy","move","link","string:","file:"]},
- {key: "hidden", values: ["hidden"]},
- {key: "id", values: []},
- {key: "inert", values: ["inert"]},
- {key: "itemid", values: []},
- {key: "itemprop", values: []},
- {key: "itemref", values: []},
- {key: "itemscope", values: ["itemscope"]},
- {key: "itemtype", values: []},
- {key: "lang", values: ["en","es"]},
- {key: "spellcheck", values: ["true","false"]},
- {key: "style", values: []},
- {key: "tabindex", values: ["1","2","3","4","5","6","7","8","9"]},
- {key: "title", values: []},
- {key: "translate", values: ["yes","no"]},
- {key: "onclick", values: []},
- {key: 'rel', values: ["stylesheet","alternate","author","bookmark","help","license","next","nofollow","noreferrer","prefetch","prev","search","tag"]}
- ];
+ populate(s);
+ for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
+ populate(data[tag]);
- CodeMirror.htmlHint = function(editor) {
- if(String.prototype.trim == undefined) {
- String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');};
- }
- return htmlHint(editor, htmlStructure, function (e, cur) { return e.getTokenAt(cur); });
+ CodeMirror.htmlSchema = data;
+ CodeMirror.htmlHint = function(cm, options) {
+ var local = {schemaInfo: data};
+ if (options) for (var opt in options) local[opt] = options[opt];
+ return CodeMirror.xmlHint(cm, local);
};
})();
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index 2c54dcd77e..35e5cbb721 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -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);
@@ -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;
+ }
+ };
+})();
diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js
index 42eab6f3b7..ea5b8d546a 100644
--- a/addon/hint/xml-hint.js
+++ b/addon/hint/xml-hint.js
@@ -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 '';
- };
-
+ };
})();
diff --git a/addon/lint/coffeescript-lint.js b/addon/lint/coffeescript-lint.js
new file mode 100644
index 0000000000..9c30de51c2
--- /dev/null
+++ b/addon/lint/coffeescript-lint.js
@@ -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;
+};
diff --git a/addon/merge/dep/diff_match_patch.js b/addon/merge/dep/diff_match_patch.js
new file mode 100644
index 0000000000..ac34105fc4
--- /dev/null
+++ b/addon/merge/dep/diff_match_patch.js
@@ -0,0 +1,50 @@
+// From https://code.google.com/p/google-diff-match-patch/ , licensed under the Apache License 2.0
+(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
+diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a,
+b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
+diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100 The This addon depends on
+the google-diff-match-patch
+library to compute the diffs.'+j+"";break;case 0:b[g]=""+j+""}}return b.join("")};
+diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cCodeMirror: merge view demo
+
+
+
+merge
+addon provides an interface for displaying and merging diffs,
+either two-way
+or three-way. The left
+(or center) pane is editable, and the differences with the other
+pane(s) are shown live as you edit it.