diff --git a/addon/comment/comment.js b/addon/comment/comment.js
new file mode 100644
index 0000000000..4f590f2870
--- /dev/null
+++ b/addon/comment/comment.js
@@ -0,0 +1,144 @@
+(function() {
+ "use strict";
+
+ var noOptions = {};
+ var nonWS = /[^\s\u00a0]/;
+ var Pos = CodeMirror.Pos;
+
+ function firstNonWS(str) {
+ var found = str.search(nonWS);
+ return found == -1 ? 0 : found;
+ }
+
+ CodeMirror.commands.toggleComment = function(cm) {
+ var from = cm.getCursor("start"), to = cm.getCursor("end");
+ cm.uncomment(from, to) || cm.lineComment(from, to);
+ };
+
+ CodeMirror.defineExtension("lineComment", function(from, to, options) {
+ if (!options) options = noOptions;
+ var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode;
+ var commentString = options.lineComment || mode.lineComment;
+ if (!commentString) {
+ if (options.blockCommentStart || mode.blockCommentStart) {
+ options.fullLines = true;
+ self.blockComment(from, to, options);
+ }
+ return;
+ }
+ var firstLine = self.getLine(from.line);
+ 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;
+
+ self.operation(function() {
+ if (options.indent) {
+ var baseString = firstLine.slice(0, firstNonWS(firstLine));
+ for (var i = from.line; i < end; ++i) {
+ var line = self.getLine(i), cut = baseString.length;
+ if (!blankLines && !nonWS.test(line)) continue;
+ if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
+ self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
+ }
+ } else {
+ for (var i = from.line; i < end; ++i) {
+ if (blankLines || nonWS.test(self.getLine(i)))
+ self.replaceRange(commentString + pad, Pos(i, 0));
+ }
+ }
+ });
+ });
+
+ CodeMirror.defineExtension("blockComment", function(from, to, options) {
+ if (!options) options = noOptions;
+ var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode;
+ var startString = options.blockCommentStart || mode.blockCommentStart;
+ var endString = options.blockCommentEnd || mode.blockCommentEnd;
+ if (!startString || !endString) {
+ if ((options.lineComment || mode.lineComment) && options.fullLines != false)
+ self.lineComment(from, to, options);
+ return;
+ }
+
+ var end = Math.min(to.line, self.lastLine());
+ if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
+
+ var pad = options.padding == null ? " " : options.padding;
+ if (from.line > end) return;
+
+ self.operation(function() {
+ if (options.fullLines != false) {
+ var lastLineHasText = nonWS.test(self.getLine(end));
+ self.replaceRange(pad + endString, Pos(end));
+ self.replaceRange(startString + pad, Pos(from.line, 0));
+ var lead = options.blockCommentLead || mode.blockCommentLead;
+ if (lead != null) for (var i = from.line + 1; i <= end; ++i)
+ if (i != end || lastLineHasText)
+ self.replaceRange(lead + pad, Pos(i, 0));
+ } else {
+ self.replaceRange(endString, to);
+ self.replaceRange(startString, from);
+ }
+ });
+ });
+
+ CodeMirror.defineExtension("uncomment", function(from, to, options) {
+ if (!options) options = noOptions;
+ var self = this, mode = CodeMirror.innerMode(self.getMode(), self.getTokenAt(from).state).mode;
+ var end = Math.min(to.line, self.lastLine()), start = Math.min(from.line, end);
+
+ // Try finding line comments
+ var lineString = options.lineComment || mode.lineComment, lines = [];
+ var pad = options.padding == null ? " " : options.padding;
+ lineComment: for(;;) {
+ if (!lineString) break;
+ 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;
+ lines.push(line);
+ }
+ self.operation(function() {
+ for (var i = start; i <= end; ++i) {
+ var line = lines[i - start];
+ var pos = line.indexOf(lineString), endPos = pos + lineString.length;
+ if (pos < 0) continue;
+ if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
+ self.replaceRange("", Pos(i, pos), Pos(i, endPos));
+ }
+ });
+ return true;
+ }
+
+ // Try block comments
+ var startString = options.blockCommentStart || mode.blockCommentStart;
+ var endString = options.blockCommentEnd || mode.blockCommentEnd;
+ if (!startString || !endString) return false;
+ var lead = options.blockCommentLead || mode.blockCommentLead;
+ var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end);
+ var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString);
+ if (close == -1 && start != end) {
+ endLine = self.getLine(--end);
+ close = endLine.lastIndexOf(endString);
+ }
+ if (open == -1 || close == -1) return false;
+
+ self.operation(function() {
+ self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
+ Pos(end, close + endString.length));
+ var openEnd = open + startString.length;
+ if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
+ self.replaceRange("", Pos(start, open), Pos(start, openEnd));
+ if (lead) for (var i = start + 1; i <= end; ++i) {
+ var line = self.getLine(i), found = line.indexOf(lead);
+ if (found == -1 || nonWS.test(line.slice(0, found))) continue;
+ var foundEnd = found + lead.length;
+ if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
+ self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
+ }
+ });
+ return true;
+ });
+})();
diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js
index f85f2df127..18f9dff3ab 100644
--- a/addon/display/placeholder.js
+++ b/addon/display/placeholder.js
@@ -19,14 +19,14 @@
});
function clearPlaceholder(cm) {
- if (cm._placeholder) {
- cm._placeholder.parentNode.removeChild(cm._placeholder);
- cm._placeholder = null;
+ if (cm.state.placeholder) {
+ cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
+ cm.state.placeholder = null;
}
}
function setPlaceholder(cm) {
clearPlaceholder(cm);
- var elt = cm._placeholder = document.createElement("pre");
+ var elt = cm.state.placeholder = document.createElement("pre");
elt.style.cssText = "height: 0; overflow: visible";
elt.className = "CodeMirror-placeholder";
elt.appendChild(document.createTextNode(cm.getOption("placeholder")));
diff --git a/addon/edit/closebrackets.js b/addon/edit/closebrackets.js
index 43902aee68..2abc8c5fe6 100644
--- a/addon/edit/closebrackets.js
+++ b/addon/edit/closebrackets.js
@@ -23,9 +23,9 @@
return CodeMirror.Pass;
}
};
- var closingBrackets = [];
+ var closingBrackets = "";
for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
- if (left != right) closingBrackets.push(right);
+ if (left != right) closingBrackets += right;
function surround(cm) {
var selection = cm.getSelection();
cm.replaceSelection(left + selection + right);
diff --git a/addon/fold/brace-fold.js b/addon/fold/brace-fold.js
index dc78883f35..efdffb877c 100644
--- a/addon/fold/brace-fold.js
+++ b/addon/fold/brace-fold.js
@@ -3,17 +3,23 @@ CodeMirror.braceRangeFinder = function(cm, start) {
var at = lineText.length, startChar, tokenType;
for (; at > 0;) {
var found = lineText.lastIndexOf("{", at);
- if (found < start.ch) break;
+ var startToken = '{', endToken = '}';
+ if (found < start.ch) {
+ found = lineText.lastIndexOf("[", at);
+ if (found < start.ch) break;
+ startToken = '['; endToken = ']';
+ }
+
tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
if (!/^(comment|string)/.test(tokenType)) { startChar = found; break; }
at = found - 1;
}
- if (startChar == null || lineText.lastIndexOf("}") > startChar) return;
+ 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;
for (;;) {
- var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
+ var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
if (nextOpen < 0) nextOpen = text.length;
if (nextClose < 0) nextClose = text.length;
pos = Math.min(nextOpen, nextClose);
diff --git a/addon/lint/lint.css b/addon/lint/lint.css
index 4fd72e774a..eb15381f02 100644
--- a/addon/lint/lint.css
+++ b/addon/lint/lint.css
@@ -24,18 +24,18 @@
-ms-transition: opacity .4s;
}
-.CodeMirror-lint-span-error, .CodeMirror-lint-span-warning {
+.CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning {
background-position: left bottom;
background-repeat: repeat-x;
}
-.CodeMirror-lint-span-error {
+.CodeMirror-lint-mark-error {
background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
;
}
-.CodeMirror-lint-span-warning {
+.CodeMirror-lint-mark-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}
diff --git a/addon/lint/lint.js b/addon/lint/lint.js
index 231eb2f567..2e7cea1925 100644
--- a/addon/lint/lint.js
+++ b/addon/lint/lint.js
@@ -59,7 +59,7 @@ CodeMirror.validate = (function() {
}
function clearMarks(cm) {
- var state = cm._lintState;
+ var state = cm.state.lint;
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
@@ -105,16 +105,16 @@ CodeMirror.validate = (function() {
}
function startLinting(cm) {
- var state = cm._lintState, options = state.options;
- if (options.async)
- options.getAnnotations(cm, updateLinting, options);
- else
- updateLinting(cm, options.getAnnotations(cm.getValue()));
+ var state = cm.state.lint, options = state.options;
+ if (options.async)
+ options.getAnnotations(cm, updateLinting, options);
+ else
+ updateLinting(cm, options.getAnnotations(cm.getValue()));
}
function updateLinting(cm, annotationsNotSorted) {
clearMarks(cm);
- var state = cm._lintState, options = state.options;
+ var state = cm.state.lint, options = state.options;
var annotations = groupByLine(annotationsNotSorted);
@@ -135,7 +135,7 @@ CodeMirror.validate = (function() {
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
- className: "CodeMirror-lint-span-" + severity,
+ className: "CodeMirror-lint-mark-" + severity,
__annotation: ann
}));
}
@@ -148,7 +148,7 @@ CodeMirror.validate = (function() {
}
function onChange(cm) {
- var state = cm._lintState;
+ var state = cm.state.lint;
clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
}
@@ -164,7 +164,7 @@ CodeMirror.validate = (function() {
var nearby = [0, 0, 0, 5, 0, -5, 5, 0, -5, 0];
function onMouseOver(cm, e) {
- if (!/\bCodeMirror-lint-span-/.test((e.target || e.srcElement).className)) return;
+ if (!/\bCodeMirror-lint-mark-/.test((e.target || e.srcElement).className)) return;
for (var i = 0; i < nearby.length; i += 2) {
var spans = cm.findMarksAt(cm.coordsChar({left: e.clientX + nearby[i],
top: e.clientY + nearby[i + 1]}));
@@ -179,14 +179,14 @@ CodeMirror.validate = (function() {
if (old && old != CodeMirror.Init) {
clearMarks(cm);
cm.off("change", onChange);
- CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver);
- delete cm._lintState;
+ CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
+ delete cm.state.lint;
}
if (val) {
var gutters = cm.getOption("gutters"), hasLintGutter = false;
for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
- var state = cm._lintState = new LintState(cm, parseOptions(val), hasLintGutter);
+ var state = cm.state.lint = new LintState(cm, parseOptions(val), hasLintGutter);
cm.on("change", onChange);
if (state.options.tooltips != false)
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
diff --git a/addon/runmode/runmode.js b/addon/runmode/runmode.js
index 3e1bed7361..a7da6d718f 100644
--- a/addon/runmode/runmode.js
+++ b/addon/runmode/runmode.js
@@ -1,5 +1,7 @@
CodeMirror.runMode = function(string, modespec, callback, options) {
var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
+ var ie = /MSIE \d/.test(navigator.userAgent);
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
if (callback.nodeType == 1) {
var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
@@ -7,7 +9,9 @@ CodeMirror.runMode = function(string, modespec, callback, options) {
node.innerHTML = "";
callback = function(text, style) {
if (text == "\n") {
- node.appendChild(document.createElement("br"));
+ // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+ // Emitting a carriage return makes everything ok.
+ node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
col = 0;
return;
}
diff --git a/addon/runmode/runmode.node.js b/addon/runmode/runmode.node.js
index 6449e77c84..a6ea919d83 100644
--- a/addon/runmode/runmode.node.js
+++ b/addon/runmode/runmode.node.js
@@ -60,8 +60,20 @@ exports.startState = function(mode, a1, a2) {
};
var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
-exports.defineMode = function(name, mode) { modes[name] = mode; };
+exports.defineMode = function(name, mode) {
+ if (arguments.length > 2) {
+ mode.dependencies = [];
+ for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]);
+ }
+ modes[name] = mode;
+};
exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
+
+exports.defineMode("null", function() {
+ return {token: function(stream) {stream.skipToEnd();}};
+});
+exports.defineMIME("text/plain", "null");
+
exports.getMode = function(options, spec) {
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
spec = mimeModes[spec];
diff --git a/addon/search/match-highlighter.js b/addon/search/match-highlighter.js
index 14c1dab5b6..0800f4c638 100644
--- a/addon/search/match-highlighter.js
+++ b/addon/search/match-highlighter.js
@@ -24,19 +24,19 @@
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
- cm._matchHighlightState = new State(val);
+ cm.state.matchHighlighter = new State(val);
cm.on("cursorActivity", highlightMatches);
} else if (!val && prev) {
- var over = cm._matchHighlightState.overlay;
+ var over = cm.state.matchHighlighter.overlay;
if (over) cm.removeOverlay(over);
- cm._matchHighlightState = null;
+ cm.state.matchHighlighter = null;
cm.off("cursorActivity", highlightMatches);
}
});
function highlightMatches(cm) {
cm.operation(function() {
- var state = cm._matchHighlightState;
+ var state = cm.state.matchHighlighter;
if (state.overlay) {
cm.removeOverlay(state.overlay);
state.overlay = null;
diff --git a/addon/search/search.js b/addon/search/search.js
index 6331b86555..eb9ab8bede 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -27,7 +27,7 @@
this.overlay = null;
}
function getSearchState(cm) {
- return cm._searchState || (cm._searchState = new SearchState());
+ return cm.state.search || (cm.state.search = new SearchState());
}
function getSearchCursor(cm, query, pos) {
// Heuristic: if the query string is all lowercase, do a case insensitive search.
diff --git a/addon/search/searchcursor.js b/addon/search/searchcursor.js
index a590e84443..3da3f04e8f 100644
--- a/addon/search/searchcursor.js
+++ b/addon/search/searchcursor.js
@@ -24,16 +24,26 @@
if (!newMatch) break;
match = newMatch;
start = match.index;
- cutOff = match.index + 1;
+ cutOff = match.index + (match[0].length || 1);
+ if (cutOff == line.length) break;
+ }
+ var matchLen = (match && match[0].length) || 0;
+ if (!matchLen) {
+ if (start == 0 && line.length == 0) {match = undefined;}
+ else if (start != doc.getLine(pos.line).length) {
+ matchLen++;
+ }
}
} else {
query.lastIndex = pos.ch;
- var line = doc.getLine(pos.line), match = query.exec(line),
- start = match && match.index;
+ var line = doc.getLine(pos.line), match = query.exec(line);
+ var matchLen = (match && match[0].length) || 0;
+ var start = match && match.index;
+ if (start + matchLen != line.length && !matchLen) matchLen = 1;
}
- if (match && match[0])
+ if (match && matchLen)
return {from: Pos(pos.line, start),
- to: Pos(pos.line, start + match[0].length),
+ to: Pos(pos.line, start + matchLen),
match: match};
};
} else { // String query
diff --git a/addon/selection/active-line.js b/addon/selection/active-line.js
index 211de0fefd..65fab6f162 100644
--- a/addon/selection/active-line.js
+++ b/addon/selection/active-line.js
@@ -17,23 +17,23 @@
} else if (!val && prev) {
cm.off("cursorActivity", updateActiveLine);
clearActiveLine(cm);
- delete cm._activeLine;
+ delete cm.state.activeLine;
}
});
function clearActiveLine(cm) {
- if ("_activeLine" in cm) {
- cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS);
- cm.removeLineClass(cm._activeLine, "background", BACK_CLASS);
+ if ("activeLine" in cm.state) {
+ cm.removeLineClass(cm.state.activeLine, "wrap", WRAP_CLASS);
+ cm.removeLineClass(cm.state.activeLine, "background", BACK_CLASS);
}
}
function updateActiveLine(cm) {
var line = cm.getLineHandle(cm.getCursor().line);
- if (cm._activeLine == line) return;
+ if (cm.state.activeLine == line) return;
clearActiveLine(cm);
cm.addLineClass(line, "wrap", WRAP_CLASS);
cm.addLineClass(line, "background", BACK_CLASS);
- cm._activeLine = line;
+ cm.state.activeLine = line;
}
})();
diff --git a/addon/selection/mark-selection.js b/addon/selection/mark-selection.js
index d7ff30c9a9..c97776e492 100644
--- a/addon/selection/mark-selection.js
+++ b/addon/selection/mark-selection.js
@@ -1,7 +1,8 @@
// Because sometimes you need to mark the selected *text*.
//
// Adds an option 'styleSelectedText' which, when enabled, gives
-// selected text the CSS class "CodeMirror-selectedtext".
+// selected text the CSS class given as option value, or
+// "CodeMirror-selectedtext" when the value is not a string.
(function() {
"use strict";
@@ -9,26 +10,99 @@
CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
- updateSelectedText(cm);
- cm.on("cursorActivity", updateSelectedText);
+ cm.state.markedSelection = [];
+ cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
+ reset(cm);
+ cm.on("cursorActivity", onCursorActivity);
+ cm.on("change", onChange);
} else if (!val && prev) {
- cm.off("cursorActivity", updateSelectedText);
- clearSelectedText(cm);
- delete cm._selectionMark;
+ cm.off("cursorActivity", onCursorActivity);
+ cm.off("change", onChange);
+ clear(cm);
+ cm.state.markedSelection = cm.state.markedSelectionStyle = null;
}
});
- function clearSelectedText(cm) {
- if (cm._selectionMark) cm._selectionMark.clear();
+ function onCursorActivity(cm) {
+ cm.operation(function() { update(cm); });
}
- function updateSelectedText(cm) {
- clearSelectedText(cm);
+ function onChange(cm) {
+ if (cm.state.markedSelection.length)
+ cm.operation(function() { clear(cm); });
+ }
+
+ var CHUNK_SIZE = 8;
+ var Pos = CodeMirror.Pos;
+
+ function cmp(pos1, pos2) {
+ return pos1.line - pos2.line || pos1.ch - pos2.ch;
+ }
+
+ function coverRange(cm, from, to, addAt) {
+ if (cmp(from, to) == 0) return;
+ var array = cm.state.markedSelection;
+ var cls = cm.state.markedSelectionStyle;
+ for (var line = from.line;;) {
+ var start = line == from.line ? from : Pos(line, 0);
+ var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
+ var end = atEnd ? to : Pos(endLine, 0);
+ var mark = cm.markText(start, end, {className: cls});
+ if (addAt == null) array.push(mark);
+ else array.splice(addAt++, 0, mark);
+ if (atEnd) break;
+ line = endLine;
+ }
+ }
- if (cm.somethingSelected())
- cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"),
- {className: "CodeMirror-selectedtext"});
- else
- cm._selectionMark = null;
+ function clear(cm) {
+ var array = cm.state.markedSelection;
+ for (var i = 0; i < array.length; ++i) array[i].clear();
+ array.length = 0;
+ }
+
+ function reset(cm) {
+ clear(cm);
+ var from = cm.getCursor("start"), to = cm.getCursor("end");
+ coverRange(cm, from, to);
+ }
+
+ function update(cm) {
+ var from = cm.getCursor("start"), to = cm.getCursor("end");
+ if (cmp(from, to) == 0) return clear(cm);
+
+ var array = cm.state.markedSelection;
+ if (!array.length) return coverRange(cm, from, to);
+
+ var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
+ if (!coverStart || !coverEnd || to.line - from.line < CHUNK_SIZE ||
+ cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
+ return reset(cm);
+
+ while (cmp(from, coverStart.from) > 0) {
+ array.shift().clear();
+ coverStart = array[0].find();
+ }
+ if (cmp(from, coverStart.from) < 0) {
+ if (coverStart.to.line - from.line < CHUNK_SIZE) {
+ array.shift().clear();
+ coverRange(cm, from, coverStart.to, 0);
+ } else {
+ coverRange(cm, from, coverStart.from, 0);
+ }
+ }
+
+ while (cmp(to, coverEnd.to) < 0) {
+ array.pop().clear();
+ coverEnd = array[array.length - 1].find();
+ }
+ if (cmp(to, coverEnd.to) > 0) {
+ if (to.line - coverEnd.from.line < CHUNK_SIZE) {
+ array.pop().clear();
+ coverRange(cm, coverEnd.from, to);
+ } else {
+ coverRange(cm, coverEnd.to, to);
+ }
+ }
}
})();
diff --git a/bin/compress b/bin/compress
index d059b618ba..809fbe83d4 100755
--- a/bin/compress
+++ b/bin/compress
@@ -29,14 +29,15 @@ function help(ok) {
process.exit(ok ? 0 : 1);
}
-var local = null, args = null, files = [], blob = "";
+var local = null, args = [], extraArgs = null, files = [], blob = "";
for (var i = 2; i < process.argv.length; ++i) {
var arg = process.argv[i];
if (arg == "--local" && i + 1 < process.argv.length) {
var parts = process.argv[++i].split(/\s+/);
local = parts[0];
- args = parts.slice(1);
+ extraArgs = parts.slice(1);
+ if (!extraArgs.length) extraArgs = ["-c", "-m"];
} else if (arg == "--help") {
help(true);
} else if (arg[0] != "-") {
@@ -73,7 +74,7 @@ if (files.length) {
}
if (local) {
- require("child_process").spawn(local, args, {stdio: ["ignore", process.stdout, process.stderr]});
+ require("child_process").spawn(local, args.concat(extraArgs), {stdio: ["ignore", process.stdout, process.stderr]});
} else {
var data = new Buffer("js_code=" + require("querystring").escape(blob), "utf8");
var req = require("http").request({
diff --git a/bin/source-highlight b/bin/source-highlight
new file mode 100755
index 0000000000..7596ed776c
--- /dev/null
+++ b/bin/source-highlight
@@ -0,0 +1,61 @@
+#!/usr/bin/env node
+
+// Simple command-line code highlighting tool. Reads code from stdin,
+// spits html to stdout. For example:
+//
+// echo 'function foo(a) { return a; }' | bin/source-highlight -s javascript
+// bin/source-highlight -s
+
+var fs = require("fs");
+
+CodeMirror = require("../addon/runmode/runmode.node.js");
+require("../mode/meta.js");
+
+var sPos = process.argv.indexOf("-s");
+if (sPos == -1 || sPos == process.argv.length - 1) {
+ console.error("Usage: source-highlight -s language");
+ process.exit(1);
+}
+var lang = process.argv[sPos + 1].toLowerCase(), modeName = lang;
+CodeMirror.modeInfo.forEach(function(info) {
+ if (info.mime == lang) {
+ modeName = info.mode;
+ } else if (info.name.toLowerCase() == lang) {
+ modeName = info.mode;
+ lang = info.mime;
+ }
+});
+
+function ensureMode(name) {
+ if (CodeMirror.modes[name] || name == "null") return;
+ try {
+ require("../mode/" + name + "/" + name + ".js");
+ } catch(e) {
+ console.error("Could not load mode " + name + ".");
+ process.exit(1);
+ }
+ var obj = CodeMirror.modes[name];
+ if (obj.dependencies) obj.dependencies.forEach(ensureMode);
+}
+ensureMode(modeName);
+
+function esc(str) {
+ return str.replace(/[<&]/, function(ch) { return ch == "&" ? "&" : "<"; });
+}
+
+var code = fs.readFileSync("/dev/stdin", "utf8");
+var curStyle = null, accum = "";
+function flush() {
+ if (curStyle) process.stdout.write("" + esc(accum) + " ");
+ else process.stdout.write(esc(accum));
+}
+
+CodeMirror.runMode(code, lang, function(text, style) {
+ if (style != curStyle) {
+ flush();
+ curStyle = style; accum = text;
+ } else {
+ accum += text;
+ }
+});
+flush();
diff --git a/doc/compress.html b/doc/compress.html
index f1fa218def..9c0fafec18 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -30,6 +30,7 @@
Version:
HEAD
+ 3.13
3.12
3.11
3.1
@@ -73,6 +74,7 @@
apl.js
clike.js
clojure.js
+ cobol.js
coffeescript.js
commonlisp.js
css.js
@@ -84,6 +86,7 @@
gas.js
go.js
groovy.js
+ haml.js
haskell.js
haxe.js
htmlembedded.js
diff --git a/doc/manual.html b/doc/manual.html
index a69a5cb92b..a7d6a67948 100644
--- a/doc/manual.html
+++ b/doc/manual.html
@@ -71,7 +71,8 @@ Basic Usage
The editor will be appended to the document body, will start
empty, and will use the mode that we loaded. To have more control
over the new editor, a configuration object can be passed
- to CodeMirror as a second argument:
+ to CodeMirror as a second
+ argument:
var myCodeMirror = CodeMirror(document.body, {
value: "function myScript(){return 100;}\n",
@@ -108,13 +109,13 @@ Basic Usage
Configuration
- Both the CodeMirror function and
- its fromTextArea method take as second (optional)
- argument an object containing configuration options. Any option
- not supplied like this will be taken
- from CodeMirror.defaults, an object containing the
- default options. You can update this object to change the defaults
- on your page.
+ Both the CodeMirror
+ function and its fromTextArea method take as second
+ (optional) argument an object containing configuration options.
+ Any option not supplied like this will be taken
+ from CodeMirror.defaults , an
+ object containing the default options. You can update this object
+ to change the defaults on your page.
Options are not checked in any way, so setting bogus option
values is bound to lead to odd errors.
@@ -231,6 +232,13 @@ Configuration
horizontally (false) or whether it stays fixed during horizontal
scrolling (true, the default).
+ coverGutterNextToScrollbar : boolean
+ When fixedGutter
+ is on, and there is a horizontal scrollbar, by default the
+ gutter will be visible to the left of this scrollbar. If this
+ option is set to true, it will be covered by an element with
+ class CodeMirror-gutter-filler.
+
readOnly : boolean|string
This disables editing of the editor content by the user. If
the special value "nocursor" is given (instead of
@@ -305,6 +313,11 @@ Configuration
Half-period in milliseconds used for cursor blinking. The default blink
rate is 530ms.
+ cursorScrollMargin : number
+ How much extra space to always keep above and below the
+ cursor when approaching the top or bottom of the visible view in
+ a scrollable document. Default is 0.
+
cursorHeight : number
Determines the height of the cursor. Default is 1, meaning
it spans the whole height of the line. For some fonts (and by
@@ -447,7 +460,7 @@ Events
"update" (instance: CodeMirror)
Will be fired whenever CodeMirror updates its DOM display.
- "renderLine" (instance: CodeMirror, line: integer, element: Element)
+ "renderLine" (instance: CodeMirror, line: LineHandle, element: Element)
Fired whenever a line is (re-)rendered to the DOM. Fired
right after the DOM element is built, before it is
added to the document. The handler may mess with the style of
@@ -715,6 +728,27 @@ Programming API
prefixed with cm. are only available
on CodeMirror instances.
+ Constructor
+
+ Constructing an editor instance is done with
+ the CodeMirror (place: Element|fn(Element),
+ ?option: object) constructor. If the place
+ argument is a DOM element, the editor will be appended to it. If
+ it is a function, it will be called, and is expected to place the
+ editor into the document. options may be an element
+ mapping option names to values. The options
+ that it doesn't explicitly specify (or all options, if it is not
+ passed) will be taken
+ from CodeMirror.defaults .
+
+ Note that the options object passed to the constructor will be
+ mutated when the instance's options
+ are changed , so you shouldn't share such
+ objects between instances.
+
+ See CodeMirror.fromTextArea
+ for another way to construct an editor instance.
+
Content manipulation methods
@@ -1078,17 +1112,16 @@ Text-marking methods
The method will return an object that represents the marker
(with constuctor CodeMirror.TextMarker), which
exposes three methods:
- clear(), to remove the mark,
- find(), which returns a {from, to}
- object (both holding document positions), indicating the current
- position of the marked range, or undefined if the
- marker is no longer in the document, and
- finally getOptions(copyWidget), which returns an
- object representing the options for the marker.
- If copyWidget is given true, it will clone the
- value of
- the replacedWith
- option, if any.
+ clear (), to remove the mark,
+ find (), which returns
+ a {from, to} object (both holding document
+ positions), indicating the current position of the marked range,
+ or undefined if the marker is no longer in the
+ document, and finally changed (),
+ which you can call if you've done something that might change
+ the size of the marker (for example changing the content of
+ a replacedWith
+ node), and want to cheaply update the display.
doc.setBookmark (pos: {line, ch}, ?options: object) → TextMarker
Inserts a bookmark, a handle that follows the text around it
@@ -1327,9 +1360,6 @@ Mode, state, and token-related methods
Miscellaneous methods
- CodeMirror (elt: Element|function(elt: Element), ?config: object)
- The constructor. See Basic Usage .
-
cm.operation (func: () → any) → any
CodeMirror internally buffers changes and only updates its
DOM structure after it has finished performing some operation.
@@ -1356,6 +1386,11 @@ Miscellaneous methods
Reduce the indentation of the line.
+ cm.toggleOverwrite (?value: bool)
+ Switches between overwrite and normal insert mode (when not
+ given an argument), or sets the overwrite mode to a specific
+ state (when given an argument).
+
doc.posFromIndex (index: integer) → {line, ch}
Calculates and returns a {line, ch} object for a
zero-based index who's value is relative to the start of the
@@ -1417,6 +1452,12 @@ Static properties
+ CodeMirror.defaults : object
+ An object containing default values for
+ all options . You can assign to its
+ properties to modify defaults (though this won't affect editors
+ that have already been created).
+
CodeMirror.defineExtension (name: string, value: any)
If you want to define extra methods in terms of the
CodeMirror API, it is possible to
@@ -1460,6 +1501,7 @@ Add-ons
prompt (should include an input tag), and a
callback function that is called when text has been entered.
Depends on addon/dialog/dialog.css.
+
search/searchcursor.js
Adds the getSearchCursor(query, start, caseFold) →
cursor method to CodeMirror instances, which can be used
@@ -1498,6 +1540,7 @@ Add-ons
on searchcursor.js, and will make use
of openDialog when
available to make prompting for search queries less ugly.
+
edit/matchbrackets.js
Defines an option matchBrackets which, when set
to true, causes matching brackets to be highlighted whenever the
@@ -1506,6 +1549,7 @@ Add-ons
once, and a method findMatchingBracket that can be
used to run the bracket-finding algorithm that this uses
internally.
+
edit/closebrackets.js
Defines an option autoCloseBrackets that will
auto-close brackets and quotes when typed. By default, it'll
@@ -1513,6 +1557,50 @@ Add-ons
string similar to that (containing pairs of matching characters)
to customize it. Demo
here .
+
+
+ Addon for commenting and uncommenting code. Adds three
+ methods to CodeMirror instances:
+
+
+ Set the lines in the given range to be line comments. Will
+ fall back to blockComment when no line comment
+ style is defined for the mode.
+
+ Wrap the code in the given range in a block comment. Will
+ fall back to lineComment when no block comment
+ style is defined for the mode.
+
+ Try to uncomment the given range.
+ Returns true if a comment range was found and
+ removed, false otherwise.
+
+ The options object accepted by these methods may
+ have the following properties:
+
+ blockCommentStart, blockCommentEnd, blockCommentLead, lineComment: string
+ Override the comment string
+ properties of the mode with custom comment strings.
+ padding
+ A string that will be inserted after opening and before
+ closing comment markers. Defaults to a single space.
+ commentBlankLines
+ Whether, when adding line comments, to also comment lines
+ that contain only whitespace.
+ indent
+ When adding line comments and this is turned on, it will
+ align the comment block to the current indentation of the
+ first line of the block.
+ fullLines
+ When block commenting, this controls whether the whole
+ lines are indented, or only the precise range that is given.
+ Defaults to true.
+
+ The addon also defines
+ a toggleComment command ,
+ which will try to uncomment the current selection, and if that
+ fails, line-comments it.
+
fold/foldcode.js
Helps with code folding.
See the demo for an example.
@@ -1530,6 +1618,7 @@ Add-ons
where indentation determines block structure (Python, Haskell),
and CodeMirror.tagRangeFinder, for XML-style
languages.
+
runmode/runmode.js
Can be used to run a CodeMirror mode over text without
actually opening an editor instance.
@@ -1539,6 +1628,7 @@ Add-ons
(without including all of CodeMirror) and
for running under
node.js .
+
mode/overlay.js
Mode combinator that can be used to extend a mode with an
'overlay' — a secondary mode is run over the stream, along with
@@ -1547,6 +1637,7 @@ Add-ons
Defines CodeMirror.overlayMode, which is used to
create such a mode. See this
demo for a detailed example.
+
mode/multiplex.js
Mode combinator that can be used to easily 'multiplex'
between several modes.
@@ -1566,6 +1657,7 @@ Add-ons
see the content between the delimiters.
See this demo for an
example.
+
hint/show-hint.js
Provides a framework for showing autocompletion hints.
Defines CodeMirror.showHint, which takes a
@@ -1581,11 +1673,13 @@ Add-ons
hint sources for various languages. Check
out the demo for an
example.
+
match-highlighter.js
Adds a highlightSelectionMatches option that
can be enabled to highlight all instances of a currently
selected word.
Demo here .
+
lint/lint.js
Defines an interface component for showing linting warnings,
with pluggable warning sources
@@ -1596,20 +1690,24 @@ Add-ons
example CodeMirror.javascriptValidator). Depends
on addon/lint/lint.css. A demo can be
found here .
+
selection/mark-selection.js
Causes the selected text to be marked with the CSS class
CodeMirror-selectedtext when the styleSelectedText option
is enabled. Useful to change the colour of the selection (in addition to the background),
like in this demo .
+
selection/active-line.js
Defines a styleActiveLine option that, when enabled,
gives the wrapper of the active line the class CodeMirror-activeline,
and adds a background with the class CodeMirror-activeline-background.
is enabled. See the demo .
+
edit/closetag.js
Provides utility functions for adding automatic tag closing
to XML modes. See
the demo .
+
mode/loadmode.js
Defines a CodeMirror.requireMode(modename,
callback) function that will try to load a given mode and
@@ -1622,12 +1720,14 @@ Add-ons
which will ensure the given mode is loaded and cause the given
editor instance to refresh its mode when the loading
succeeded. See the demo .
+
Adds an continueComments option, which can be
set to true to have the editor prefix new lines inside C-like
block comments with an asterisk when Enter is pressed. It can
also be set to a string in order to bind this functionality to a
specific key..
+
display/placeholder.js
Adds a placeholder option that can be used to
make text appear in the editor when it is empty and not focused.
@@ -1792,6 +1892,15 @@ Writing CodeMirror Modes
return CodeMirror.Pass to indicate that it
could not come up with a precise indentation.
+
+
Finally, a mode may define
an electricChars property, which should hold a string
containing all the characters that should trigger the behaviour
@@ -1870,6 +1979,7 @@
Writing CodeMirror Modes
Customized Styling
Programming API
+ Constructor
Content manipulation
Selection
Configuration
diff --git a/doc/modes.html b/doc/modes.html
index d016aca8ee..af798aa774 100644
--- a/doc/modes.html
+++ b/doc/modes.html
@@ -27,6 +27,7 @@
Asterisk dialplan
C, C++, C#
Clojure
+ COBOL
CoffeeScript
Common Lisp
CSS
@@ -37,6 +38,7 @@
Gas (AT&T-style assembly)
Go
Groovy
+ HAML
Haskell
Haxe
HTML embedded scripts
diff --git a/doc/oldrelease.html b/doc/oldrelease.html
index b51e93c328..dd5d8e8c1d 100644
--- a/doc/oldrelease.html
+++ b/doc/oldrelease.html
@@ -19,6 +19,34 @@
+ 20-11-2012: Version 3.0, release candidate 1 :
+
+
+
+ 22-10-2012: Version 2.35 :
+
+
+ New (sub) mode: TypeScript .
+ Don't overwrite (insert key) when pasting.
+ Fix several bugs in markText /undo interaction.
+ Better indentation of JavaScript code without semicolons.
+ Add defineInitHook function.
+ Full list of patches .
+
+
22-10-2012: Version 3.0, beta 2 :
diff --git a/doc/realworld.html b/doc/realworld.html
index 576a003de0..f90b0f5a13 100644
--- a/doc/realworld.html
+++ b/doc/realworld.html
@@ -25,17 +25,21 @@
Adobe Brackets (code editor)
Amber (JavaScript-based Smalltalk system)
APEye (tool for testing & documenting APIs)
+ Bitbucket (code hosting)
Blogger's template editor
BlueGriffon (HTML editor)
Cargo Collective (creative publishing platform)
+ ClickHelp (technical writing tool)
Code per Node (Drupal module)
Codebug (PHP Xdebug front-end)
+ CodeMirror Eclipse (embed CM in Eclipse)
CodeMirror movie (scripted editing demos)
CodeMirror2-GWT (Google Web Toolkit wrapper)
Code Monster & Code Maven (learning environment)
Codepen (gallery of animations)
Code School (online tech learning environment)
Code Snippets (WordPress snippet management plugin)
+ Code together (collaborative editing)
Codev (collaborative IDE)
Collaborative CodeMirror demo (CodeMirror + operational transforms)
Community Code Camp (code snippet sharing)
@@ -54,13 +58,16 @@
GitHub's Android app
Google Apps Script
Graphit (function graphing)
+ Handcraft 2 (HTML prototyping)
Haxe (Haxe Playground)
HaxPad (editor for Win RT)
Histone template engine playground
ICEcoder (web IDE)
+ Janvas (vector graphics editor)
Joomla plugin
jQuery fundamentals (interactive tutorial)
jsbin.com (JS playground)
+ jsfiddle.com (another JS playground)
JSHint (JS linter)
Jumpseller (online store builder)
kl1p (paste service)
@@ -69,7 +76,9 @@
MIHTool (iOS web-app debugging tool)
Mongo MapReduce WebBrowser
My2ndGeneration (social coding)
+ Navigate CMS
NoTex (rST authoring)
+ Oak (online outliner)
ORG (z80 assembly IDE)
Orion-CodeMirror integration (running CodeMirror modes in Orion)
Paper.js (graphics scripting)
@@ -81,6 +90,7 @@
RealTime.io (Internet-of-Things infrastructure)
sketchPatch Livecodelab
Skulpt (in-browser Python environment)
+ Snippets.pro (code snippet sharing)
SolidShops (hosted e-commerce platform)
SQLFiddle (SQL playground)
The File Tree (collab editor)
@@ -89,6 +99,7 @@
Tumblr code highlighting shim
TurboPY (web publishing framework)
UmpleOnline (model-oriented programming tool)
+ Upsource (code viewer)
WebGL playground
WeScheme (learning tool)
WordPress plugin
diff --git a/index.html b/index.html
index 1dba41984b..0f855ecbb3 100644
--- a/index.html
+++ b/index.html
@@ -120,28 +120,29 @@ Usage demos:
Real-world uses:
@@ -286,6 +287,16 @@ Commercial support
Releases
+ 20-05-2013: Version 3.13 :
+
+
+
19-04-2013: Version 3.12 :
- 20-11-2012: Version 3.0, release candidate 1 :
-
-
-
- 22-10-2012: Version 2.35 :
-
-
- New (sub) mode: TypeScript .
- Don't overwrite (insert key) when pasting.
- Fix several bugs in markText /undo interaction.
- Better indentation of JavaScript code without semicolons.
- Add defineInitHook function.
- Full list of patches .
-
-
Older releases...
diff --git a/keymap/vim.js b/keymap/vim.js
index 499601b5b0..42e6ecaac0 100644
--- a/keymap/vim.js
+++ b/keymap/vim.js
@@ -58,36 +58,36 @@
var defaultKeymap = [
// Key to key mapping. This goes first to make it possible to override
// existing mappings.
- { keys: ['Left'], type: 'keyToKey', toKeys: ['h'] },
- { keys: ['Right'], type: 'keyToKey', toKeys: ['l'] },
- { keys: ['Up'], type: 'keyToKey', toKeys: ['k'] },
- { keys: ['Down'], type: 'keyToKey', toKeys: ['j'] },
- { keys: ['Space'], type: 'keyToKey', toKeys: ['l'] },
- { keys: ['Backspace'], type: 'keyToKey', toKeys: ['h'] },
- { keys: ['Ctrl-Space'], type: 'keyToKey', toKeys: ['W'] },
- { keys: ['Ctrl-Backspace'], type: 'keyToKey', toKeys: ['B'] },
- { keys: ['Shift-Space'], type: 'keyToKey', toKeys: ['w'] },
- { keys: ['Shift-Backspace'], type: 'keyToKey', toKeys: ['b'] },
- { keys: ['Ctrl-n'], type: 'keyToKey', toKeys: ['j'] },
- { keys: ['Ctrl-p'], type: 'keyToKey', toKeys: ['k'] },
- { keys: ['Ctrl-['], type: 'keyToKey', toKeys: ['Esc'] },
- { keys: ['Ctrl-c'], type: 'keyToKey', toKeys: ['Esc'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['h'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['l'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['k'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['j'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['l'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['h'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['W'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['B'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['w'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['b'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['j'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['k'] },
+ { keys: ['C-['], type: 'keyToKey', toKeys: [''] },
+ { keys: [''], type: 'keyToKey', toKeys: [''] },
{ keys: ['s'], type: 'keyToKey', toKeys: ['c', 'l'] },
{ keys: ['S'], type: 'keyToKey', toKeys: ['c', 'c'] },
- { keys: ['Home'], type: 'keyToKey', toKeys: ['0'] },
- { keys: ['End'], type: 'keyToKey', toKeys: ['$'] },
- { keys: ['PageUp'], type: 'keyToKey', toKeys: ['Ctrl-b'] },
- { keys: ['PageDown'], type: 'keyToKey', toKeys: ['Ctrl-f'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['0'] },
+ { keys: [''], type: 'keyToKey', toKeys: ['$'] },
+ { keys: [''], type: 'keyToKey', toKeys: [''] },
+ { keys: [''], type: 'keyToKey', toKeys: [''] },
// Motions
{ keys: ['H'], type: 'motion',
motion: 'moveToTopLine',
- motionArgs: { linewise: true }},
+ motionArgs: { linewise: true, toJumplist: true }},
{ keys: ['M'], type: 'motion',
motion: 'moveToMiddleLine',
- motionArgs: { linewise: true }},
+ motionArgs: { linewise: true, toJumplist: true }},
{ keys: ['L'], type: 'motion',
motion: 'moveToBottomLine',
- motionArgs: { linewise: true }},
+ motionArgs: { linewise: true, toJumplist: true }},
{ keys: ['h'], type: 'motion',
motion: 'moveByCharacters',
motionArgs: { forward: false }},
@@ -133,25 +133,25 @@
motionArgs: { forward: false, wordEnd: true, bigWord: true,
inclusive: true }},
{ keys: ['{'], type: 'motion', motion: 'moveByParagraph',
- motionArgs: { forward: false }},
+ motionArgs: { forward: false, toJumplist: true }},
{ keys: ['}'], type: 'motion', motion: 'moveByParagraph',
- motionArgs: { forward: true }},
- { keys: ['Ctrl-f'], type: 'motion',
+ motionArgs: { forward: true, toJumplist: true }},
+ { keys: [''], type: 'motion',
motion: 'moveByPage', motionArgs: { forward: true }},
- { keys: ['Ctrl-b'], type: 'motion',
+ { keys: [''], type: 'motion',
motion: 'moveByPage', motionArgs: { forward: false }},
- { keys: ['Ctrl-d'], type: 'motion',
+ { keys: [''], type: 'motion',
motion: 'moveByScroll',
motionArgs: { forward: true, explicitRepeat: true }},
- { keys: ['Ctrl-u'], type: 'motion',
+ { keys: [''], type: 'motion',
motion: 'moveByScroll',
motionArgs: { forward: false, explicitRepeat: true }},
{ keys: ['g', 'g'], type: 'motion',
motion: 'moveToLineOrEdgeOfDocument',
- motionArgs: { forward: false, explicitRepeat: true, linewise: true }},
+ motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
{ keys: ['G'], type: 'motion',
motion: 'moveToLineOrEdgeOfDocument',
- motionArgs: { forward: true, explicitRepeat: true, linewise: true }},
+ motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
{ keys: ['0'], type: 'motion', motion: 'moveToStartOfLine' },
{ keys: ['^'], type: 'motion',
motion: 'moveToFirstNonWhiteSpaceCharacter' },
@@ -169,7 +169,7 @@
motionArgs: { inclusive: true }},
{ keys: ['%'], type: 'motion',
motion: 'moveToMatchedSymbol',
- motionArgs: { inclusive: true }},
+ motionArgs: { inclusive: true, toJumplist: true }},
{ keys: ['f', 'character'], type: 'motion',
motion: 'moveToCharacter',
motionArgs: { forward: true , inclusive: true }},
@@ -186,18 +186,20 @@
motionArgs: { forward: true }},
{ keys: [','], type: 'motion', motion: 'repeatLastCharacterSearch',
motionArgs: { forward: false }},
- { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark' },
- { keys: ['`', 'character'], type: 'motion', motion: 'goToMark' },
+ { keys: ['\'', 'character'], type: 'motion', motion: 'goToMark',
+ motionArgs: {toJumplist: true}},
+ { keys: ['`', 'character'], type: 'motion', motion: 'goToMark',
+ motionArgs: {toJumplist: true}},
{ keys: [']', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
{ keys: ['[', '`',], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
{ keys: [']', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
{ keys: ['[', '\''], type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
{ keys: [']', 'character'], type: 'motion',
motion: 'moveToSymbol',
- motionArgs: { forward: true}},
+ motionArgs: { forward: true, toJumplist: true}},
{ keys: ['[', 'character'], type: 'motion',
motion: 'moveToSymbol',
- motionArgs: { forward: false}},
+ motionArgs: { forward: false, toJumplist: true}},
{ keys: ['|'], type: 'motion',
motion: 'moveToColumn',
motionArgs: { }},
@@ -212,9 +214,9 @@
operatorArgs: { indentRight: false }},
{ keys: ['g', '~'], type: 'operator', operator: 'swapcase' },
{ keys: ['n'], type: 'motion', motion: 'findNext',
- motionArgs: { forward: true }},
+ motionArgs: { forward: true, toJumplist: true }},
{ keys: ['N'], type: 'motion', motion: 'findNext',
- motionArgs: { forward: false }},
+ motionArgs: { forward: false, toJumplist: true }},
// Operator-Motion dual commands
{ keys: ['x'], type: 'operatorMotion', operator: 'delete',
motion: 'moveByCharacters', motionArgs: { forward: true },
@@ -235,13 +237,18 @@
{ keys: ['~'], type: 'operatorMotion', operator: 'swapcase',
motion: 'moveByCharacters', motionArgs: { forward: true }},
// Actions
+ { keys: [''], type: 'action', action: 'jumpListWalk',
+ actionArgs: { forward: true }},
+ { keys: [''], type: 'action', action: 'jumpListWalk',
+ actionArgs: { forward: false }},
{ keys: ['a'], type: 'action', action: 'enterInsertMode',
actionArgs: { insertAt: 'charAfter' }},
{ keys: ['A'], type: 'action', action: 'enterInsertMode',
actionArgs: { insertAt: 'eol' }},
- { keys: ['i'], type: 'action', action: 'enterInsertMode' },
+ { keys: ['i'], type: 'action', action: 'enterInsertMode',
+ actionArgs: { insertAt: 'inplace' }},
{ keys: ['I'], type: 'action', action: 'enterInsertMode',
- motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ actionArgs: { insertAt: 'firstNonBlank' }},
{ keys: ['o'], type: 'action', action: 'newLineAndEnterInsertMode',
actionArgs: { after: true }},
{ keys: ['O'], type: 'action', action: 'newLineAndEnterInsertMode',
@@ -255,8 +262,11 @@
{ keys: ['P'], type: 'action', action: 'paste',
actionArgs: { after: false }},
{ keys: ['r', 'character'], type: 'action', action: 'replace' },
+ { keys: ['@', 'character'], type: 'action', action: 'replayMacro' },
+ { keys: ['q', 'character'], type: 'action', action: 'enterMacroRecordMode' },
+ { keys: ['R'], type: 'action', action: 'enterReplaceMode' },
{ keys: ['u'], type: 'action', action: 'undo' },
- { keys: ['Ctrl-r'], type: 'action', action: 'redo' },
+ { keys: [''], type: 'action', action: 'redo' },
{ keys: ['m', 'character'], type: 'action', action: 'setMark' },
{ keys: ['\"', 'character'], type: 'action', action: 'setRegister' },
{ keys: ['z', 'z'], type: 'action', action: 'scrollToCursor',
@@ -266,7 +276,7 @@
motion: 'moveToFirstNonWhiteSpaceCharacter' },
{ keys: ['z', 't'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'top' }},
- { keys: ['z', 'Enter'], type: 'action', action: 'scrollToCursor',
+ { keys: ['z', ''], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'top' },
motion: 'moveToFirstNonWhiteSpaceCharacter' },
{ keys: ['z', '-'], type: 'action', action: 'scrollToCursor',
@@ -275,9 +285,9 @@
actionArgs: { position: 'bottom' },
motion: 'moveToFirstNonWhiteSpaceCharacter' },
{ keys: ['.'], type: 'action', action: 'repeatLastEdit' },
- { keys: ['Ctrl-a'], type: 'action', action: 'incrementNumberToken',
+ { keys: [''], type: 'action', action: 'incrementNumberToken',
actionArgs: {increase: true, backtrack: false}},
- { keys: ['Ctrl-x'], type: 'action', action: 'incrementNumberToken',
+ { keys: [''], type: 'action', action: 'incrementNumberToken',
actionArgs: {increase: false, backtrack: false}},
// Text object motions
{ keys: ['a', 'character'], type: 'motion',
@@ -287,13 +297,13 @@
motionArgs: { textObjectInner: true }},
// Search
{ keys: ['/'], type: 'search',
- searchArgs: { forward: true, querySrc: 'prompt' }},
+ searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
{ keys: ['?'], type: 'search',
- searchArgs: { forward: false, querySrc: 'prompt' }},
+ searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
{ keys: ['*'], type: 'search',
- searchArgs: { forward: true, querySrc: 'wordUnderCursor' }},
+ searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
{ keys: ['#'], type: 'search',
- searchArgs: { forward: false, querySrc: 'wordUnderCursor' }},
+ searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
// Ex command
{ keys: [':'], type: 'ex' }
];
@@ -361,6 +371,91 @@
return false;
}
+ var createCircularJumpList = function() {
+ var size = 100;
+ var pointer = -1;
+ var head = 0;
+ var tail = 0;
+ var buffer = new Array(size);
+ function add(cm, oldCur, newCur) {
+ var current = pointer % size;
+ var curMark = buffer[current];
+ function useNextSlot(cursor) {
+ var next = ++pointer % size;
+ var trashMark = buffer[next];
+ if (trashMark) {
+ trashMark.clear();
+ }
+ buffer[next] = cm.setBookmark(cursor);
+ }
+ if (curMark) {
+ var markPos = curMark.find();
+ // avoid recording redundant cursor position
+ if (markPos && !cursorEqual(markPos, oldCur)) {
+ useNextSlot(oldCur);
+ }
+ } else {
+ useNextSlot(oldCur);
+ }
+ useNextSlot(newCur);
+ head = pointer;
+ tail = pointer - size + 1;
+ if (tail < 0) {
+ tail = 0;
+ }
+ }
+ function move(cm, offset) {
+ pointer += offset;
+ if (pointer > head) {
+ pointer = head;
+ } else if (pointer < tail) {
+ pointer = tail;
+ }
+ var mark = buffer[(size + pointer) % size];
+ // skip marks that are temporarily removed from text buffer
+ if (mark && !mark.find()) {
+ var inc = offset > 0 ? 1 : -1;
+ var newCur;
+ var oldCur = cm.getCursor();
+ do {
+ pointer += inc;
+ mark = buffer[(size + pointer) % size];
+ // skip marks that are the same as current position
+ if (mark &&
+ (newCur = mark.find()) &&
+ !cursorEqual(oldCur, newCur)) {
+ break;
+ }
+ } while (pointer < head && pointer > tail);
+ }
+ return mark;
+ }
+ return {
+ cachedCursor: undefined, //used for # and * jumps
+ add: add,
+ move: move
+ };
+ };
+
+ var createMacroState = function() {
+ return {
+ macroKeyBuffer: [],
+ latestRegister: undefined,
+ enteredMacroMode: undefined,
+ isMacroPlaying: false,
+ toggle: function(cm, registerName) {
+ if (this.enteredMacroMode) { //onExit
+ this.enteredMacroMode(); // close dialog
+ this.enteredMacroMode = undefined;
+ } else { //onEnter
+ this.latestRegister = registerName;
+ this.enteredMacroMode = cm.openDialog(
+ '(recording)['+registerName+']', null, {bottom:true});
+ }
+ }
+ }
+ }
+
// Global Vim state. Call getVimGlobalState to get and initialize.
var vimGlobalState;
function getVimGlobalState() {
@@ -370,6 +465,8 @@
searchQuery: null,
// Whether we are searching backwards.
searchIsReversed: false,
+ jumpList: createCircularJumpList(),
+ macroModeState: createMacroState(),
// Recording latest f, t, F or T motion command.
lastChararacterSearch: {increment:0, forward:true, selectedCharacter:''},
registerController: new RegisterController({})
@@ -435,7 +532,15 @@
handleKey: function(cm, key) {
var command;
var vim = getVimState(cm);
- if (key == 'Esc') {
+ var macroModeState = getVimGlobalState().macroModeState;
+ if (macroModeState.enteredMacroMode) {
+ if (key == 'q') {
+ actions.exitMacroRecordMode();
+ return;
+ }
+ logKey(macroModeState, key);
+ }
+ if (key == '') {
// Clear input state and get back to normal mode.
vim.inputState = new InputState();
if (vim.visualMode) {
@@ -597,6 +702,9 @@
this.unamedRegister.set(text, linewise);
}
},
+ setRegisterText: function(name, text, linewise) {
+ this.getRegister(name).set(text, linewise);
+ },
// Gets the register named @name. If one of @name doesn't already exist,
// create it. If @name is invalid, return the unamedRegister.
getRegister: function(name) {
@@ -643,10 +751,10 @@
inputState.selectedCharacter = keys[keys.length - 1];
if(inputState.selectedCharacter.length>1){
switch(inputState.selectedCharacter){
- case "Enter":
+ case "":
inputState.selectedCharacter='\n';
break;
- case "Space":
+ case "":
inputState.selectedCharacter=' ';
break;
default:
@@ -769,13 +877,13 @@
try {
updateSearchQuery(cm, query, ignoreCase, smartCase);
} catch (e) {
- showConfirm(cm, 'Invalid regex: ' + regexPart);
+ showConfirm(cm, 'Invalid regex: ' + query);
return;
}
commandDispatcher.processMotion(cm, vim, {
type: 'motion',
motion: 'findNext',
- motionArgs: { forward: true }
+ motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }
});
}
function onPromptClose(query) {
@@ -834,13 +942,19 @@
return;
}
var query = cm.getLine(word.start.line).substring(word.start.ch,
- word.end.ch + 1);
+ word.end.ch);
if (isKeyword) {
query = '\\b' + query + '\\b';
} else {
query = escapeRegex(query);
}
+
+ // cachedCursor is used to save the old position of the cursor
+ // when * or # causes vim to seek for the nearest word and shift
+ // the cursor before entering the motion.
+ getVimGlobalState().jumpList.cachedCursor = cm.getCursor();
cm.setCursor(word.start);
+
handleQuery(query, true /** ignoreCase */, false /** smartCase */);
break;
}
@@ -917,6 +1031,17 @@
if (!motionResult) {
return;
}
+ if (motionArgs.toJumplist) {
+ var jumpList = getVimGlobalState().jumpList;
+ // if the current motion is # or *, use cachedCursor
+ var cachedCursor = jumpList.cachedCursor;
+ if (cachedCursor) {
+ recordJumpPosition(cm, cachedCursor, motionResult);
+ delete jumpList.cachedCursor;
+ } else {
+ recordJumpPosition(cm, curOriginal, motionResult);
+ }
+ }
if (motionResult instanceof Array) {
curStart = motionResult[0];
curEnd = motionResult[1];
@@ -992,7 +1117,7 @@
// Expand selection to entire line.
expandSelectionToLine(cm, curStart, curEnd);
} else if (motionArgs.forward) {
- // Clip to trailing newlines only if we the motion goes forward.
+ // Clip to trailing newlines only if the motion goes forward.
clipToLine(cm, curStart, curEnd);
}
operatorArgs.registerName = registerName;
@@ -1077,7 +1202,7 @@
}
var equal = cursorEqual(cursor, best);
- var between = (motionArgs.forward) ?
+ var between = (motionArgs.forward) ?
cusrorIsBetween(cursor, mark, best) :
cusrorIsBetween(best, mark, cursor);
@@ -1255,9 +1380,24 @@
},
moveToMatchedSymbol: function(cm, motionArgs) {
var cursor = cm.getCursor();
- var symbol = cm.getLine(cursor.line).charAt(cursor.ch);
- if (isMatchableSymbol(symbol)) {
- return findMatchedSymbol(cm, cm.getCursor(), motionArgs.symbol);
+ var line = cursor.line;
+ var ch = cursor.ch;
+ var lineText = cm.getLine(line);
+ var symbol;
+ var startContext = cm.getTokenAt(cursor).type;
+ var startCtxLevel = getContextLevel(startContext);
+ do {
+ symbol = lineText.charAt(ch++);
+ if (symbol && isMatchableSymbol(symbol)) {
+ var endContext = cm.getTokenAt({line:line, ch:ch}).type;
+ var endCtxLevel = getContextLevel(endContext);
+ if (startCtxLevel >= endCtxLevel) {
+ break;
+ }
+ }
+ } while (symbol);
+ if (symbol) {
+ return findMatchedSymbol(cm, {line:line, ch:ch-1}, symbol);
} else {
return cursor;
}
@@ -1383,24 +1523,58 @@
};
var actions = {
+ jumpListWalk: function(cm, actionArgs, vim) {
+ if (vim.visualMode) {
+ return;
+ }
+ var repeat = actionArgs.repeat;
+ var forward = actionArgs.forward;
+ var jumpList = getVimGlobalState().jumpList;
+
+ var mark = jumpList.move(cm, forward ? repeat : -repeat);
+ var markPos = mark ? mark.find() : undefined;
+ markPos = markPos ? markPos : cm.getCursor();
+ cm.setCursor(markPos);
+ },
scrollToCursor: function(cm, actionArgs) {
var lineNum = cm.getCursor().line;
- var heightProp = window.getComputedStyle(cm.getScrollerElement()).
- getPropertyValue('height');
- var height = parseInt(heightProp);
- var y = cm.charCoords({line: lineNum, ch: 0}, "local").top;
- var halfHeight = parseInt(height) / 2;
+ var charCoords = cm.charCoords({line: lineNum, ch: 0}, "local");
+ var height = cm.getScrollInfo().clientHeight;
+ var y = charCoords.top;
+ var lineHeight = charCoords.bottom - y;
switch (actionArgs.position) {
- case 'center': y = y - (height / 2) + 10;
- break;
- case 'bottom': y = y - height;
- break;
- case 'top': break;
+ case 'center': y = y - (height / 2) + lineHeight;
+ break;
+ case 'bottom': y = y - height + lineHeight*1.4;
+ break;
+ case 'top': y = y + lineHeight*0.4;
+ break;
}
cm.scrollTo(null, y);
- // The calculations are slightly off, use scrollIntoView to nudge the
- // view into the right place.
- cm.scrollIntoView();
+ },
+ replayMacro: function(cm, actionArgs) {
+ var registerName = actionArgs.selectedCharacter;
+ var repeat = actionArgs.repeat;
+ var macroModeState = getVimGlobalState().macroModeState;
+ if (registerName == '@') {
+ registerName = macroModeState.latestRegister;
+ }
+ var keyBuffer = parseRegisterToKeyBuffer(macroModeState, registerName);
+ while(repeat--){
+ executeMacroKeyBuffer(cm, macroModeState, keyBuffer);
+ }
+ },
+ exitMacroRecordMode: function(cm, actionArgs) {
+ var macroModeState = getVimGlobalState().macroModeState;
+ macroModeState.toggle();
+ parseKeyBufferToRegister(macroModeState.latestRegister,
+ macroModeState.macroKeyBuffer);
+ },
+ enterMacroRecordMode: function(cm, actionArgs) {
+ var macroModeState = getVimGlobalState().macroModeState;
+ var registerName = actionArgs.selectedCharacter;
+ macroModeState.toggle(cm, registerName);
+ emptyMacroKeyBuffer(macroModeState);
},
enterInsertMode: function(cm, actionArgs) {
var insertAt = (actionArgs) ? actionArgs.insertAt : null;
@@ -1410,6 +1584,8 @@
cm.setCursor(cursor);
} else if (insertAt == 'charAfter') {
cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
+ } else if (insertAt == 'firstNonBlank') {
+ cm.setCursor(motions.moveToFirstNonWhiteSpaceCharacter(cm));
}
cm.setOption('keyMap', 'vim-insert');
},
@@ -1605,6 +1781,10 @@
}
}
},
+ enterReplaceMode: function(cm, actionArgs) {
+ cm.setOption('keyMap', 'vim-replace');
+ cm.toggleOverwrite();
+ },
incrementNumberToken: function(cm, actionArgs, vim) {
var cur = cm.getCursor();
var lineStr = cm.getLine(cur.line);
@@ -1804,10 +1984,30 @@
// caret to the first character of the next line.
function clipToLine(cm, curStart, curEnd) {
var selection = cm.getRange(curStart, curEnd);
- var lines = selection.split('\n');
- if (lines.length > 1 && isWhiteSpaceString(lines.pop())) {
- curEnd.line--;
- curEnd.ch = lineLength(cm, curEnd.line);
+ // Only clip if the selection ends with trailing newline + whitespace
+ if (/\n\s*$/.test(selection)) {
+ var lines = selection.split('\n');
+ // We know this is all whitepsace.
+ lines.pop();
+
+ // Cases:
+ // 1. Last word is an empty line - do not clip the trailing '\n'
+ // 2. Last word is not an empty line - clip the trailing '\n'
+ var line;
+ // Find the line containing the last word, and clip all whitespace up
+ // to it.
+ for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
+ var clipped = false;
+ curEnd.line--;
+ curEnd.ch = 0;
+ }
+ // If the last word is not an empty line, clip an additional newline
+ if (line) {
+ curEnd.line--;
+ curEnd.ch = lineLength(cm, curEnd.line);
+ } else {
+ curEnd.ch = 0;
+ }
}
}
@@ -1861,21 +2061,39 @@
var wordAfterRegex = matchRegex.exec(textAfterIdx);
var wordStart = idx;
- var wordEnd = idx + wordAfterRegex[0].length - 1;
+ var wordEnd = idx + wordAfterRegex[0].length;
// TODO: Find a better way to do this. It will be slow on very long lines.
- var wordBeforeRegex = matchRegex.exec(reverse(textBeforeIdx));
+ var revTextBeforeIdx = reverse(textBeforeIdx);
+ var wordBeforeRegex = matchRegex.exec(revTextBeforeIdx);
if (wordBeforeRegex) {
wordStart -= wordBeforeRegex[0].length;
}
if (inclusive) {
- wordEnd++;
+ // If present, trim all whitespace after word.
+ // Otherwise, trim all whitespace before word.
+ var textAfterWordEnd = line.substring(wordEnd);
+ var whitespacesAfterWord = textAfterWordEnd.match(/^\s*/)[0].length;
+ if (whitespacesAfterWord > 0) {
+ wordEnd += whitespacesAfterWord;
+ } else {
+ var revTrim = revTextBeforeIdx.length - wordStart;
+ var textBeforeWordStart = revTextBeforeIdx.substring(revTrim);
+ var whitespacesBeforeWord = textBeforeWordStart.match(/^\s*/)[0].length;
+ wordStart -= whitespacesBeforeWord;
+ }
}
return { start: { line: cur.line, ch: wordStart },
end: { line: cur.line, ch: wordEnd }};
}
+ function recordJumpPosition(cm, oldCur, newCur) {
+ if(!cursorEqual(oldCur, newCur)) {
+ getVimGlobalState().jumpList.add(cm, oldCur, newCur);
+ }
+ }
+
function recordLastCharacterSearch(increment, args) {
var vimGlobalState = getVimGlobalState();
vimGlobalState.lastChararacterSearch.increment = increment;
@@ -2016,18 +2234,31 @@
* backward.
* @param {boolean} bigWord True if punctuation count as part of the word.
* False if only [a-zA-Z0-9] characters count as part of the word.
+ * @param {boolean} emptyLineIsWord True if empty lines should be treated
+ * as words.
* @return {Object{from:number, to:number, line: number}} The boundaries of
* the word, or null if there are no more words.
*/
- // TODO: Treat empty lines (with no whitespace) as words.
- function findWord(cm, cur, forward, bigWord) {
+ function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {
var lineNum = cur.line;
var pos = cur.ch;
var line = cm.getLine(lineNum);
var dir = forward ? 1 : -1;
var regexps = bigWord ? bigWordRegexp : wordRegexp;
+ if (emptyLineIsWord && line == '') {
+ lineNum += dir;
+ line = cm.getLine(lineNum);
+ if (!isLine(cm, lineNum)) {
+ return null;
+ }
+ pos = (forward) ? 0 : line.length;
+ }
+
while (true) {
+ if (emptyLineIsWord && line == '') {
+ return { from: 0, to: 0, line: lineNum };
+ }
var stop = (dir > 0) ? line.length : -1;
var wordStart = stop, wordEnd = stop;
// Find bounds of next word.
@@ -2083,56 +2314,48 @@
*/
function moveToWord(cm, repeat, forward, wordEnd, bigWord) {
var cur = cm.getCursor();
+ var curStart = copyCursor(cur);
+ var words = [];
+ if (forward && !wordEnd || !forward && wordEnd) {
+ repeat++;
+ }
+ // For 'e', empty lines are not considered words, go figure.
+ var emptyLineIsWord = !(forward && wordEnd);
for (var i = 0; i < repeat; i++) {
- var startCh = cur.ch, startLine = cur.line, word;
- var movedToNextWord = false;
- while (!movedToNextWord) {
- // Search and advance.
- word = findWord(cm, cur, forward, bigWord);
- movedToNextWord = true;
- if (word) {
- // Move to the word we just found. If by moving to the word we end
- // up in the same spot, then move an extra character and search
- // again.
- cur.line = word.line;
- if (forward && wordEnd) {
- // 'e'
- cur.ch = word.to - 1;
- } else if (forward && !wordEnd) {
- // 'w'
- if (inRangeInclusive(cur.ch, word.from, word.to) &&
- word.line == startLine) {
- // Still on the same word. Go to the next one.
- movedToNextWord = false;
- cur.ch = word.to - 1;
- } else {
- cur.ch = word.from;
- }
- } else if (!forward && wordEnd) {
- // 'ge'
- if (inRangeInclusive(cur.ch, word.from, word.to) &&
- word.line == startLine) {
- // still on the same word. Go to the next one.
- movedToNextWord = false;
- cur.ch = word.from;
- } else {
- cur.ch = word.to;
- }
- } else if (!forward && !wordEnd) {
- // 'b'
- cur.ch = word.from;
- }
- } else {
- // No more words to be found. Move to the end.
- if (forward) {
- return { line: cur.line, ch: lineLength(cm, cur.line) };
- } else {
- return { line: cur.line, ch: 0 };
- }
- }
- }
+ var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);
+ if (!word) {
+ var eodCh = lineLength(cm, cm.lastLine());
+ words.push(forward
+ ? {line: cm.lastLine(), from: eodCh, to: eodCh}
+ : {line: 0, from: 0, to: 0});
+ break;
+ }
+ words.push(word);
+ cur = {line: word.line, ch: forward ? (word.to - 1) : word.from};
+ }
+ var shortCircuit = words.length != repeat;
+ var firstWord = words[0];
+ var lastWord = words.pop();
+ if (forward && !wordEnd) {
+ // w
+ if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {
+ // We did not start in the middle of a word. Discard the extra word at the end.
+ lastWord = words.pop();
+ }
+ return {line: lastWord.line, ch: lastWord.from};
+ } else if (forward && wordEnd) {
+ return {line: lastWord.line, ch: lastWord.to - 1};
+ } else if (!forward && wordEnd) {
+ // ge
+ if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {
+ // We did not start in the middle of a word. Discard the extra word at the end.
+ lastWord = words.pop();
+ }
+ return {line: lastWord.line, ch: lastWord.to};
+ } else {
+ // b
+ return {line: lastWord.line, ch: lastWord.from};
}
- return cur;
}
function moveToCharacter(cm, repeat, forward, character) {
@@ -2188,9 +2411,17 @@
return idx;
}
+ function getContextLevel(ctx) {
+ return (ctx === 'string' || ctx === 'comment') ? 1 : 0;
+ }
+
function findMatchedSymbol(cm, cur, symb) {
var line = cur.line;
- symb = symb ? symb : cm.getLine(line).charAt(cur.ch);
+ var ch = cur.ch;
+ symb = symb ? symb : cm.getLine(line).charAt(ch);
+
+ var symbContext = cm.getTokenAt({line:line, ch:ch+1}).type;
+ var symbCtxLevel = getContextLevel(symbContext);
var reverseSymb = ({
'(': ')', ')': '(',
@@ -2206,7 +2437,7 @@
// depending on which bracket we're matching
var increment = ({'(': 1, '{': 1, '[': 1})[symb] || -1;
var endLine = increment === 1 ? cm.lineCount() : -1;
- var depth = 1, nextCh = symb, index = cur.ch, lineText = cm.getLine(line);
+ var depth = 1, nextCh = symb, index = ch, lineText = cm.getLine(line);
// Simple search for closing paren--just count openings and closings till
// we find our match
// TODO: use info from CodeMirror to ignore closing brackets in comments
@@ -2225,10 +2456,14 @@
}
nextCh = lineText.charAt(index);
}
- if (nextCh === symb) {
- depth++;
- } else if (nextCh === reverseSymb) {
- depth--;
+ var revSymbContext = cm.getTokenAt({line:line, ch:index+1}).type;
+ var revSymbCtxLevel = getContextLevel(revSymbContext);
+ if (symbCtxLevel >= revSymbCtxLevel) {
+ if (nextCh === symb) {
+ depth++;
+ } else if (nextCh === reverseSymb) {
+ depth--;
+ }
}
}
@@ -2352,7 +2587,7 @@
onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp });
}
else {
- callback(prompt(shortText, ""));
+ onClose(prompt(shortText, ""));
}
}
function findUnescapedSlashes(str) {
@@ -2479,7 +2714,7 @@
if (match[0].length == 0) {
// Matched empty string, skip to next.
stream.next();
- return;
+ return "searching";
}
if (!stream.sol()) {
// Backtrack 1 to match \b
@@ -2515,12 +2750,11 @@
if (repeat === undefined) { repeat = 1; }
return cm.operation(function() {
var pos = cm.getCursor();
- if (!prev) {
- pos.ch += 1;
- }
var cursor = cm.getSearchCursor(query, pos);
for (var i = 0; i < repeat; i++) {
- if (!cursor.find(prev)) {
+ var found = cursor.find(prev);
+ if (i == 0 && found && cursorEqual(cursor.from(), pos)) { found = cursor.find(prev); }
+ if (!found) {
// SearchCursor may have returned null because it hit EOF, wrap
// around and try again.
cursor = cm.getSearchCursor(query,
@@ -2751,41 +2985,14 @@
// Converts a key string sequence of the form abd into Vim's
// keymap representation.
function parseKeyString(str) {
- var idx = 0;
+ var key, match;
var keys = [];
- while (idx < str.length) {
- if (str.charAt(idx) != '<') {
- keys.push(str.charAt(idx));
- idx++;
- continue;
- }
- // Vim key notation here means desktop Vim key-notation.
- // See :help key-notation in desktop Vim.
- var vimKeyNotationStart = ++idx;
- while (str.charAt(idx++) != '>') {}
- var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1);
- var mod='';
- var match = (/^C-(.+)$/).exec(vimKeyNotation);
- if (match) {
- mod='Ctrl-';
- vimKeyNotation=match[1];
- }
- var key;
- switch (vimKeyNotation) {
- case 'BS':
- key = 'Backspace';
- break;
- case 'CR':
- key = 'Enter';
- break;
- case 'Del':
- key = 'Delete';
- break;
- default:
- key = vimKeyNotation;
- break;
- }
- keys.push(mod + key);
+ while (str) {
+ match = (/<\w+-.+?>|<\w+>|./).exec(str);
+ if(match === null)break;
+ key = match[0];
+ str = str.substring(match.index + key.length);
+ keys.push(key);
}
return keys;
}
@@ -2956,33 +3163,37 @@
* modifers.
*/
// TODO: Figure out a way to catch capslock.
- function handleKeyEvent_(cm, key, modifier) {
- if (isUpperCase(key)) {
+ function cmKeyToVimKey(key, modifier) {
+ var vimKey = key;
+ if (isUpperCase(vimKey)) {
// Convert to lower case if shift is not the modifier since the key
// we get from CodeMirror is always upper case.
if (modifier == 'Shift') {
modifier = null;
}
else {
- key = key.toLowerCase();
+ vimKey = vimKey.toLowerCase();
}
}
if (modifier) {
// Vim will parse modifier+key combination as a single key.
- key = modifier + '-' + key;
+ vimKey = modifier.charAt(0) + '-' + vimKey;
}
- vim.handleKey(cm, key);
+ var specialKey = ({Enter:'CR',Backspace:'BS',Delete:'Del'})[vimKey];
+ vimKey = specialKey ? specialKey : vimKey;
+ vimKey = vimKey.length > 1 ? '<'+ vimKey + '>' : vimKey;
+ return vimKey;
}
// Closure to bind CodeMirror, key, modifier.
- function keyMapper(key, modifier) {
+ function keyMapper(vimKey) {
return function(cm) {
- handleKeyEvent_(cm, key, modifier);
+ vim.handleKey(cm, vimKey);
};
}
var modifiers = ['Shift', 'Ctrl'];
- var keyMap = {
+ var cmToVimKeymap = {
'nofallthrough': true,
'style': 'fat-cursor'
};
@@ -2994,11 +3205,9 @@
// them.
key = "'" + key + "'";
}
- if (modifier) {
- keyMap[modifier + '-' + key] = keyMapper(keys[i], modifier);
- } else {
- keyMap[key] = keyMapper(keys[i]);
- }
+ var vimKey = cmKeyToVimKey(keys[i], modifier);
+ var cmKey = modifier ? modifier + '-' + key : key;
+ cmToVimKeymap[cmKey] = keyMapper(vimKey);
}
}
bindKeys(upperCaseAlphabet);
@@ -3010,7 +3219,7 @@
bindKeys(numbers, 'Ctrl');
bindKeys(specialKeys);
bindKeys(specialKeys, 'Ctrl');
- return keyMap;
+ return cmToVimKeymap;
}
CodeMirror.keyMap.vim = buildVimKeyMap();
@@ -3035,6 +3244,61 @@
fallthrough: ['default']
};
+ function parseRegisterToKeyBuffer(macroModeState, registerName) {
+ var match, key;
+ var register = getVimGlobalState().registerController.getRegister(registerName);
+ var text = register.toString();
+ var macroKeyBuffer = macroModeState.macroKeyBuffer;
+ emptyMacroKeyBuffer(macroModeState);
+ do {
+ match = text.match(/<\w+-.+>|<\w+>|.|\n/);
+ if(match === null)break;
+ key = match[0];
+ text = text.substring(match.index + key.length);
+ macroKeyBuffer.push(key);
+ } while (text);
+ return macroKeyBuffer;
+ }
+
+ function parseKeyBufferToRegister(registerName, keyBuffer) {
+ var text = keyBuffer.join('');
+ getVimGlobalState().registerController.setRegisterText(registerName, text);
+ }
+
+ function emptyMacroKeyBuffer(macroModeState) {
+ if(macroModeState.isMacroPlaying)return;
+ var macroKeyBuffer = macroModeState.macroKeyBuffer;
+ macroKeyBuffer.length = 0;
+ }
+
+ function executeMacroKeyBuffer(cm, macroModeState, keyBuffer) {
+ macroModeState.isMacroPlaying = true;
+ for (var i = 0, len = keyBuffer.length; i < len; i++) {
+ CodeMirror.Vim.handleKey(cm, keyBuffer[i]);
+ };
+ macroModeState.isMacroPlaying = false;
+ }
+
+ function logKey(macroModeState, key) {
+ if(macroModeState.isMacroPlaying)return;
+ var macroKeyBuffer = macroModeState.macroKeyBuffer;
+ macroKeyBuffer.push(key);
+ }
+
+ function exitReplaceMode(cm) {
+ cm.toggleOverwrite();
+ cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1, true);
+ cm.setOption('keyMap', 'vim');
+ }
+
+ CodeMirror.keyMap['vim-replace'] = {
+ 'Esc': exitReplaceMode,
+ 'Ctrl-[': exitReplaceMode,
+ 'Ctrl-C': exitReplaceMode,
+ 'Backspace': 'goCharLeft',
+ fallthrough: ['default']
+ };
+
return vimApi;
};
// Initialize Vim and make it available as an API.
diff --git a/lib/codemirror.css b/lib/codemirror.css
index 0b53704506..f5379d967c 100644
--- a/lib/codemirror.css
+++ b/lib/codemirror.css
@@ -19,7 +19,7 @@
padding: 0 4px; /* Horizontal padding of content */
}
-.CodeMirror-scrollbar-filler {
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
background-color: white; /* The little square between H and V scrollbars */
}
@@ -28,6 +28,7 @@
.CodeMirror-gutters {
border-right: 1px solid #ddd;
background-color: #f7f7f7;
+ white-space: nowrap;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
@@ -124,7 +125,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
/* The fake, visible scrollbars. Used to force redraw during scrolling
before actuall scrolling happens, thus preventing shaking and
flickering artifacts. */
-.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
position: absolute;
z-index: 6;
display: none;
@@ -141,16 +142,18 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
}
.CodeMirror-scrollbar-filler {
right: 0; bottom: 0;
- z-index: 6;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
- height: 100%;
padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
+ white-space: normal;
height: 100%;
padding-bottom: 30px;
margin-bottom: -32px;
diff --git a/lib/codemirror.js b/lib/codemirror.js
index a1f9277223..b42c321527 100644
--- a/lib/codemirror.js
+++ b/lib/codemirror.js
@@ -97,7 +97,7 @@ window.CodeMirror = (function() {
else input.setAttribute("wrap", "off");
// if border: 0; -- iOS fails to open keyboard (issue #1287)
if (ios) input.style.border = "1px solid black";
- input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off");
+ input.setAttribute("autocorrect", "off"); input.setAttribute("autocapitalize", "off"); input.setAttribute("spellcheck", "false");
// Wraps and hides input textarea
d.inputDiv = elt("div", [input], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
@@ -105,8 +105,9 @@ window.CodeMirror = (function() {
d.scrollbarH = elt("div", [elt("div", null, null, "height: 1px")], "CodeMirror-hscrollbar");
d.scrollbarV = elt("div", [elt("div", null, null, "width: 1px")], "CodeMirror-vscrollbar");
d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
// DIVs containing the selection and the actual code
- d.lineDiv = elt("div");
+ d.lineDiv = elt("div", null, "CodeMirror-code");
d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
// Blinky cursor, and element used to ensure cursor fits at the end of a line
d.cursor = elt("div", "\u00a0", "CodeMirror-cursor");
@@ -126,14 +127,12 @@ window.CodeMirror = (function() {
// Will contain the gutters, if any
d.gutters = elt("div", null, "CodeMirror-gutters");
d.lineGutter = null;
- // Helper element to properly size the gutter backgrounds
- var scrollerInner = elt("div", [d.sizer, d.heightForcer, d.gutters], null, "position: relative; min-height: 100%");
// Provides scrolling
- d.scroller = elt("div", [scrollerInner], "CodeMirror-scroll");
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
d.scroller.setAttribute("tabIndex", "-1");
// The element in which the editor lives.
d.wrapper = elt("div", [d.inputDiv, d.scrollbarH, d.scrollbarV,
- d.scrollbarFiller, d.scroller], "CodeMirror");
+ d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
// Work around IE7 z-index bug
if (ie_lt8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
if (place.appendChild) place.appendChild(d.wrapper); else place(d.wrapper);
@@ -212,7 +211,7 @@ window.CodeMirror = (function() {
estimateLineHeights(cm);
regChange(cm);
clearCaches(cm);
- setTimeout(function(){updateScrollbars(cm.display, cm.doc.height);}, 100);
+ setTimeout(function(){updateScrollbars(cm);}, 100);
}
function estimateHeight(cm) {
@@ -251,6 +250,7 @@ window.CodeMirror = (function() {
function guttersChanged(cm) {
updateGutters(cm);
regChange(cm);
+ setTimeout(function(){alignHorizontally(cm);}, 20);
}
function updateGutters(cm) {
@@ -317,9 +317,11 @@ window.CodeMirror = (function() {
// Re-synchronize the fake scrollbars with the actual size of the
// content. Optionally force a scrollTop.
- function updateScrollbars(d /* display */, docHeight) {
+ function updateScrollbars(cm) {
+ var d = cm.display, docHeight = cm.doc.height;
var totalHeight = docHeight + paddingVert(d);
d.sizer.style.minHeight = d.heightForcer.style.top = totalHeight + "px";
+ d.gutters.style.height = Math.max(totalHeight, d.scroller.clientHeight - scrollerCutOff) + "px";
var scrollHeight = Math.max(totalHeight, d.scroller.scrollHeight);
var needsH = d.scroller.scrollWidth > d.scroller.clientWidth;
var needsV = scrollHeight > d.scroller.clientHeight;
@@ -339,6 +341,11 @@ window.CodeMirror = (function() {
d.scrollbarFiller.style.display = "block";
d.scrollbarFiller.style.height = d.scrollbarFiller.style.width = scrollbarWidth(d.measure) + "px";
} else d.scrollbarFiller.style.display = "";
+ if (needsH && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
+ d.gutterFiller.style.display = "block";
+ d.gutterFiller.style.height = scrollbarWidth(d.measure) + "px";
+ d.gutterFiller.style.width = d.gutters.offsetWidth + "px";
+ } else d.gutterFiller.style.display = "";
if (mac_geLion && scrollbarWidth(d.measure) === 0)
d.scrollbarV.style.minWidth = d.scrollbarH.style.minHeight = mac_geMountainLion ? "18px" : "12px";
@@ -397,14 +404,10 @@ window.CodeMirror = (function() {
var oldFrom = cm.display.showingFrom, oldTo = cm.display.showingTo, updated;
var visible = visibleLines(cm.display, cm.doc, viewPort);
for (;;) {
- if (updateDisplayInner(cm, changes, visible)) {
- updated = true;
- signalLater(cm, "update", cm);
- if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
- signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
- } else break;
+ if (!updateDisplayInner(cm, changes, visible)) break;
+ updated = true;
updateSelection(cm);
- updateScrollbars(cm.display, cm.doc.height);
+ updateScrollbars(cm);
// Clip forced viewport to actual scrollable area
if (viewPort)
@@ -416,6 +419,11 @@ window.CodeMirror = (function() {
changes = [];
}
+ if (updated) {
+ signalLater(cm, "update", cm);
+ if (cm.display.showingFrom != oldFrom || cm.display.showingTo != oldTo)
+ signalLater(cm, "viewportChange", cm, cm.display.showingFrom, cm.display.showingTo);
+ }
return updated;
}
@@ -976,23 +984,28 @@ window.CodeMirror = (function() {
if (memo.text == line.text && memo.markedSpans == line.markedSpans &&
cm.display.scroller.clientWidth == memo.width &&
memo.classes == line.textClass + "|" + line.bgClass + "|" + line.wrapClass)
- return memo.measure;
+ return memo;
}
}
+ function clearCachedMeasurement(cm, line) {
+ var exists = findCachedMeasurement(cm, line);
+ if (exists) exists.text = exists.measure = exists.markedSpans = null;
+ }
+
function measureLine(cm, line) {
// First look in the cache
- var measure = findCachedMeasurement(cm, line);
- if (!measure) {
- // Failing that, recompute and store result in cache
- measure = measureLineInner(cm, line);
- var cache = cm.display.measureLineCache;
- var memo = {text: line.text, width: cm.display.scroller.clientWidth,
- markedSpans: line.markedSpans, measure: measure,
- classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
- if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
- else cache.push(memo);
- }
+ var cached = findCachedMeasurement(cm, line);
+ if (cached) return cached.measure;
+
+ // Failing that, recompute and store result in cache
+ var measure = measureLineInner(cm, line);
+ var cache = cm.display.measureLineCache;
+ var memo = {text: line.text, width: cm.display.scroller.clientWidth,
+ markedSpans: line.markedSpans, measure: measure,
+ classes: line.textClass + "|" + line.bgClass + "|" + line.wrapClass};
+ if (cache.length == 16) cache[++cm.display.measureLineCachePos % 16] = memo;
+ else cache.push(memo);
return measure;
}
@@ -1068,7 +1081,7 @@ window.CodeMirror = (function() {
if (sp.collapsed && (sp.to == null || sp.to == line.text.length)) hasBadSpan = true;
}
var cached = !hasBadSpan && findCachedMeasurement(cm, line);
- if (cached) return measureChar(cm, line, line.text.length, cached).right;
+ if (cached) return measureChar(cm, line, line.text.length, cached.measure).right;
var pre = lineContent(cm, line);
var end = pre.appendChild(zeroWidthElement(cm.display.measure));
@@ -1136,32 +1149,26 @@ window.CodeMirror = (function() {
if (right) m.left = m.right; else m.right = m.left;
return intoCoordSystem(cm, lineObj, m, context);
}
- var order = getOrder(lineObj), ch = pos.ch;
- if (!order) return get(ch);
- var main, other, linedir = order[0].level;
- for (var i = 0; i < order.length; ++i) {
- var part = order[i], rtl = part.level % 2, nb, here;
- if (part.from < ch && part.to > ch) return get(ch, rtl);
- var left = rtl ? part.to : part.from, right = rtl ? part.from : part.to;
- if (left == ch) {
- // IE returns bogus offsets and widths for edges where the
- // direction flips, but only for the side with the lower
- // level. So we try to use the side with the higher level.
- if (i && part.level < (nb = order[i-1]).level) here = get(nb.level % 2 ? nb.from : nb.to - 1, true);
- else here = get(rtl && part.from != part.to ? ch - 1 : ch);
- if (rtl == linedir) main = here; else other = here;
- } else if (right == ch) {
- var nb = i < order.length - 1 && order[i+1];
- if (!rtl && nb && nb.from == nb.to) continue;
- if (nb && part.level < nb.level) here = get(nb.level % 2 ? nb.to - 1 : nb.from);
- else here = get(rtl ? ch : ch - 1, true);
- if (rtl == linedir) main = here; else other = here;
+ function getBidi(ch, partPos) {
+ var part = order[partPos], right = part.level % 2;
+ if (ch == bidiLeft(part) && partPos && part.level < order[partPos - 1].level) {
+ part = order[--partPos];
+ ch = bidiRight(part) - (part.level % 2 ? 0 : 1);
+ right = true;
+ } else if (ch == bidiRight(part) && partPos < order.length - 1 && part.level < order[partPos + 1].level) {
+ part = order[++partPos];
+ ch = bidiLeft(part) - part.level % 2;
+ right = false;
}
+ if (right && ch == part.to && ch > part.from) return get(ch - 1);
+ return get(ch, right);
}
- if (linedir && !ch) other = get(order[0].to - 1);
- if (!main) return other;
- if (other) main.other = other;
- return main;
+ var order = getOrder(lineObj), ch = pos.ch;
+ if (!order) return get(ch);
+ var partPos = getBidiPartAt(order, ch);
+ var val = getBidi(ch, partPos);
+ if (bidiOther != null) val.other = getBidi(ch, bidiOther);
+ return val;
}
function PosMaybeOutside(line, ch, outside) {
@@ -1585,7 +1592,7 @@ window.CodeMirror = (function() {
var target = e_target(e);
if (target == display.scrollbarH || target == display.scrollbarH.firstChild ||
target == display.scrollbarV || target == display.scrollbarV.firstChild ||
- target == display.scrollbarFiller) return null;
+ target == display.scrollbarFiller || target == display.gutterFiller) return null;
}
var x, y, space = getRect(display.lineSpace);
// Fails unpredictably on IE[67] when mouse is dragged around quickly.
@@ -2090,6 +2097,13 @@ window.CodeMirror = (function() {
// Adds "Select all" to context menu in FF
if (posEq(sel.from, sel.to)) display.input.value = display.prevInput = " ";
+ function prepareSelectAllHack() {
+ if (display.input.selectionStart != null) {
+ var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value);
+ display.prevInput = " ";
+ display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
+ }
+ }
function rehide() {
display.inputDiv.style.position = "relative";
display.input.style.cssText = oldCSS;
@@ -2097,12 +2111,10 @@ window.CodeMirror = (function() {
slowPoll(cm);
// Try to detect the user choosing select-all
- if (display.input.selectionStart != null && (!ie || ie_lt9)) {
+ if (display.input.selectionStart != null) {
+ if (!ie || ie_lt9) prepareSelectAllHack();
clearTimeout(detectingSelectAll);
- var extval = display.input.value = " " + (posEq(sel.from, sel.to) ? "" : display.input.value), i = 0;
- display.prevInput = " ";
- display.input.selectionStart = 1; display.input.selectionEnd = extval.length;
- var poll = function(){
+ var i = 0, poll = function(){
if (display.prevInput == " " && display.input.selectionStart == 0)
operation(cm, commands.selectAll)(cm);
else if (i++ < 10) detectingSelectAll = setTimeout(poll, 500);
@@ -2112,6 +2124,7 @@ window.CodeMirror = (function() {
}
}
+ if (ie && !ie_lt9) prepareSelectAllHack();
if (captureMiddleClick) {
e_stop(e);
var mouseup = function() {
@@ -2512,7 +2525,7 @@ window.CodeMirror = (function() {
// SCROLLING
function scrollCursorIntoView(cm) {
- var coords = scrollPosIntoView(cm, cm.doc.sel.head);
+ var coords = scrollPosIntoView(cm, cm.doc.sel.head, cm.options.cursorScrollMargin);
if (!cm.state.focused) return;
var display = cm.display, box = getRect(display.sizer), doScroll = null, pTop = paddingTop(cm.display);
if (coords.top + pTop + box.top < 0) doScroll = true;
@@ -2643,7 +2656,7 @@ window.CodeMirror = (function() {
}
function findPosH(doc, pos, dir, unit, visually) {
- var line = pos.line, ch = pos.ch;
+ var line = pos.line, ch = pos.ch, origDir = dir;
var lineObj = getLine(doc, line);
var possible = true;
function findNextLine() {
@@ -2682,7 +2695,7 @@ window.CodeMirror = (function() {
if (dir > 0 && !moveOnce(!first)) break;
}
}
- var result = skipAtomic(doc, Pos(line, ch), dir, true);
+ var result = skipAtomic(doc, Pos(line, ch), origDir, true);
if (!possible) result.hitSide = true;
return result;
}
@@ -2728,6 +2741,7 @@ window.CodeMirror = (function() {
// 'wrap f in an operation, performed on its `this` parameter'
CodeMirror.prototype = {
+ constructor: CodeMirror,
focus: function(){window.focus(); focusInput(this); onFocus(this); fastPoll(this);},
setOption: function(option, value) {
@@ -2988,7 +3002,8 @@ window.CodeMirror = (function() {
sel.goalColumn = pos.left;
}),
- toggleOverwrite: function() {
+ toggleOverwrite: function(value) {
+ if (value != null && value == this.state.overwrite) return;
if (this.state.overwrite = !this.state.overwrite)
this.display.cursor.className += " CodeMirror-overwrite";
else
@@ -3111,6 +3126,7 @@ window.CodeMirror = (function() {
cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
cm.refresh();
}, true);
+ option("coverGutterNextToScrollbar", false, updateScrollbars, true);
option("lineNumbers", false, function(cm) {
setGuttersForLineNumbers(cm.options);
guttersChanged(cm);
@@ -3126,6 +3142,7 @@ window.CodeMirror = (function() {
option("dragDrop", true);
option("cursorBlinkRate", 530);
+ option("cursorScrollMargin", 0);
option("cursorHeight", 1);
option("workTime", 100);
option("workDelay", 100);
@@ -3163,10 +3180,15 @@ window.CodeMirror = (function() {
};
CodeMirror.resolveMode = function(spec) {
- if (typeof spec == "string" && mimeModes.hasOwnProperty(spec))
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
spec = mimeModes[spec];
- else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec))
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+ var found = mimeModes[spec.name];
+ spec = createObj(found, spec);
+ spec.name = found.name;
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
return CodeMirror.resolveMode("application/xml");
+ }
if (typeof spec == "string") return {name: spec};
else return spec || {name: "null"};
};
@@ -3596,15 +3618,18 @@ window.CodeMirror = (function() {
return from && {from: from, to: to};
};
- TextMarker.prototype.getOptions = function(copyWidget) {
- var repl = this.replacedWith;
- return {className: this.className,
- inclusiveLeft: this.inclusiveLeft, inclusiveRight: this.inclusiveRight,
- atomic: this.atomic,
- collapsed: this.collapsed,
- replacedWith: copyWidget ? repl && repl.cloneNode(true) : repl,
- readOnly: this.readOnly,
- startStyle: this.startStyle, endStyle: this.endStyle};
+ TextMarker.prototype.changed = function() {
+ var pos = this.find(), cm = this.doc.cm;
+ if (!pos || !cm) return;
+ var line = getLine(this.doc, pos.from.line);
+ clearCachedMeasurement(cm, line);
+ if (pos.from.line >= cm.display.showingFrom && pos.from.line < cm.display.showingTo) {
+ for (var node = cm.display.lineDiv.firstChild; node; node = node.nextSibling) if (node.lineObj == line) {
+ if (node.offsetHeight != line.height) updateLineHeight(line, node.offsetHeight);
+ break;
+ }
+ runInOp(cm, function() { cm.curOp.selectionChanged = true; });
+ }
};
TextMarker.prototype.attachLine = function(line) {
@@ -3704,11 +3729,6 @@ window.CodeMirror = (function() {
SharedTextMarker.prototype.find = function() {
return this.primary.find();
};
- SharedTextMarker.prototype.getOptions = function(copyWidget) {
- var inner = this.primary.getOptions(copyWidget);
- inner.shared = true;
- return inner;
- };
function markTextShared(doc, from, to, options, type) {
options = copyObj(options);
@@ -4216,8 +4236,7 @@ window.CodeMirror = (function() {
if (ch >= "\ud800" && ch < "\udbff" && i < text.length - 1) {
ch = text.slice(i, i + 2);
++i;
- } else if (i && wrapping &&
- spanAffectsWrapping.test(text.slice(i - 1, i + 1))) {
+ } else if (i && wrapping && spanAffectsWrapping(text, i)) {
builder.pre.appendChild(elt("wbr"));
}
var span = builder.measure[builder.pos] =
@@ -4516,6 +4535,7 @@ window.CodeMirror = (function() {
};
Doc.prototype = createObj(BranchChunk.prototype, {
+ constructor: Doc,
iter: function(from, to, op) {
if (op) this.iterN(from - this.first, to - from, op);
else this.iterN(this.first, this.first + this.size, from);
@@ -5180,7 +5200,7 @@ window.CodeMirror = (function() {
return function(){return f.apply(null, args);};
}
- var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc]/;
+ var nonASCIISingleCaseWordChar = /[\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
function isWordChar(ch) {
return /\w/.test(ch) || ch > "\x80" &&
(ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch));
@@ -5241,13 +5261,24 @@ window.CodeMirror = (function() {
// word wrapping between certain characters *only* if a new inline
// element is started between them. This makes it hard to reliably
// measure the position of things, since that requires inserting an
- // extra span. This terribly fragile set of regexps matches the
+ // extra span. This terribly fragile set of tests matches the
// character combinations that suffer from this phenomenon on the
// various browsers.
- var spanAffectsWrapping = /^$/; // Won't match any two-character string
- if (gecko) spanAffectsWrapping = /$'/;
- else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent)) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/;
- else if (webkit) spanAffectsWrapping = /[~!#%&*)=+}\]|\"\.>,:;][({[<]|-[^\-?\.]|\?[\w~`@#$%\^&*(_=+{[|><]/;
+ function spanAffectsWrapping() { return false; }
+ if (gecko) // Only for "$'"
+ spanAffectsWrapping = function(str, i) {
+ return str.charCodeAt(i - 1) == 36 && str.charCodeAt(i) == 39;
+ };
+ else if (safari && !/Version\/([6-9]|\d\d)\b/.test(navigator.userAgent))
+ spanAffectsWrapping = function(str, i) {
+ return /\-[^ \-?]|\?[^ !\'\"\),.\-\/:;\?\]\}]/.test(str.slice(i - 1, i + 1));
+ };
+ else if (webkit)
+ spanAffectsWrapping = function(str, i) {
+ if (i > 1 && str.charCodeAt(i - 1) == 45 && /\w/.test(str.charAt(i - 2)) && /[^\-?\.]/.test(str.charAt(i)))
+ return true;
+ return /[~!#%&*)=+}\]|\"\.>,:;][({[<]|\?[\w~`@#$%\^&*(_=+{[|><]/.test(str.slice(i - 1, i + 1));
+ };
var knownScrollbarWidth;
function scrollbarWidth(measure) {
@@ -5366,6 +5397,40 @@ window.CodeMirror = (function() {
return Pos(lineN, ch);
}
+ function compareBidiLevel(order, a, b) {
+ var linedir = order[0].level;
+ if (a == linedir) return true;
+ if (b == linedir) return false;
+ return a < b;
+ }
+ var bidiOther;
+ function getBidiPartAt(order, pos) {
+ for (var i = 0, found; i < order.length; ++i) {
+ var cur = order[i];
+ if (cur.from < pos && cur.to > pos) { bidiOther = null; return i; }
+ if (cur.from == pos || cur.to == pos) {
+ if (found == null) {
+ found = i;
+ } else if (compareBidiLevel(order, cur.level, order[found].level)) {
+ bidiOther = found;
+ return i;
+ } else {
+ bidiOther = i;
+ return found;
+ }
+ }
+ }
+ bidiOther = null;
+ return found;
+ }
+
+ function moveInLine(line, pos, dir, byUnit) {
+ if (!byUnit) return pos + dir;
+ do pos += dir;
+ while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
+ return pos;
+ }
+
// This is somewhat involved. It is needed in order to move
// 'visually' through bi-directional text -- i.e., pressing left
// should make the cursor go left, even when in RTL text. The
@@ -5375,37 +5440,24 @@ window.CodeMirror = (function() {
function moveVisually(line, start, dir, byUnit) {
var bidi = getOrder(line);
if (!bidi) return moveLogically(line, start, dir, byUnit);
- var moveOneUnit = byUnit ? function(pos, dir) {
- do pos += dir;
- while (pos > 0 && isExtendingChar.test(line.text.charAt(pos)));
- return pos;
- } : function(pos, dir) { return pos + dir; };
- var linedir = bidi[0].level;
- for (var i = 0; i < bidi.length; ++i) {
- var part = bidi[i], sticky = part.level % 2 == linedir;
- if ((part.from < start && part.to > start) ||
- (sticky && (part.from == start || part.to == start))) break;
- }
- var target = moveOneUnit(start, part.level % 2 ? -dir : dir);
-
- while (target != null) {
- if (part.level % 2 == linedir) {
- if (target < part.from || target > part.to) {
- part = bidi[i += dir];
- target = part && (dir > 0 == part.level % 2 ? moveOneUnit(part.to, -1) : moveOneUnit(part.from, 1));
- } else break;
+ var pos = getBidiPartAt(bidi, start), part = bidi[pos];
+ var target = moveInLine(line, start, part.level % 2 ? -dir : dir, byUnit);
+
+ for (;;) {
+ if (target > part.from && target < part.to) return target;
+ if (target == part.from || target == part.to) {
+ if (getBidiPartAt(bidi, target) == pos) return target;
+ part = bidi[pos += dir];
+ return (dir > 0) == part.level % 2 ? part.to : part.from;
} else {
- if (target == bidiLeft(part)) {
- part = bidi[--i];
- target = part && bidiRight(part);
- } else if (target == bidiRight(part)) {
- part = bidi[++i];
- target = part && bidiLeft(part);
- } else break;
+ part = bidi[pos += dir];
+ if (!part) return null;
+ if ((dir > 0) == part.level % 2)
+ target = moveInLine(line, part.to, -1, byUnit);
+ else
+ target = moveInLine(line, part.from, 1, byUnit);
}
}
-
- return target < 0 || target > line.text.length ? null : target;
}
function moveLogically(line, start, dir, byUnit) {
@@ -5577,7 +5629,7 @@ window.CodeMirror = (function() {
// THE END
- CodeMirror.version = "3.12";
+ CodeMirror.version = "3.13";
return CodeMirror;
})();
diff --git a/mode/clike/clike.js b/mode/clike/clike.js
index c14d7b4006..09621bc08d 100644
--- a/mode/clike/clike.js
+++ b/mode/clike/clike.js
@@ -150,12 +150,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
var closing = firstChar == ctx.type;
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
- else if (dontAlignCalls && ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
- else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+ else if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1);
+ else if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit;
else return ctx.indented + (closing ? 0 : indentUnit);
},
- electricChars: "{}"
+ electricChars: "{}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ lineComment: "//"
};
});
diff --git a/mode/clojure/clojure.js b/mode/clojure/clojure.js
index fae675477d..ee22a12fed 100644
--- a/mode/clojure/clojure.js
+++ b/mode/clojure/clojure.js
@@ -215,7 +215,9 @@ CodeMirror.defineMode("clojure", function () {
indent: function (state) {
if (state.indentStack == null) return state.indentation;
return state.indentStack.indent;
- }
+ },
+
+ lineComment: ";;"
};
});
diff --git a/mode/cobol/cobol.js b/mode/cobol/cobol.js
new file mode 100644
index 0000000000..d92491dde8
--- /dev/null
+++ b/mode/cobol/cobol.js
@@ -0,0 +1,240 @@
+/**
+ * Author: Gautam Mehta
+ * Branched from CodeMirror's Scheme mode
+ */
+CodeMirror.defineMode("cobol", function () {
+ var BUILTIN = "builtin", COMMENT = "comment", STRING = "string",
+ ATOM = "atom", NUMBER = "number", KEYWORD = "keyword", MODTAG = "header",
+ COBOLLINENUM = "def", PERIOD = "link";
+ function makeKeywords(str) {
+ var obj = {}, words = str.split(" ");
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+ return obj;
+ }
+ var atoms = makeKeywords("TRUE FALSE ZEROES ZEROS ZERO SPACES SPACE LOW-VALUE LOW-VALUES ");
+ var keywords = makeKeywords(
+ "ACCEPT ACCESS ACQUIRE ADD ADDRESS " +
+ "ADVANCING AFTER ALIAS ALL ALPHABET " +
+ "ALPHABETIC ALPHABETIC-LOWER ALPHABETIC-UPPER ALPHANUMERIC ALPHANUMERIC-EDITED " +
+ "ALSO ALTER ALTERNATE AND ANY " +
+ "ARE AREA AREAS ARITHMETIC ASCENDING " +
+ "ASSIGN AT ATTRIBUTE AUTHOR AUTO " +
+ "AUTO-SKIP AUTOMATIC B-AND B-EXOR B-LESS " +
+ "B-NOT B-OR BACKGROUND-COLOR BACKGROUND-COLOUR BEEP " +
+ "BEFORE BELL BINARY BIT BITS " +
+ "BLANK BLINK BLOCK BOOLEAN BOTTOM " +
+ "BY CALL CANCEL CD CF " +
+ "CH CHARACTER CHARACTERS CLASS CLOCK-UNITS " +
+ "CLOSE COBOL CODE CODE-SET COL " +
+ "COLLATING COLUMN COMMA COMMIT COMMITMENT " +
+ "COMMON COMMUNICATION COMP COMP-0 COMP-1 " +
+ "COMP-2 COMP-3 COMP-4 COMP-5 COMP-6 " +
+ "COMP-7 COMP-8 COMP-9 COMPUTATIONAL COMPUTATIONAL-0 " +
+ "COMPUTATIONAL-1 COMPUTATIONAL-2 COMPUTATIONAL-3 COMPUTATIONAL-4 COMPUTATIONAL-5 " +
+ "COMPUTATIONAL-6 COMPUTATIONAL-7 COMPUTATIONAL-8 COMPUTATIONAL-9 COMPUTE " +
+ "CONFIGURATION CONNECT CONSOLE CONTAINED CONTAINS " +
+ "CONTENT CONTINUE CONTROL CONTROL-AREA CONTROLS " +
+ "CONVERTING COPY CORR CORRESPONDING COUNT " +
+ "CRT CRT-UNDER CURRENCY CURRENT CURSOR " +
+ "DATA DATE DATE-COMPILED DATE-WRITTEN DAY " +
+ "DAY-OF-WEEK DB DB-ACCESS-CONTROL-KEY DB-DATA-NAME DB-EXCEPTION " +
+ "DB-FORMAT-NAME DB-RECORD-NAME DB-SET-NAME DB-STATUS DBCS " +
+ "DBCS-EDITED DE DEBUG-CONTENTS DEBUG-ITEM DEBUG-LINE " +
+ "DEBUG-NAME DEBUG-SUB-1 DEBUG-SUB-2 DEBUG-SUB-3 DEBUGGING " +
+ "DECIMAL-POINT DECLARATIVES DEFAULT DELETE DELIMITED " +
+ "DELIMITER DEPENDING DESCENDING DESCRIBED DESTINATION " +
+ "DETAIL DISABLE DISCONNECT DISPLAY DISPLAY-1 " +
+ "DISPLAY-2 DISPLAY-3 DISPLAY-4 DISPLAY-5 DISPLAY-6 " +
+ "DISPLAY-7 DISPLAY-8 DISPLAY-9 DIVIDE DIVISION " +
+ "DOWN DROP DUPLICATE DUPLICATES DYNAMIC " +
+ "EBCDIC EGI EJECT ELSE EMI " +
+ "EMPTY EMPTY-CHECK ENABLE END END. END-ACCEPT END-ACCEPT. " +
+ "END-ADD END-CALL END-COMPUTE END-DELETE END-DISPLAY " +
+ "END-DIVIDE END-EVALUATE END-IF END-INVOKE END-MULTIPLY " +
+ "END-OF-PAGE END-PERFORM END-READ END-RECEIVE END-RETURN " +
+ "END-REWRITE END-SEARCH END-START END-STRING END-SUBTRACT " +
+ "END-UNSTRING END-WRITE END-XML ENTER ENTRY " +
+ "ENVIRONMENT EOP EQUAL EQUALS ERASE " +
+ "ERROR ESI EVALUATE EVERY EXCEEDS " +
+ "EXCEPTION EXCLUSIVE EXIT EXTEND EXTERNAL " +
+ "EXTERNALLY-DESCRIBED-KEY FD FETCH FILE FILE-CONTROL " +
+ "FILE-STREAM FILES FILLER FINAL FIND " +
+ "FINISH FIRST FOOTING FOR FOREGROUND-COLOR " +
+ "FOREGROUND-COLOUR FORMAT FREE FROM FULL " +
+ "FUNCTION GENERATE GET GIVING GLOBAL " +
+ "GO GOBACK GREATER GROUP HEADING " +
+ "HIGH-VALUE HIGH-VALUES HIGHLIGHT I-O I-O-CONTROL " +
+ "ID IDENTIFICATION IF IN INDEX " +
+ "INDEX-1 INDEX-2 INDEX-3 INDEX-4 INDEX-5 " +
+ "INDEX-6 INDEX-7 INDEX-8 INDEX-9 INDEXED " +
+ "INDIC INDICATE INDICATOR INDICATORS INITIAL " +
+ "INITIALIZE INITIATE INPUT INPUT-OUTPUT INSPECT " +
+ "INSTALLATION INTO INVALID INVOKE IS " +
+ "JUST JUSTIFIED KANJI KEEP KEY " +
+ "LABEL LAST LD LEADING LEFT " +
+ "LEFT-JUSTIFY LENGTH LENGTH-CHECK LESS LIBRARY " +
+ "LIKE LIMIT LIMITS LINAGE LINAGE-COUNTER " +
+ "LINE LINE-COUNTER LINES LINKAGE LOCAL-STORAGE " +
+ "LOCALE LOCALLY LOCK " +
+ "MEMBER MEMORY MERGE MESSAGE METACLASS " +
+ "MODE MODIFIED MODIFY MODULES MOVE " +
+ "MULTIPLE MULTIPLY NATIONAL NATIVE NEGATIVE " +
+ "NEXT NO NO-ECHO NONE NOT " +
+ "NULL NULL-KEY-MAP NULL-MAP NULLS NUMBER " +
+ "NUMERIC NUMERIC-EDITED OBJECT OBJECT-COMPUTER OCCURS " +
+ "OF OFF OMITTED ON ONLY " +
+ "OPEN OPTIONAL OR ORDER ORGANIZATION " +
+ "OTHER OUTPUT OVERFLOW OWNER PACKED-DECIMAL " +
+ "PADDING PAGE PAGE-COUNTER PARSE PERFORM " +
+ "PF PH PIC PICTURE PLUS " +
+ "POINTER POSITION POSITIVE PREFIX PRESENT " +
+ "PRINTING PRIOR PROCEDURE PROCEDURE-POINTER PROCEDURES " +
+ "PROCEED PROCESS PROCESSING PROGRAM PROGRAM-ID " +
+ "PROMPT PROTECTED PURGE QUEUE QUOTE " +
+ "QUOTES RANDOM RD READ READY " +
+ "REALM RECEIVE RECONNECT RECORD RECORD-NAME " +
+ "RECORDS RECURSIVE REDEFINES REEL REFERENCE " +
+ "REFERENCE-MONITOR REFERENCES RELATION RELATIVE RELEASE " +
+ "REMAINDER REMOVAL RENAMES REPEATED REPLACE " +
+ "REPLACING REPORT REPORTING REPORTS REPOSITORY " +
+ "REQUIRED RERUN RESERVE RESET RETAINING " +
+ "RETRIEVAL RETURN RETURN-CODE RETURNING REVERSE-VIDEO " +
+ "REVERSED REWIND REWRITE RF RH " +
+ "RIGHT RIGHT-JUSTIFY ROLLBACK ROLLING ROUNDED " +
+ "RUN SAME SCREEN SD SEARCH " +
+ "SECTION SECURE SECURITY SEGMENT SEGMENT-LIMIT " +
+ "SELECT SEND SENTENCE SEPARATE SEQUENCE " +
+ "SEQUENTIAL SET SHARED SIGN SIZE " +
+ "SKIP1 SKIP2 SKIP3 SORT SORT-MERGE " +
+ "SORT-RETURN SOURCE SOURCE-COMPUTER SPACE-FILL " +
+ "SPECIAL-NAMES STANDARD STANDARD-1 STANDARD-2 " +
+ "START STARTING STATUS STOP STORE " +
+ "STRING SUB-QUEUE-1 SUB-QUEUE-2 SUB-QUEUE-3 SUB-SCHEMA " +
+ "SUBFILE SUBSTITUTE SUBTRACT SUM SUPPRESS " +
+ "SYMBOLIC SYNC SYNCHRONIZED SYSIN SYSOUT " +
+ "TABLE TALLYING TAPE TENANT TERMINAL " +
+ "TERMINATE TEST TEXT THAN THEN " +
+ "THROUGH THRU TIME TIMES TITLE " +
+ "TO TOP TRAILING TRAILING-SIGN TRANSACTION " +
+ "TYPE TYPEDEF UNDERLINE UNEQUAL UNIT " +
+ "UNSTRING UNTIL UP UPDATE UPON " +
+ "USAGE USAGE-MODE USE USING VALID " +
+ "VALIDATE VALUE VALUES VARYING VLR " +
+ "WAIT WHEN WHEN-COMPILED WITH WITHIN " +
+ "WORDS WORKING-STORAGE WRITE XML XML-CODE " +
+ "XML-EVENT XML-NTEXT XML-TEXT ZERO ZERO-FILL " );
+
+ var builtins = makeKeywords("- * ** / + < <= = > >= ");
+ var tests = {
+ digit: /\d/,
+ digit_or_colon: /[\d:]/,
+ hex: /[0-9a-f]/i,
+ sign: /[+-]/,
+ exponent: /e/i,
+ keyword_char: /[^\s\(\[\;\)\]]/,
+ symbol: /[\w*+\-]/
+ };
+ function isNumber(ch, stream){
+ // hex
+ if ( ch === '0' && stream.eat(/x/i) ) {
+ stream.eatWhile(tests.hex);
+ return true;
+ }
+ // leading sign
+ if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) {
+ stream.eat(tests.sign);
+ ch = stream.next();
+ }
+ if ( tests.digit.test(ch) ) {
+ stream.eat(ch);
+ stream.eatWhile(tests.digit);
+ if ( '.' == stream.peek()) {
+ stream.eat('.');
+ stream.eatWhile(tests.digit);
+ }
+ if ( stream.eat(tests.exponent) ) {
+ stream.eat(tests.sign);
+ stream.eatWhile(tests.digit);
+ }
+ return true;
+ }
+ return false;
+ }
+ return {
+ startState: function () {
+ return {
+ indentStack: null,
+ indentation: 0,
+ mode: false
+ };
+ },
+ token: function (stream, state) {
+ if (state.indentStack == null && stream.sol()) {
+ // update indentation, but only if indentStack is empty
+ state.indentation = 6 ; //stream.indentation();
+ }
+ // skip spaces
+ if (stream.eatSpace()) {
+ return null;
+ }
+ var returnType = null;
+ switch(state.mode){
+ case "string": // multi-line string parsing mode
+ var next = false;
+ while ((next = stream.next()) != null) {
+ if (next == "\"" || next == "\'") {
+ state.mode = false;
+ break;
+ }
+ }
+ returnType = STRING; // continue on in string mode
+ break;
+ default: // default parsing mode
+ var ch = stream.next();
+ var col = stream.column();
+ if (col >= 0 && col <= 5) {
+ returnType = COBOLLINENUM;
+ } else if (col >= 72 && col <= 79) {
+ stream.skipToEnd();
+ returnType = MODTAG;
+ } else if (ch == "*" && col == 6) { // comment
+ stream.skipToEnd(); // rest of the line is a comment
+ returnType = COMMENT;
+ } else if (ch == "\"" || ch == "\'") {
+ state.mode = "string";
+ returnType = STRING;
+ } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
+ returnType = ATOM;
+ } else if (ch == ".") {
+ returnType = PERIOD;
+ } else if (isNumber(ch,stream)){
+ returnType = NUMBER;
+ } else {
+ if (stream.current().match(tests.symbol)) {
+ while (col < 71) {
+ if (stream.eat(tests.symbol) === undefined) {
+ break;
+ } else {
+ col++;
+ }
+ }
+ }
+ if (keywords && keywords.propertyIsEnumerable(stream.current().toUpperCase())) {
+ returnType = KEYWORD;
+ } else if (builtins && builtins.propertyIsEnumerable(stream.current().toUpperCase())) {
+ returnType = BUILTIN;
+ } else if (atoms && atoms.propertyIsEnumerable(stream.current().toUpperCase())) {
+ returnType = ATOM;
+ } else returnType = null;
+ }
+ }
+ return returnType;
+ },
+ indent: function (state) {
+ if (state.indentStack == null) return state.indentation;
+ return state.indentStack.indent;
+ }
+ };
+});
+
+CodeMirror.defineMIME("text/x-cobol", "cobol");
diff --git a/mode/cobol/index.html b/mode/cobol/index.html
new file mode 100644
index 0000000000..71cc2fa151
--- /dev/null
+++ b/mode/cobol/index.html
@@ -0,0 +1,195 @@
+
+
+
+
+ CodeMirror: COBOL mode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Select Theme
+ default
+ ambiance
+ blackboard
+ cobalt
+ eclipse
+ elegant
+ erlang-dark
+ lesser-dark
+ midnight
+ monokai
+ neat
+ night
+ rubyblue
+ solarized dark
+ solarized light
+ twilight
+ vibrant-ink
+ xq-dark
+ xq-light
+ Select Font Size
+ 13px
+ 14px
+ 16px
+ 18px
+ 20px
+ 24px
+ 26px
+ 28px
+ 30px
+ 32px
+ 34px
+ 36px
+
+Read-only
+
+Insert Spaces on Tab
+
+
+
+
+
+
diff --git a/mode/coffeescript/coffeescript.js b/mode/coffeescript/coffeescript.js
index cf1cf4f246..509d9207bb 100644
--- a/mode/coffeescript/coffeescript.js
+++ b/mode/coffeescript/coffeescript.js
@@ -337,8 +337,9 @@ CodeMirror.defineMode('coffeescript', function(conf) {
}
return state.scopes[0].offset;
- }
+ },
+ lineComment: "#"
};
return external;
});
diff --git a/mode/commonlisp/commonlisp.js b/mode/commonlisp/commonlisp.js
index eeba759668..8fa08c8ac2 100644
--- a/mode/commonlisp/commonlisp.js
+++ b/mode/commonlisp/commonlisp.js
@@ -94,7 +94,11 @@ CodeMirror.defineMode("commonlisp", function (config) {
indent: function (state, _textAfter) {
var i = state.ctx.indentTo;
return typeof i == "number" ? i : state.ctx.start + 1;
- }
+ },
+
+ lineComment: ";;",
+ blockCommentStart: "#|",
+ blockCommentEnd: "|#"
};
});
diff --git a/mode/css/css.js b/mode/css/css.js
index 1ef72b517d..27c97f37eb 100644
--- a/mode/css/css.js
+++ b/mode/css/css.js
@@ -165,26 +165,27 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
if (type == "variable-definition") state.stack.push("propertyValue");
return "variable-2";
} else if (style == "property") {
- if (context == "propertyValue"){
- if (valueKeywords[stream.current()]) {
+ var word = stream.current().toLowerCase();
+ if (context == "propertyValue") {
+ if (valueKeywords.hasOwnProperty(word)) {
style = "string-2";
- } else if (colorKeywords[stream.current()]) {
+ } else if (colorKeywords.hasOwnProperty(word)) {
style = "keyword";
} else {
style = "variable-2";
}
} else if (context == "rule") {
- if (!propertyKeywords[stream.current()]) {
+ if (!propertyKeywords.hasOwnProperty(word)) {
style += " error";
}
} else if (context == "block") {
// if a value is present in both property, value, or color, the order
// of preference is property -> color -> value
- if (propertyKeywords[stream.current()]) {
+ if (propertyKeywords.hasOwnProperty(word)) {
style = "property";
- } else if (colorKeywords[stream.current()]) {
+ } else if (colorKeywords.hasOwnProperty(word)) {
style = "keyword";
- } else if (valueKeywords[stream.current()]) {
+ } else if (valueKeywords.hasOwnProperty(word)) {
style = "string-2";
} else {
style = "tag";
@@ -194,42 +195,42 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
} else if (context == "@media") {
if (atMediaTypes[stream.current()]) {
style = "attribute"; // Known attribute
- } else if (/^(only|not)$/i.test(stream.current())) {
+ } else if (/^(only|not)$/.test(word)) {
style = "keyword";
- } else if (stream.current().toLowerCase() == "and") {
+ } else if (word == "and") {
style = "error"; // "and" is only allowed in @mediaType
- } else if (atMediaFeatures[stream.current()]) {
+ } else if (atMediaFeatures.hasOwnProperty(word)) {
style = "error"; // Known property, should be in @mediaType(
} else {
// Unknown, expecting keyword or attribute, assuming attribute
style = "attribute error";
}
} else if (context == "@mediaType") {
- if (atMediaTypes[stream.current()]) {
+ if (atMediaTypes.hasOwnProperty(word)) {
style = "attribute";
- } else if (stream.current().toLowerCase() == "and") {
+ } else if (word == "and") {
style = "operator";
- } else if (/^(only|not)$/i.test(stream.current())) {
+ } else if (/^(only|not)$/.test(word)) {
style = "error"; // Only allowed in @media
- } else if (atMediaFeatures[stream.current()]) {
- style = "error"; // Known property, should be in parentheses
} else {
// Unknown attribute or property, but expecting property (preceded
// by "and"). Should be in parentheses
style = "error";
}
} else if (context == "@mediaType(") {
- if (propertyKeywords[stream.current()]) {
+ if (propertyKeywords.hasOwnProperty(word)) {
// do nothing, remains "property"
- } else if (atMediaTypes[stream.current()]) {
+ } else if (atMediaTypes.hasOwnProperty(word)) {
style = "error"; // Known property, should be in parentheses
- } else if (stream.current().toLowerCase() == "and") {
+ } else if (word == "and") {
style = "operator";
- } else if (/^(only|not)$/i.test(stream.current())) {
+ } else if (/^(only|not)$/.test(word)) {
style = "error"; // Only allowed in @media
} else {
style += " error";
}
+ } else if (context == "@import") {
+ style = "tag";
} else {
style = "error";
}
@@ -266,6 +267,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
}
else if (type == "interpolation") state.stack.push("interpolation");
else if (type == "@media") state.stack.push("@media");
+ else if (type == "@import") state.stack.push("@import");
else if (context == "@media" && /\b(keyword|attribute)\b/.test(style))
state.stack.push("@mediaType");
else if (context == "@mediaType" && stream.current() == ",") state.stack.pop();
@@ -273,6 +275,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
else if (context == "@mediaType(" && type == ")") state.stack.pop();
else if ((context == "rule" || context == "block") && type == ":") state.stack.push("propertyValue");
else if (context == "propertyValue" && type == ";") state.stack.pop();
+ else if (context == "@import" && type == ";") state.stack.pop();
return style;
},
@@ -283,7 +286,9 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
return state.baseIndent + n * indentUnit;
},
- electricChars: "}"
+ electricChars: "}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/"
};
});
@@ -387,12 +392,46 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"vertical-align", "visibility", "voice-balance", "voice-duration",
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
- "word-spacing", "word-wrap", "z-index"
+ "word-spacing", "word-wrap", "z-index",
+ // SVG-specific
+ "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
+ "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
+ "color-interpolation", "color-interpolation-filters", "color-profile",
+ "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
+ "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
+ "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
+ "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
+ "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode"
]);
var colorKeywords = keySet([
- "black", "silver", "gray", "white", "maroon", "red", "purple", "fuchsia",
- "green", "lime", "olive", "yellow", "navy", "blue", "teal", "aqua"
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
+ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
+ "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+ "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+ "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
+ "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
+ "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+ "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew",
+ "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
+ "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
+ "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
+ "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
+ "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
+ "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
+ "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+ "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon",
+ "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+ "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+ "whitesmoke", "yellow", "yellowgreen"
]);
var valueKeywords = keySet([
@@ -475,7 +514,7 @@ CodeMirror.defineMode("css-base", function(config, parserConfig) {
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
"vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
- "visibleStroke", "visual", "w-resize", "wait", "wave", "white", "wider",
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
"window", "windowframe", "windowtext", "x-large", "x-small", "xor",
"xx-large", "xx-small"
]);
diff --git a/mode/erlang/erlang.js b/mode/erlang/erlang.js
index 029b8e5561..79e0434d1f 100644
--- a/mode/erlang/erlang.js
+++ b/mode/erlang/erlang.js
@@ -456,8 +456,9 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
indent:
function(state, textAfter) {
-// console.log(state.tokenStack);
return myIndent(state,textAfter);
- }
+ },
+
+ lineComment: "%"
};
});
diff --git a/mode/gas/gas.js b/mode/gas/gas.js
index 6604f01820..a6e6892908 100644
--- a/mode/gas/gas.js
+++ b/mode/gas/gas.js
@@ -321,6 +321,10 @@ CodeMirror.defineMode("gas", function(_config, parserConfig) {
return style;
}
}
- }
+ },
+
+ lineComment: lineCommentStartSymbol,
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/"
};
});
diff --git a/mode/go/go.js b/mode/go/go.js
index 8b84a5ca42..6a458a6fe9 100644
--- a/mode/go/go.js
+++ b/mode/go/go.js
@@ -158,7 +158,10 @@ CodeMirror.defineMode("go", function(config) {
else return ctx.indented + (closing ? 0 : indentUnit);
},
- electricChars: "{}:"
+ electricChars: "{}:",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ lineComment: "//"
};
});
diff --git a/mode/haml/haml.js b/mode/haml/haml.js
new file mode 100644
index 0000000000..793308f6fb
--- /dev/null
+++ b/mode/haml/haml.js
@@ -0,0 +1,153 @@
+(function() {
+ "use strict";
+
+ // full haml mode. This handled embeded ruby and html fragments too
+ CodeMirror.defineMode("haml", function(config) {
+ var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
+ var rubyMode = CodeMirror.getMode(config, "ruby");
+
+ function rubyInQuote(endQuote) {
+ return function(stream, state) {
+ var ch = stream.peek();
+ if (ch == endQuote && state.rubyState.tokenize.length == 1) {
+ // step out of ruby context as it seems to complete processing all the braces
+ stream.next();
+ state.tokenize = html;
+ return "closeAttributeTag";
+ } else {
+ return ruby(stream, state);
+ }
+ };
+ }
+
+ function ruby(stream, state) {
+ if (stream.match("-#")) {
+ stream.skipToEnd();
+ return "comment";
+ }
+ return rubyMode.token(stream, state.rubyState);
+ }
+
+ function html(stream, state) {
+ var ch = stream.peek();
+
+ // handle haml declarations. All declarations that cant be handled here
+ // will be passed to html mode
+ if (state.previousToken.style == "comment" ) {
+ if (state.indented > state.previousToken.indented) {
+ stream.skipToEnd();
+ return "commentLine";
+ }
+ }
+
+ if (state.startOfLine) {
+ if (ch == "!" && stream.match("!!")) {
+ stream.skipToEnd();
+ return "tag";
+ } else if (stream.match(/^%[\w:#\.]+=/)) {
+ state.tokenize = ruby;
+ return "hamlTag";
+ } else if (stream.match(/^%[\w:]+/)) {
+ return "hamlTag";
+ } else if (ch == "/" ) {
+ stream.skipToEnd();
+ return "comment";
+ }
+ }
+
+ if (state.startOfLine || state.previousToken.style == "hamlTag") {
+ if ( ch == "#" || ch == ".") {
+ stream.match(/[\w-#\.]*/);
+ return "hamlAttribute";
+ }
+ }
+
+ // donot handle --> as valid ruby, make it HTML close comment instead
+ if (state.startOfLine && !stream.match("-->", false) && (ch == "=" || ch == "-" )) {
+ state.tokenize = ruby;
+ return null;
+ }
+
+ if (state.previousToken.style == "hamlTag" ||
+ state.previousToken.style == "closeAttributeTag" ||
+ state.previousToken.style == "hamlAttribute") {
+ if (ch == "(") {
+ state.tokenize = rubyInQuote(")");
+ return null;
+ } else if (ch == "{") {
+ state.tokenize = rubyInQuote("}");
+ return null;
+ }
+ }
+
+ return htmlMode.token(stream, state.htmlState);
+ }
+
+ return {
+ // default to html mode
+ startState: function() {
+ var htmlState = htmlMode.startState();
+ var rubyState = rubyMode.startState();
+ return {
+ htmlState: htmlState,
+ rubyState: rubyState,
+ indented: 0,
+ previousToken: { style: null, indented: 0},
+ tokenize: html
+ };
+ },
+
+ copyState: function(state) {
+ return {
+ htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
+ rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
+ indented: state.indented,
+ previousToken: state.previousToken,
+ tokenize: state.tokenize
+ };
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ state.indented = stream.indentation();
+ state.startOfLine = true;
+ }
+ if (stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+ state.startOfLine = false;
+ // dont record comment line as we only want to measure comment line with
+ // the opening comment block
+ if (style && style != "commentLine") {
+ state.previousToken = { style: style, indented: state.indented };
+ }
+ // if current state is ruby and the previous token is not `,` reset the
+ // tokenize to html
+ if (stream.eol() && state.tokenize == ruby) {
+ stream.backUp(1);
+ var ch = stream.peek();
+ stream.next();
+ if (ch && ch != ",") {
+ state.tokenize = html;
+ }
+ }
+ // reprocess some of the specific style tag when finish setting previousToken
+ if (style == "hamlTag") {
+ style = "tag";
+ } else if (style == "commentLine") {
+ style = "comment";
+ } else if (style == "hamlAttribute") {
+ style = "attribute";
+ } else if (style == "closeAttributeTag") {
+ style = null;
+ }
+ return style;
+ },
+
+ indent: function(state) {
+ return state.indented;
+ }
+ };
+ }, "htmlmixed", "ruby");
+
+ CodeMirror.defineMIME("text/x-haml", "haml");
+})();
diff --git a/mode/haml/index.html b/mode/haml/index.html
new file mode 100644
index 0000000000..7da378fc80
--- /dev/null
+++ b/mode/haml/index.html
@@ -0,0 +1,67 @@
+
+
+
+
+ CodeMirror: HAML mode
+
+
+
+
+
+
+
+
+
+
+
+ CodeMirror: HAML mode
+
+
+
+ MIME types defined: text/x-haml.
+
+ Parsing/Highlighting Tests: normal , verbose .
+
+
+
diff --git a/mode/haml/test.js b/mode/haml/test.js
new file mode 100644
index 0000000000..b7178d40fb
--- /dev/null
+++ b/mode/haml/test.js
@@ -0,0 +1,94 @@
+(function() {
+ var mode = CodeMirror.getMode({tabSize: 4}, "haml");
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+ // Requires at least one media query
+ MT("elementName",
+ "[tag %h1] Hey There");
+
+ MT("oneElementPerLine",
+ "[tag %h1] Hey There %h2");
+
+ MT("idSelector",
+ "[tag %h1][attribute #test] Hey There");
+
+ MT("classSelector",
+ "[tag %h1][attribute .hello] Hey There");
+
+ MT("docType",
+ "[tag !!! XML]");
+
+ MT("comment",
+ "[comment / Hello WORLD]");
+
+ MT("notComment",
+ "[tag %h1] This is not a / comment ");
+
+ MT("attributes",
+ "[tag %a]([variable title][operator =][string \"test\"]){[atom :title] [operator =>] [string \"test\"]}");
+
+ MT("htmlCode",
+ "[tag ]Title[tag ]");
+
+ MT("rubyBlock",
+ "[operator =][variable-2 @item]");
+
+ MT("selectorRubyBlock",
+ "[tag %a.selector=] [variable-2 @item]");
+
+ MT("nestedRubyBlock",
+ "[tag %a]",
+ " [operator =][variable puts] [string \"test\"]");
+
+ MT("multilinePlaintext",
+ "[tag %p]",
+ " Hello,",
+ " World");
+
+ MT("multilineRuby",
+ "[tag %p]",
+ " [comment -# this is a comment]",
+ " [comment and this is a comment too]",
+ " Date/Time",
+ " [operator -] [variable now] [operator =] [tag DateTime][operator .][variable now]",
+ " [tag %strong=] [variable now]",
+ " [operator -] [keyword if] [variable now] [operator >] [tag DateTime][operator .][variable parse]([string \"December 31, 2006\"])",
+ " [operator =][string \"Happy\"]",
+ " [operator =][string \"Belated\"]",
+ " [operator =][string \"Birthday\"]");
+
+ MT("multilineComment",
+ "[comment /]",
+ " [comment Multiline]",
+ " [comment Comment]");
+
+ MT("hamlComment",
+ "[comment -# this is a comment]");
+
+ MT("multilineHamlComment",
+ "[comment -# this is a comment]",
+ " [comment and this is a comment too]");
+
+ MT("multilineHTMLComment",
+ "[comment ]");
+
+ MT("hamlAfterRubyTag",
+ "[attribute .block]",
+ " [tag %strong=] [variable now]",
+ " [attribute .test]",
+ " [operator =][variable now]",
+ " [attribute .right]");
+
+ MT("stretchedRuby",
+ "[operator =] [variable puts] [string \"Hello\"],",
+ " [string \"World\"]");
+
+ MT("interpolationInHashAttribute",
+ //"[tag %div]{[atom :id] [operator =>] [string \"#{][variable test][string }_#{][variable ting][string }\"]} test");
+ "[tag %div]{[atom :id] [operator =>] [string \"#{][variable test][string }_#{][variable ting][string }\"]} test");
+
+ MT("interpolationInHTMLAttribute",
+ "[tag %div]([variable title][operator =][string \"#{][variable test][string }_#{][variable ting]()[string }\"]) Test");
+})();
diff --git a/mode/haskell/haskell.js b/mode/haskell/haskell.js
index faec08dc33..b18d5ced1a 100644
--- a/mode/haskell/haskell.js
+++ b/mode/haskell/haskell.js
@@ -234,7 +234,11 @@ CodeMirror.defineMode("haskell", function() {
var t = state.f(stream, function(s) { state.f = s; });
var w = stream.current();
return (w in wellKnownWords) ? wellKnownWords[w] : t;
- }
+ },
+
+ blockCommentStart: "{-",
+ blockCommentEnd: "-}",
+ lineComment: "--"
};
});
diff --git a/mode/javascript/index.html b/mode/javascript/index.html
index dd0ca220d8..db063b772d 100644
--- a/mode/javascript/index.html
+++ b/mode/javascript/index.html
@@ -7,6 +7,7 @@
+
@@ -68,7 +69,8 @@
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
matchBrackets: true,
- continueComments: "Enter"
+ continueComments: "Enter",
+ extraKeys: {"Ctrl-Q": "toggleComment"}
});
diff --git a/mode/javascript/javascript.js b/mode/javascript/javascript.js
index 08c1cb1f4f..fabe1c42b9 100644
--- a/mode/javascript/javascript.js
+++ b/mode/javascript/javascript.js
@@ -291,7 +291,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
}
function maybeoperatorComma(type, value) {
- if (type == ",") return cont(expression);
+ if (type == ",") return pass();
return maybeoperatorNoComma(type, value, maybeoperatorComma);
}
function maybeoperatorNoComma(type, value, me) {
@@ -453,6 +453,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
},
electricChars: ":{}",
+ blockCommentStart: jsonMode ? null : "/*",
+ blockCommentEnd: jsonMode ? null : "*/",
+ lineComment: jsonMode ? null : "//",
jsonMode: jsonMode
};
@@ -463,5 +466,6 @@ CodeMirror.defineMIME("text/ecmascript", "javascript");
CodeMirror.defineMIME("application/javascript", "javascript");
CodeMirror.defineMIME("application/ecmascript", "javascript");
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
diff --git a/mode/less/less.js b/mode/less/less.js
index 6df4790995..09f510e032 100644
--- a/mode/less/less.js
+++ b/mode/less/less.js
@@ -7,12 +7,6 @@
CodeMirror.defineMode("less", function(config) {
var indentUnit = config.indentUnit, type;
function ret(style, tp) {type = tp; return style;}
- //html tags
- var tags = "a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption cite code col colgroup command datalist dd del details dfn dir div dl dt em embed fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins keygen kbd label legend li link map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr".split(' ');
-
- function inTagsArray(val){
- for(var i=0; i", "<", "==", ">=", "<=", "\\+", "-", "\\!=", "/", "\\*", "%", "and", "or", "not"];
var opRegexp = tokenRegexp(operators);
- function htmlTag(val){
- for(var i=0; i=@%|&?!.:;^]/;
+ var specialChars = /[+\-\/\\*~<>=@%|&?!.,:;^]/;
var keywords = /true|false|nil|self|super|thisContext/;
var Context = function(tokenizer, parent) {
@@ -36,11 +36,14 @@ CodeMirror.defineMode('smalltalk', function(config) {
token = nextString(stream, new Context(nextString, context));
} else if (aChar === '#') {
- stream.eatWhile(/[^ .]/);
+ stream.eatWhile(/[^ .\[\]()]/);
token.name = 'string-2';
} else if (aChar === '$') {
- stream.eatWhile(/[^ ]/);
+ if (stream.next() === '<') {
+ stream.eatWhile(/[^ >]/);
+ stream.next();
+ }
token.name = 'string-2';
} else if (aChar === '|' && state.expectVariable) {
@@ -118,7 +121,6 @@ CodeMirror.defineMode('smalltalk', function(config) {
state.context = token.context;
state.expectVariable = token.eos;
- state.lastToken = token;
return token.name;
},
diff --git a/mode/sql/index.html b/mode/sql/index.html
index 7ec938a121..8a2495ca24 100644
--- a/mode/sql/index.html
+++ b/mode/sql/index.html
@@ -6,7 +6,6 @@
-