155 changes: 155 additions & 0 deletions addon/hint/show-hint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
CodeMirror.showHint = function(cm, getHints, options) {
if (!options) options = {};
var startCh = cm.getCursor().ch, continued = false;

function startHinting() {
// We want a single cursor position.
if (cm.somethingSelected()) return;

if (options.async)
getHints(cm, showHints, options);
else
return showHints(getHints(cm, options));
}

function showHints(data) {
if (!data || !data.list.length) return;
var completions = data.list;
// When there is only one completion, use it directly.
if (!continued && options.completeSingle !== false && completions.length == 1) {
cm.replaceRange(completions[0], data.from, data.to);
return true;
}

// Build the select widget
var hints = document.createElement("ul"), selectedHint = 0;
hints.className = "CodeMirror-hints";
for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(document.createElement("li"));
elt.className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
elt.appendChild(document.createTextNode(completions[i]));
elt.hintId = i;
}
var pos = cm.cursorCoords(options.alignWithWord !== false ? data.from : null);
var left = pos.left, top = pos.bottom, below = true;
hints.style.left = left + "px";
hints.style.top = top + "px";
document.body.appendChild(hints);

// If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
var box = hints.getBoundingClientRect();
var overlapX = box.right - winW, overlapY = box.bottom - winH;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px";
overlapX -= (box.right - box.left) - winW;
}
hints.style.left = (left = pos.left - overlapX) + "px";
}
if (overlapY > 0) {
var height = box.bottom - box.top;
if (box.top - (pos.bottom - pos.top) - height > 0) {
overlapY = height + (pos.bottom - pos.top);
below = false;
} else if (height > winH) {
hints.style.height = (winH - 5) + "px";
overlapY -= height - winH;
}
hints.style.top = (top = pos.bottom - overlapY) + "px";
}

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

function screenAmount() {
return Math.floor(hints.clientHeight / hints.firstChild.offsetHeight) || 1;
}

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

cm.addKeyMap(ourMap);
cm.on("cursorActivity", cursorActivity);
var closingOnBlur;
function onBlur(){ closingOnBlur = setTimeout(close, 100); };
function onFocus(){ clearTimeout(closingOnBlur); };
cm.on("blur", onBlur);
cm.on("focus", onFocus);
var startScroll = cm.getScrollInfo();
function onScroll() {
var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
var newTop = top + startScroll.top - curScroll.top, point = newTop;
if (!below) point += hints.offsetHeight;
if (point <= editor.top || point >= editor.bottom) return close();
hints.style.top = newTop + "px";
hints.style.left = (left + startScroll.left - curScroll.left) + "px";
}
cm.on("scroll", onScroll);
CodeMirror.on(hints, "dblclick", function(e) {
var t = e.target || e.srcElement;
if (t.hintId != null) {selectedHint = t.hintId; pick();}
setTimeout(function(){cm.focus();}, 20);
});
CodeMirror.on(hints, "click", function(e) {
var t = e.target || e.srcElement;
if (t.hintId != null) changeActive(t.hintId);
setTimeout(function(){cm.focus();}, 20);
});

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

var pos = cm.getCursor(), len = cm.getLine(pos.line).length;
if (pos.line != lastPos.line || len - pos.ch != lastLen - lastPos.ch ||
pos.ch < startCh || cm.somethingSelected())
close();
else
once = setTimeout(function(){close(); continued = true; startHinting();}, 70);
}
return true;
}

return startHinting();
};
21 changes: 4 additions & 17 deletions addon/hint/xml-hint.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@

(function() {

CodeMirror.xmlHints = [];

CodeMirror.xmlHint = function(cm, simbol) {

if(simbol.length > 0) {
var cursor = cm.getCursor();
cm.replaceSelection(simbol);
cursor = {line: cursor.line, ch: cursor.ch + 1};
cm.setCursor(cursor);
}

CodeMirror.simpleHint(cm, getHint);
};

var getHint = function(cm) {
CodeMirror.xmlHint = function(cm) {

var cursor = cm.getCursor();

if (cursor.ch > 0) {

var text = cm.getRange({line: 0, ch: 0}, cursor);
var text = cm.getRange(CodeMirror.Pos(0, 0), cursor);
var typed = '';
var simbol = '';
for(var i = text.length - 1; i >= 0; i--) {
Expand Down Expand Up @@ -51,10 +38,10 @@

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

var getActiveElement = function(text) {
Expand Down
121 changes: 121 additions & 0 deletions addon/lint/javascript-lint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
(function() {

var bogus = [ "Dangerous comment" ];

var warnings = [ [ "Expected '{'",
"Statement body should be inside '{ }' braces." ] ];

var errors = [ "Missing semicolon", "Extra comma", "Missing property name",
"Unmatched ", " and instead saw", " is not defined",
"Unclosed string", "Stopping, unable to continue" ];

CodeMirror.javascriptValidator = function(text) {
JSHINT(text);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
return result;
};

function cleanup(error) {
// All problems are warnings by default
fixWith(error, warnings, "warning", true);
fixWith(error, errors, "error");

return isBogus(error) ? null : error;
}

function fixWith(error, fixes, severity, force) {
var description, fix, find, replace, found;

description = error.description;

for ( var i = 0; i < fixes.length; i++) {
fix = fixes[i];
find = (typeof fix === "string" ? fix : fix[0]);
replace = (typeof fix === "string" ? null : fix[1]);
found = description.indexOf(find) !== -1;

if (force || found) {
error.severity = severity;
}
if (found && replace) {
error.description = replace;
}
}
}

function isBogus(error) {
var description = error.description;
for ( var i = 0; i < bogus.length; i++) {
if (description.indexOf(bogus[i]) !== -1) {
return true;
}
}
return false;
}

function parseErrors(errors, output) {
for ( var i = 0; i < errors.length; i++) {
var error = errors[i];
if (error) {
var linetabpositions, index;

linetabpositions = [];

// This next block is to fix a problem in jshint. Jshint
// replaces
// all tabs with spaces then performs some checks. The error
// positions (character/space) are then reported incorrectly,
// not taking the replacement step into account. Here we look
// at the evidence line and try to adjust the character position
// to the correct value.
if (error.evidence) {
// Tab positions are computed once per line and cached
var tabpositions = linetabpositions[error.line];
if (!tabpositions) {
var evidence = error.evidence;
tabpositions = [];
// ugggh phantomjs does not like this
// forEachChar(evidence, function(item, index) {
Array.prototype.forEach.call(evidence, function(item,
index) {
if (item === '\t') {
// First col is 1 (not 0) to match error
// positions
tabpositions.push(index + 1);
}
});
linetabpositions[error.line] = tabpositions;
}
if (tabpositions.length > 0) {
var pos = error.character;
tabpositions.forEach(function(tabposition) {
if (pos > tabposition) pos -= 1;
});
error.character = pos;
}
}

var start = error.character - 1, end = start + 1;
if (error.evidence) {
index = error.evidence.substring(start).search(/.\b/);
if (index > -1) {
end += index;
}
}

// Convert to format expected by validation service
error.description = error.reason;// + "(jshint)";
error.start = error.character;
error.end = end;
error = cleanup(error);

if (error)
output.push({message: error.description,
severity: error.severity,
from: CodeMirror.Pos(error.line - 1, start),
to: CodeMirror.Pos(error.line - 1, end)});
}
}
}
})();
14 changes: 14 additions & 0 deletions addon/lint/json-lint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Depends on jsonlint.js from https://github.com/zaach/jsonlint

CodeMirror.jsonValidator = function(text) {
var found = [];
jsonlint.parseError = function(str, hash) {
var loc = hash.loc;
found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
message: str});
};
try { jsonlint.parse(text); }
catch(e) {}
return found;
};
96 changes: 96 additions & 0 deletions addon/lint/lint.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* The lint marker gutter */
.CodeMirror-lint-markers {
width: 16px;
}

.CodeMirror-lint-tooltip {
background-color: infobackground;
border: 1px solid black;
border-radius: 4px 4px 4px 4px;
color: infotext;
font-family: monospace;
font-size: 10pt;
overflow: hidden;
padding: 2px 5px;
position: fixed;
white-space: pre;
z-index: 100;
max-width: 600px;
opacity: 0;
transition: opacity .4s;
-moz-transition: opacity .4s;
-webkit-transition: opacity .4s;
-o-transition: opacity .4s;
-ms-transition: opacity .4s;
}

.CodeMirror-lint-span-error, .CodeMirror-lint-span-warning {
background-position: left bottom;
background-repeat: repeat-x;
}

.CodeMirror-lint-span-error {
background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
;
}

.CodeMirror-lint-span-warning {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
}

.CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning {
background-position: center center;
background-repeat: no-repeat;
cursor: pointer;
display: inline-block;
height: 16px;
width: 16px;
vertical-align: middle;
position: relative;
}

.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
padding-left: 18px;
background-position: top left;
background-repeat: no-repeat;
}

.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAPVvcvWHiPVucvRuc+ttcfV6f91KVN5LU99PV/FZY/JhaM4oN84pONE4Rd1ATfJLWutVYPRgbdxpcsgWKMgZKs4lNfE/UvE/U+artcpdSc5uXveimslHPuBhW/eJhfV5efaCgO2CgP+/v+PExP///////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACUALAAAAAAQABAAAAZ+wJJwSCwaScgkySgkjTQZTkYzWhadnE5oE+pwqkSshwQqkzxfa4kkQXxEpA9J9EFI1KQGQQBAigYCBA14ExEWF0gXihETeA0QD3AkD5QQg0NsDnAJmwkOd5gYFSQKpXAFDBhqaxgLBwQBBAapq00YEg0UDRKqTGtKSL7Cw8JBADs=");
}

.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
background-image: url("data:image/gif;base64,R0lGODlhEAAQANUAAP7bc//egf/ij/7ijv/jl/7kl//mnv7lnv/uwf7CTP7DTf7DT/7IW//Na/7Na//NbP7QdP/dmbltAIJNAF03AMSAJMSCLKqASa2DS6uBSquCSrGHTq6ETbCHT7WKUrKIUcCVXL+UXMOYX8GWXsSZYMiib6+ETbOIUcOXX86uhd3Muf///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACsALAAAAAAQABAAAAZowJVwSCwaj0ihikRSJYcoBEL0XKlGkcjImQQhJBREKFnyICoThKeE/AAW6AXgdPyUAgrLJBEo0YsbAQyDhAEdRRwDDw8OaA4NDQImRBgFEJdglxAEGEQZKQcHBqOkKRpFF6mqq1WtrUEAOw==");
}

.CodeMirror-lint-marker-multiple {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII=");
background-repeat: no-repeat;
background-position: right bottom;
width: 100%; height: 100%;
}

/* Styles for the overview ruler
.annotationOverview {
cursor: pointer;
border-radius: 2px;
left: 2px;
width: 8px;
}
.annotationOverview.error {
background-color: lightcoral;
border: 1px solid darkred;
}
.annotationOverview.warning {
background-color: Gold;
border: 1px solid black;
}
.annotationHTML.overlay {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJEAQvB2JVdrAAAAAdaVRYdENvbW1lbnQAAAAAAENyZWF0ZWQgd2l0aCBHSU1QZC5lBwAAAD1JREFUCNdtjkESADAEAzemf69f66HMqGlOIhYiFRFRtSQBWAY7mzx+EDTL6sSgb1jTk7Q87rxyqe37fXsAa78gLyZnRgEAAAAASUVORK5CYII=");
background-position: right bottom;
position: relative;
top: -16px;
}
*/
182 changes: 182 additions & 0 deletions addon/lint/lint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
CodeMirror.validate = (function() {
var GUTTER_ID = "CodeMirror-lint-markers";
var SEVERITIES = /^(?:error|warning)$/;

function showTooltip(e, content) {
var tt = document.createElement("div");
tt.className = "CodeMirror-lint-tooltip";
tt.appendChild(content.cloneNode(true));
document.body.appendChild(tt);

function position(e) {
if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
tt.style.top = (e.clientY - tt.offsetHeight - 5) + "px";
tt.style.left = (e.clientX + 5) + "px";
}
CodeMirror.on(document, "mousemove", position);
position(e);
tt.style.opacity = 1;
return tt;
}
function rm(elt) {
if (elt.parentNode) elt.parentNode.removeChild(elt);
}
function hideTooltip(tt) {
if (!tt.parentNode) return;
if (tt.style.opacity == null) rm(tt);
tt.style.opacity = 0;
setTimeout(function() { rm(tt); }, 600);
}

function LintState(cm, options, hasGutter) {
this.marked = [];
this.options = options;
this.timeout = null;
this.hasGutter = hasGutter;
this.onMouseOver = function(e) { onMouseOver(cm, e); };
}

function parseOptions(options) {
if (options instanceof Function) return {getAnnotations: options};
else if (!options || !options.getAnnotations) throw new Error("Required option 'getAnnotations' missing (lint addon)");
return options;
}

function clearMarks(cm) {
var state = cm._lintState;
if (state.hasGutter) cm.clearGutter(GUTTER_ID);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
state.marked.length = 0;
}

function makeMarker(labels, severity, multiple) {
var marker = document.createElement("div"), inner = marker;
marker.className = "CodeMirror-lint-marker-" + severity;
if (multiple) {
inner = marker.appendChild(document.createElement("div"));
inner.className = "CodeMirror-lint-marker-multiple";
}

var tooltip;
CodeMirror.on(inner, "mouseover", function(e) { tooltip = showTooltip(e, labels); });
CodeMirror.on(inner, "mouseout", function() { if (tooltip) hideTooltip(tooltip); });

return marker;
}

function getMaxSeverity(a, b) {
if (a == "error") return a;
else return b;
}

function groupByLine(annotations) {
var lines = [];
for (var i = 0; i < annotations.length; ++i) {
var ann = annotations[i], line = ann.from.line;
(lines[line] || (lines[line] = [])).push(ann);
}
return lines;
}

function annotationTooltip(ann) {
var severity = ann.severity;
if (!SEVERITIES.test(severity)) severity = "error";
var tip = document.createElement("div");
tip.className = "CodeMirror-lint-message-" + severity;
tip.appendChild(document.createTextNode(ann.message));
return tip;
}

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()));
}

function updateLinting(cm, annotationsNotSorted) {
clearMarks(cm);
var state = cm._lintState, options = state.options;

var annotations = groupByLine(annotationsNotSorted);

for (var line = 0; line < annotations.length; ++line) {
var anns = annotations[line];
if (!anns) continue;

var maxSeverity = null;
var tipLabel = state.hasGutter && document.createDocumentFragment();

for (var i = 0; i < anns.length; ++i) {
var ann = anns[i];
var severity = ann.severity;
if (!SEVERITIES.test(severity)) severity = "error";
maxSeverity = getMaxSeverity(maxSeverity, severity);

if (options.formatAnnotation) ann = options.formatAnnotation(ann);
if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));

if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
className: "CodeMirror-lint-span-" + severity,
__annotation: ann
}));
}

if (state.hasGutter)
cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1));
}
}

function onChange(cm) {
var state = cm._lintState;
clearTimeout(state.timeout);
state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
}

function popupSpanTooltip(ann, e) {
var tooltip = showTooltip(e, annotationTooltip(ann));
var target = e.target || e.srcElement;
CodeMirror.on(target, "mouseout", hide);
function hide() {
CodeMirror.off(target, "mouseout", hide);
hideTooltip(tooltip);
}
}

// When the mouseover fires, the cursor might not actually be over
// the character itself yet. These pairs of x,y offsets are used to
// probe a few nearby points when no suitable marked range is found.
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;
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]}));
for (var j = 0; j < spans.length; ++j) {
var span = spans[j], ann = span.__annotation;
if (ann) return popupSpanTooltip(ann, e);
}
}
}

CodeMirror.defineOption("lintWith", false, function(cm, val, old) {
if (old && old != CodeMirror.Init) {
clearMarks(cm);
cm.off("change", onChange);
CodeMirror.off(cm.getWrapperElement(), "mouseover", cm._lintState.onMouseOver);
delete cm._lintState;
}

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);
cm.on("change", onChange);
CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
startLinting(cm);
}
});
})();
5 changes: 2 additions & 3 deletions addon/runmode/runmode-standalone.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,12 @@ StringStream.prototype = {
indentation: function() {return 0;},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
if (consume !== false) this.pos += pattern.length;
return true;
}
}
else {
} else {
var match = this.string.slice(this.pos).match(pattern);
if (match && consume !== false) this.pos += match[0].length;
return match;
Expand Down
5 changes: 2 additions & 3 deletions addon/runmode/runmode.node.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,12 @@ StringStream.prototype = {
indentation: function() {return 0;},
match: function(pattern, consume, caseInsensitive) {
if (typeof pattern == "string") {
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
if (consume !== false) this.pos += pattern.length;
return true;
}
}
else {
} else {
var match = this.string.slice(this.pos).match(pattern);
if (match && consume !== false) this.pos += match[0].length;
return match;
Expand Down
90 changes: 52 additions & 38 deletions addon/search/match-highlighter.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,60 @@
// Define match-highlighter commands. Depends on searchcursor.js
// Use by attaching the following function call to the cursorActivity event:
//myCodeMirror.matchHighlight(minChars);
// And including a special span.CodeMirror-matchhighlight css class (also optionally a separate one for .CodeMirror-focused -- see demo matchhighlighter.html)
// Highlighting text that matches the selection
//
// Defines an option highlightSelectionMatches, which, when enabled,
// will style strings that match the selection throughout the
// document.
//
// The option can be set to true to simply enable it, or to a
// {minChars, style} object to explicitly configure it. minChars is
// the minimum amount of characters that should be selected for the
// behavior to occur, and style is the token style to apply to the
// matches. This will be prefixed by "cm-" to create an actual CSS
// class name.

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

function MatchHighlightState() {
this.marked = [];
}
function getMatchHighlightState(cm) {
return cm._matchHighlightState || (cm._matchHighlightState = new MatchHighlightState());
}

function clearMarks(cm) {
var state = getMatchHighlightState(cm);
for (var i = 0; i < state.marked.length; ++i)
state.marked[i].clear();
state.marked = [];
}

function markDocument(cm, className, minChars) {
clearMarks(cm);
minChars = (typeof minChars !== 'undefined' ? minChars : DEFAULT_MIN_CHARS);
if (cm.somethingSelected() && cm.getSelection().replace(/^\s+|\s+$/g, "").length >= minChars) {
var state = getMatchHighlightState(cm);
var query = cm.getSelection();
cm.operation(function() {
if (cm.lineCount() < 2000) { // This is too expensive on big documents.
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) {
//Only apply matchhighlight to the matches other than the one actually selected
if (cursor.from().line !== cm.getCursor(true).line ||
cursor.from().ch !== cm.getCursor(true).ch)
state.marked.push(cm.markText(cursor.from(), cursor.to(),
{className: className}));
}
}
});
}
function State(options) {
this.minChars = typeof options == "object" && options.minChars || DEFAULT_MIN_CHARS;
this.style = typeof options == "object" && options.style || DEFAULT_TOKEN_STYLE;
this.overlay = null;
}

CodeMirror.defineExtension("matchHighlight", function(className, minChars) {
markDocument(this, className, minChars);
CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
cm._matchHighlightState = new State(val);
cm.on("cursorActivity", highlightMatches);
} else if (!val && prev) {
var over = cm._matchHighlightState.overlay;
if (over) cm.removeOverlay(over);
cm._matchHighlightState = null;
cm.off("cursorActivity", highlightMatches);
}
});

function highlightMatches(cm) {
cm.operation(function() {
var state = cm._matchHighlightState;
if (state.overlay) {
cm.removeOverlay(state.overlay);
state.overlay = null;
}

if (!cm.somethingSelected()) return;
var selection = cm.getSelection().replace(/^\s+|\s+$/g, "");
if (selection.length < state.minChars) return;

cm.addOverlay(state.overlay = makeOverlay(selection, state.style));
});
}

function makeOverlay(query, style) {
return {token: function(stream) {
if (stream.match(query)) return style;
stream.next();
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
}};
}
})();
10 changes: 5 additions & 5 deletions addon/search/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
var state = getSearchState(cm);
var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
if (!cursor.find(rev)) {
cursor = getSearchCursor(cm, state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0});
cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
if (!cursor.find(rev)) return;
}
cm.setSelection(cursor.from(), cursor.to());
Expand Down Expand Up @@ -100,7 +100,7 @@
} else {
clearSearch(cm);
var cursor = getSearchCursor(cm, query, cm.getCursor());
function advance() {
var advance = function() {
var start = cursor.from(), match;
if (!(match = cursor.findNext())) {
cursor = getSearchCursor(cm, query);
Expand All @@ -110,12 +110,12 @@
cm.setSelection(cursor.from(), cursor.to());
confirmDialog(cm, doReplaceConfirm, "Replace?",
[function() {doReplace(match);}, advance]);
}
function doReplace(match) {
};
var doReplace = function(match) {
cursor.replace(typeof query == "string" ? text :
text.replace(/\$(\d)/, function(_, i) {return match[i];}));
advance();
}
};
advance();
}
});
Expand Down
29 changes: 17 additions & 12 deletions addon/search/searchcursor.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
(function(){
var Pos = CodeMirror.Pos;

function SearchCursor(cm, query, pos, caseFold) {
this.atOccurrence = false; this.cm = cm;
if (caseFold == null && typeof query == "string") caseFold = false;

pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0};
pos = pos ? cm.clipPos(pos) : Pos(0, 0);
this.pos = {from: pos, to: pos};

// The matches method is filled in based on the type of query.
Expand Down Expand Up @@ -31,8 +33,8 @@
start = match && match.index;
}
if (match && match[0])
return {from: {line: pos.line, ch: start},
to: {line: pos.line, ch: start + match[0].length},
return {from: Pos(pos.line, start),
to: Pos(pos.line, start + match[0].length),
match: match};
};
} else { // String query
Expand All @@ -50,8 +52,8 @@
var line = fold(cm.getLine(pos.line)), len = query.length, match;
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1)
: (match = line.indexOf(query, pos.ch)) != -1)
return {from: {line: pos.line, ch: match},
to: {line: pos.line, ch: match + len}};
return {from: Pos(pos.line, match),
to: Pos(pos.line, match + len)};
};
}
} else {
Expand All @@ -72,7 +74,7 @@
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
return;
var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB};
var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB);
return {from: reverse ? end : start, to: reverse ? start : end};
}
};
Expand All @@ -87,25 +89,26 @@
find: function(reverse) {
var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to);
function savePosAndFail(line) {
var pos = {line: line, ch: 0};
var pos = Pos(line, 0);
self.pos = {from: pos, to: pos};
self.atOccurrence = false;
return false;
}

for (;;) {
if (this.pos = this.matches(reverse, pos)) {
if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); }
this.atOccurrence = true;
return this.pos.match || true;
}
if (reverse) {
if (!pos.line) return savePosAndFail(0);
pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length};
pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length);
}
else {
var maxLine = this.cm.lineCount();
if (pos.line == maxLine - 1) return savePosAndFail(maxLine);
pos = {line: pos.line+1, ch: 0};
pos = Pos(pos.line + 1, 0);
}
}
},
Expand All @@ -114,9 +117,11 @@
to: function() {if (this.atOccurrence) return this.pos.to;},

replace: function(newText) {
var self = this;
if (this.atOccurrence)
self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to);
if (!this.atOccurrence) return;
var lines = CodeMirror.splitLines(newText);
this.cm.replaceRange(lines, this.pos.from, this.pos.to);
this.pos.to = Pos(this.pos.from.line + lines.length - 1,
lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0));
}
};

Expand Down
39 changes: 39 additions & 0 deletions addon/selection/active-line.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Because sometimes you need to style the cursor's line.
//
// Adds an option 'styleActiveLine' which, when enabled, gives the
// active line's wrapping <div> the CSS class "CodeMirror-activeline",
// and gives its background <div> the class "CodeMirror-activeline-background".

(function() {
"use strict";
var WRAP_CLASS = "CodeMirror-activeline";
var BACK_CLASS = "CodeMirror-activeline-background";

CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
updateActiveLine(cm);
cm.on("cursorActivity", updateActiveLine);
} else if (!val && prev) {
cm.off("cursorActivity", updateActiveLine);
clearActiveLine(cm);
delete cm._activeLine;
}
});

function clearActiveLine(cm) {
if ("_activeLine" in cm) {
cm.removeLineClass(cm._activeLine, "wrap", WRAP_CLASS);
cm.removeLineClass(cm._activeLine, "background", BACK_CLASS);
}
}

function updateActiveLine(cm) {
var line = cm.getLineHandle(cm.getCursor().line);
if (cm._activeLine == line) return;
clearActiveLine(cm);
cm.addLineClass(line, "wrap", WRAP_CLASS);
cm.addLineClass(line, "background", BACK_CLASS);
cm._activeLine = line;
}
})();
34 changes: 34 additions & 0 deletions addon/selection/mark-selection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Because sometimes you need to mark the selected *text*.
//
// Adds an option 'styleSelectedText' which, when enabled, gives
// selected text the CSS class "CodeMirror-selectedtext".

(function() {
"use strict";

CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
updateSelectedText(cm);
cm.on("cursorActivity", updateSelectedText);
} else if (!val && prev) {
cm.off("cursorActivity", updateSelectedText);
clearSelectedText(cm);
delete cm._selectionMark;
}
});

function clearSelectedText(cm) {
if (cm._selectionMark) cm._selectionMark.clear();
}

function updateSelectedText(cm) {
clearSelectedText(cm);

if (cm.somethingSelected())
cm._selectionMark = cm.markText(cm.getCursor("start"), cm.getCursor("end"),
{className: "CodeMirror-selectedtext"});
else
cm._selectionMark = null;
}
})();
2 changes: 1 addition & 1 deletion bin/compress
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ walk("lib/");
walk("addon/");
walk("mode/");

if (!blob) help(false);
if (!local && !blob) help(false);

if (files.length) {
console.log("Some speficied files were not found: " +
Expand Down
12 changes: 3 additions & 9 deletions demo/activeline.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<script src="../addon/selection/active-line.js"></script>
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.activeline {background: #e8f2ff !important;}
.CodeMirror-activeline-background {background: #e8f2ff !important;}
</style>
</head>
<body>
Expand Down Expand Up @@ -57,17 +58,10 @@ <h1>CodeMirror: Active Line Demo</h1>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "application/xml",
styleActiveLine: true,
lineNumbers: true,
lineWrapping: true
});
var hlLine = editor.addLineClass(0, "background", "activeline");
editor.on("cursorActivity", function() {
var cur = editor.getLineHandle(editor.getCursor().line);
if (cur != hlLine) {
editor.removeLineClass(hlLine, "background", "activeline");
hlLine = editor.addLineClass(cur, "background", "activeline");
}
});
</script>

<p>Styling the current cursor line.</p>
Expand Down
61 changes: 61 additions & 0 deletions demo/bidi.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Bi-directional Text Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/xml/xml.js"></script>
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
</style>
</head>
<body>
<h1>CodeMirror: Bi-directional Text Demo</h1>

<form><textarea id="code" name="code"><!-- Piece of the CodeMirror manual, 'translated' into Arabic by
Google Translate -->

<dl>
<dt id=option_value><code>value (string or Doc)</code></dt>
<dd>قيمة البداية المحرر. يمكن أن تكون سلسلة، أو. كائن مستند.</dd>
<dt id=option_mode><code>mode (string or object)</code></dt>
<dd>وضع الاستخدام. عندما لا تعطى، وهذا الافتراضي إلى الطريقة الاولى
التي تم تحميلها. قد يكون من سلسلة، والتي إما أسماء أو ببساطة هو وضع
MIME نوع المرتبطة اسطة. بدلا من ذلك، قد يكون من كائن يحتوي على
خيارات التكوين لواسطة، مع <code>name</code> الخاصية التي وضع أسماء
(على سبيل المثال <code>{name: "javascript", json: true}</code>).
صفحات التجريبي لكل وضع تحتوي على معلومات حول ما معلمات تكوين وضع
يدعمها. يمكنك أن تطلب CodeMirror التي تم تعريفها طرق وأنواع MIME
الكشف على <code>CodeMirror.modes</code>
و <code>CodeMirror.mimeModes</code> الكائنات. وضع خرائط الأسماء
الأولى لمنشئات الخاصة بهم، وخرائط لأنواع MIME 2 المواصفات
واسطة.</dd>
<dt id=option_theme><code>theme (string)</code></dt>
<dd>موضوع لنمط المحرر مع. يجب عليك التأكد من الملف CSS تحديد
المقابلة <code>.cm-s-[name]</code> يتم تحميل أنماط (انظر
<a href=../theme/><code>theme</code></a> الدليل في التوزيع).
الافتراضي هو <code>"default"</code> ، والتي تم تضمينها في
الألوان <code>codemirror.css</code>. فمن الممكن استخدام فئات متعددة
في تطبيق السمات مرة واحدة على سبيل المثال <code>"foo bar"</code>
سيتم تعيين كل من <code>cm-s-foo</code> و <code>cm-s-bar</code>
الطبقات إلى المحرر.</dd>
</dl>
</textarea></form>

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

<p>Demonstration of bi-directional text support. See
the <a href="http://marijnhaverbeke.nl/blog/cursor-in-bidi-text.html">related
blog post</a> for more background.</p>

</body>
</html>
2 changes: 1 addition & 1 deletion demo/btree.html
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ <h1>CodeMirror: B-Tree visualization</h1>
drawTree(sub, node.children[i]);
}
}
drawTree(out, editor.view.doc);
drawTree(out, editor.getDoc());
}

function fillEditor() {
Expand Down
98 changes: 98 additions & 0 deletions demo/buffers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Multiple Buffer & Split View Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="../mode/css/css.js"></script>
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css" id=style>
.CodeMirror {border: 1px solid black; height: 250px;}
</style>
</head>
<body>
<h1>CodeMirror: Multiple Buffer & Split View Demo</h1>

<div id=code_top></div>
<div>
Select buffer: <select id=buffers_top></select>
&nbsp; &nbsp; <button onclick="newBuf('top')">New buffer</button>
</div>
<div id=code_bot></div>
<div>
Select buffer: <select id=buffers_bot></select>
&nbsp; &nbsp; <button onclick="newBuf('bot')">New buffer</button>
</div>

<script id=script>
var sel_top = document.getElementById("buffers_top");
CodeMirror.on(sel_top, "change", function() {
selectBuffer(ed_top, sel_top.options[sel_top.selectedIndex].value);
});

var sel_bot = document.getElementById("buffers_bot");
CodeMirror.on(sel_bot, "change", function() {
selectBuffer(ed_bot, sel_bot.options[sel_bot.selectedIndex].value);
});

var buffers = {};

function openBuffer(name, text, mode) {
buffers[name] = CodeMirror.Doc(text, mode);
var opt = document.createElement("option");
opt.appendChild(document.createTextNode(name));
sel_top.appendChild(opt);
sel_bot.appendChild(opt.cloneNode(true));
}

function newBuf(where) {
var name = prompt("Name for the buffer", "*scratch*");
if (name == null) return;
if (buffers.hasOwnProperty(name)) {
alert("There's already a buffer by that name.");
return;
}
openBuffer(name, "", "javascript");
selectBuffer(where == "top" ? ed_top : ed_bot, name);
var sel = where == "top" ? sel_top : sel_bot;
sel.value = name;
}

function selectBuffer(editor, name) {
var buf = buffers[name];
if (buf.getEditor()) buf = buf.linkedDoc({sharedHist: true});
var old = editor.swapDoc(buf);
var linked = old.iterLinkedDocs(function(doc) {linked = doc;});
if (linked) {
// Make sure the document in buffers is the one the other view is looking at
for (var name in buffers) if (buffers[name] == old) buffers[name] = linked;
old.unlinkDoc(linked);
}
editor.focus();
}

function nodeContent(id) {
var node = document.getElementById(id), val = node.textContent || node.innerText;
val = val.slice(val.match(/^\s*/)[0].length, val.length - val.match(/\s*$/)[0].length) + "\n";
return val;
}
openBuffer("js", nodeContent("script"), "javascript");
openBuffer("css", nodeContent("style"), "css");

var ed_top = CodeMirror(document.getElementById("code_top"), {lineNumbers: true});
selectBuffer(ed_top, "js");
var ed_bot = CodeMirror(document.getElementById("code_bot"), {lineNumbers: true});
selectBuffer(ed_bot, "js");
</script>

<p>Demonstration of
using <a href="../doc/manual.html#linkedDoc">linked documents</a>
to provide a split view on a document, and
using <a href="../doc/manual.html#swapDoc"><code>swapDoc</code></a>
to use a single editor to display multiple documents.</p>

</body>
</html>
59 changes: 59 additions & 0 deletions demo/closebrackets.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Closebrackets Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../addon/edit/closebrackets.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../doc/docs.css">
<style type="text/css">
.CodeMirror {border-top: 1px solid #888; border-bottom: 1px solid #888;}
</style>
</head>
<body>

<h1>CodeMirror: Closebrackets Demo</h1>

<p>Type a bracket like '[', '(', '{', '&quot;', or '''
and <a href="../doc/manual.html#addon_closebrackets">the addon</a>
will auto-close it. Type the closing variant when directly in
front of a matching character and it will overwrite it.</p>

<form><textarea id="code" name="code">(function() {
var DEFAULT_BRACKETS = "()[]{}''\"\"";

CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
var wasOn = old && old != CodeMirror.Init;
if (val && !wasOn)
cm.addKeyMap(buildKeymap(typeof val == "string" ? val : DEFAULT_BRACKETS));
else if (!val && wasOn)
cm.removeKeyMap("autoCloseBrackets");
});

function buildKeymap(pairs) {
var map = {name : "autoCloseBrackets"};
for (var i = 0; i < pairs.length; i += 2) (function(left, right) {
function maybeOverwrite(cm) {
var cur = cm.getCursor(), ahead = cm.getRange(cur, CodeMirror.Pos(cur.line, cur.ch + 1));
if (ahead != right) return CodeMirror.Pass;
else cm.execCommand("goCharRight");
}
map["'" + left + "'"] = function(cm) {
if (left == right && maybeOverwrite(cm) != CodeMirror.Pass) return;
var cur = cm.getCursor(), ahead = CodeMirror.Pos(cur.line, cur.ch + 1);
cm.replaceSelection(left + right, {head: ahead, anchor: ahead});
};
if (left != right) map["'" + right + "'"] = maybeOverwrite;
})(pairs.charAt(i), pairs.charAt(i + 1));
return map;
}
})();
</textarea></form>

<script type="text/javascript">
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {autoCloseBrackets: true});
</script>
</body>
</html>
6 changes: 3 additions & 3 deletions demo/complete.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<title>CodeMirror: Autocomplete Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/simple-hint.js"></script>
<link rel="stylesheet" href="../addon/hint/simple-hint.css">
<script src="../addon/hint/show-hint.js"></script>
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<script src="../addon/hint/javascript-hint.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="../doc/docs.css">
Expand Down Expand Up @@ -59,7 +59,7 @@ <h1>CodeMirror: Autocomplete demo</h1>

<script>
CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.simpleHint(cm, CodeMirror.javascriptHint);
CodeMirror.showHint(cm, CodeMirror.javascriptHint);
}
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
Expand Down
10 changes: 9 additions & 1 deletion demo/formatting.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
<body>
<h1>CodeMirror: Formatting demo</h1>

<p><strong>Note:</strong> The formatting addon receives a fair
amount of bug requests. I (the maintainer of CodeMirror) do not
intend to spend time on improving it. Pull requests (if clean and
intelligent) are welcome, but you should see this code as a
proof-of-concept (using CodeMirror's mode tokenizers to help
format code), not a finished, robust module.</p>

<form><textarea id="code" name="code"><script> function (s,e){ for(var i=0; i < 1; i++) test("test();a=1");} </script>
<script>
function test(c){ for (var i = 0; i < 10; i++){ process("a.b();c = null;", 300);}
Expand Down Expand Up @@ -72,8 +79,9 @@ <h1>CodeMirror: Formatting demo</h1>
}

function commentSelection(isComment) {
var range = getSelectedRange();
var range = getSelectedRange(), selStart = editor.getCursor("start");
editor.commentRange(isComment, range.from, range.to);
editor.setSelection(selStart, editor.getCursor("end"));
}
</script>

Expand Down
90 changes: 90 additions & 0 deletions demo/lint.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Linter Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../mode/javascript/javascript.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jshint/r07/jshint.js"></script>
<script src="https://raw.github.com/zaach/jsonlint/79b553fb65c192add9066da64043458981b3972b/lib/jsonlint.js"></script>
<link rel="stylesheet" href="../doc/docs.css">
<link rel="stylesheet" href="../addon/lint/lint.css">
<script src="../addon/lint/lint.js"></script>
<script src="../addon/lint/javascript-lint.js"></script>
<script src="../addon/lint/json-lint.js"></script>

<style type="text/css">
.CodeMirror {border: 1px solid black;}
</style>
</head>
<body>
<h1>CodeMirror: Linter Demo</h1>

<p><textarea id="code-js">var widgets = []
function updateHints() {
editor.operation(function(){
for (var i = 0; i < widgets.length; ++i)
editor.removeLineWidget(widgets[i]);
widgets.length = 0;

JSHINT(editor.getValue());
for (var i = 0; i < JSHINT.errors.length; ++i) {
var err = JSHINT.errors[i];
if (!err) continue;
var msg = document.createElement("div");
var icon = msg.appendChild(document.createElement("span"));
icon.innerHTML = "!!";
icon.className = "lint-error-icon";
msg.appendChild(document.createTextNode(err.reason));
msg.className = "lint-error";
widgets.push(editor.addLineWidget(err.line - 1, msg, {coverGutter: false, noHScroll: true}));
}
});
var info = editor.getScrollInfo();
var after = editor.charCoords({line: editor.getCursor().line + 1, ch: 0}, "local").top;
if (info.top + info.clientHeight < after)
editor.scrollTo(null, after - info.clientHeight + 3);
}
</textarea></p>

<p><textarea id="code-json">[
{
_id: "post 1",
"author": "Bob",
"content": "...",
"page_views": 5
},
{
"_id": "post 2",
"author": "Bob",
"content": "...",
"page_views": 9
},
{
"_id": "post 3",
"author": "Bob",
"content": "...",
"page_views": 8
}
]
</textarea></p>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code-js"), {
lineNumbers: true,
mode: "javascript",
gutters: ["CodeMirror-lint-markers"],
lintWith: CodeMirror.javascriptValidator
});

var editor_json = CodeMirror.fromTextArea(document.getElementById("code-json"), {
lineNumbers: true,
mode: "application/json",
gutters: ["CodeMirror-lint-markers"],
lintWith: CodeMirror.jsonValidator
});
</script>

</body>
</html>
36 changes: 36 additions & 0 deletions demo/markselection.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>CodeMirror: Match Highlighter Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../addon/search/searchcursor.js"></script>
<script src="../addon/selection/mark-selection.js"></script>
<link rel="stylesheet" href="../doc/docs.css">

<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
.CodeMirror-selected { background-color: blue !important; }
.CodeMirror-selectedtext { color: white; }
</style>
</head>
<body>
<h1>CodeMirror: Mark Selection Demo</h1>

<form><textarea id="code" name="code">Select something from here.
You'll see that the selection's foreground color changes to white!
Since, by default, CodeMirror only puts an independent "marker" layer
behind the text, you'll need something like this to change its colour.</textarea></form>

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

<p>Simple addon to easily mark (and style) selected text.</p>

</body>
</html>
16 changes: 9 additions & 7 deletions demo/matchhighlighter.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@

<style type="text/css">
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}

span.CodeMirror-matchhighlight { background: #e9e9e9 }
.CodeMirror-focused span.CodeMirror-matchhighlight { background: #e7e4ff; !important }
.CodeMirror-focused .cm-matchhighlight {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
background-position: bottom;
background-repeat: repeat-x;
}
</style>
</head>
<body>
Expand All @@ -24,13 +26,13 @@ <h1>CodeMirror: Match Highlighter Demo</h1>
Give it a try! No more hardToSpotVars.</textarea></form>

<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {lineNumbers : true});
editor.on("cursorActivity", function() {
editor.matchHighlight("CodeMirror-matchhighlight");
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
highlightSelectionMatches: true
});
</script>

<p>Highlight matches of selected text on select</p>
<p>Search and highlight occurences of the selected text.</p>

</body>
</html>
2 changes: 1 addition & 1 deletion demo/variableheight.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<body>
<h1>CodeMirror: Variable Height Demo</h1>

<form><textarea id="code" name="code">### A First Level Header
<form><textarea id="code" name="code"># A First Level Header

**Bold** text in a normal-size paragraph.

Expand Down
20 changes: 14 additions & 6 deletions demo/xmlcomplete.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
<title>CodeMirror: XML Autocomplete Demo</title>
<link rel="stylesheet" href="../lib/codemirror.css">
<script src="../lib/codemirror.js"></script>
<script src="../addon/hint/simple-hint.js"></script>
<link rel="stylesheet" href="../addon/hint/simple-hint.css">
<script src="../addon/hint/show-hint.js"></script>
<link rel="stylesheet" href="../addon/hint/show-hint.css">
<script src="../addon/edit/closetag.js"></script>
<script src="../addon/hint/xml-hint.js"></script>
<script src="../mode/xml/xml.js"></script>
Expand All @@ -23,7 +23,7 @@ <h1>CodeMirror: XML Autocomplete demo</h1>

<p>Type '&lt;' or space inside tag or
press <strong>ctrl-space</strong> to activate autocompletion. See
the code (<a href="../addon/hint/simple-hint.js">here</a>
the code (<a href="../addon/hint/show-hint.js">here</a>
and <a href="../addon/hint/xml-hint.js">here</a>) to figure out how
it works.</p>

Expand Down Expand Up @@ -57,14 +57,22 @@ <h1>CodeMirror: XML Autocomplete demo</h1>
'x-three'
];

CodeMirror.commands.autocomplete = function(cm) {
CodeMirror.showHint(cm, CodeMirror.xmlHint);
}
function passAndHint(cm) {
setTimeout(function() {cm.execCommand("autocomplete");}, 100);
return CodeMirror.Pass;
}

var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
value: '',
mode: 'text/html',
lineNumbers: true,
extraKeys: {
"' '": function(cm) { CodeMirror.xmlHint(cm, ' '); },
"'<'": function(cm) { CodeMirror.xmlHint(cm, '<'); },
"Ctrl-Space": function(cm) { CodeMirror.xmlHint(cm, ''); }
"' '": passAndHint,
"'<'": passAndHint,
"Ctrl-Space": "autocomplete"
},
autoCloseTags: true
});
Expand Down
10 changes: 10 additions & 0 deletions doc/compress.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<input type="hidden" id="download" name="download" value="codemirror-compressed.js"/>
<p>Version: <select id="version" onchange="setVersion(this);" style="padding: 1px">
<option value="http://codemirror.net/">HEAD</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v3.1;f=">3.1</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v3.02;f=">3.02</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v3.01;f=">3.01</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v3.0;f=">3.0</option>
<option value="http://marijnhaverbeke.nl/git/codemirror?a=blob_plain;hb=v2.38;f=">2.38</option>
Expand Down Expand Up @@ -99,6 +101,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<option value="http://codemirror.net/mode/plsql/plsql.js">plsql.js</option>
<option value="http://codemirror.net/mode/properties/properties.js">properties.js</option>
<option value="http://codemirror.net/mode/python/python.js">python.js</option>
<option value="http://codemirror.net/mode/q/q.js">q.js</option>
<option value="http://codemirror.net/mode/r/r.js">r.js</option>
<option value="http://codemirror.net/mode/rpm/changes/changes.js">rpm/changes.js</option>
<option value="http://codemirror.net/mode/rpm/spec/spec.js">rpm/spec.js</option>
Expand All @@ -117,6 +120,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<option value="http://codemirror.net/mode/stex/stex.js">stex.js</option>
<option value="http://codemirror.net/mode/tiddlywiki/tiddlywiki.js">tiddlywiki.js</option>
<option value="http://codemirror.net/mode/tiki/tiki.js">tiki.js</option>
<option value="http://codemirror.net/mode/turtle/turtle.js">turtle.js</option>
<option value="http://codemirror.net/mode/vb/vb.js">vb.js</option>
<option value="http://codemirror.net/mode/vbscript/vbscript.js">vbscript.js</option>
<option value="http://codemirror.net/mode/velocity/velocity.js">velocity.js</option>
Expand All @@ -132,6 +136,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<option value="http://codemirror.net/addon/edit/continuecomment.js">continuecomment.js</option>
<option value="http://codemirror.net/addon/edit/continuelist.js">continuelist.js</option>
<option value="http://codemirror.net/addon/edit/matchbrackets.js">matchbrackets.js</option>
<option value="http://codemirror.net/addon/edit/closebrackets.js">closebrackets.js</option>
<option value="http://codemirror.net/addon/fold/foldcode.js">foldcode.js</option>
<option value="http://codemirror.net/addon/fold/collapserange.js">collapserange.js</option>
<option value="http://codemirror.net/addon/format/formatting.js">formatting.js</option>
Expand All @@ -150,6 +155,11 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<option value="http://codemirror.net/addon/search/search.js">search.js</option>
<option value="http://codemirror.net/addon/search/searchcursor.js">searchcursor.js</option>
<option value="http://codemirror.net/addon/search/match-highlighter.js">match-highlighter.js</option>
<option value="http://codemirror.net/addon/selection/mark-selection.js">mark-selection.js</option>
<option value="http://codemirror.net/addon/selection/active-line.js">active-line.js</option>
<option value="http://codemirror.net/addon/lint/lint.js">lint.js</option>
<option value="http://codemirror.net/addon/lint/javascript-lint.js">javascript-lint.js</option>
<option value="http://codemirror.net/addon/lint/json-lint.js">json-lint.js</option>
</optgroup>
<optgroup label="Keymaps">
<option value="http://codemirror.net/keymap/emacs.js">emacs.js</option>
Expand Down
2 changes: 1 addition & 1 deletion doc/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ h2 {
}

h3 {
font-size: 1em;
font-size: 1.1em;
font-weight: bold;
margin: .4em 0;
}
Expand Down
795 changes: 532 additions & 263 deletions doc/manual.html

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions doc/modes.html
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<li><a href="../mode/pig/index.html">Pig Latin</a></li>
<li><a href="../mode/properties/index.html">Properties files</a></li>
<li><a href="../mode/python/index.html">Python</a></li>
<li><a href="../mode/q/index.html">Q</a></li>
<li><a href="../mode/r/index.html">R</a></li>
<li>RPM <a href="../mode/rpm/spec/index.html">spec</a> and <a href="../mode/rpm/changes/index.html">changelog</a></li>
<li><a href="../mode/rst/index.html">reStructuredText</a></li>
Expand All @@ -72,6 +73,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<li><a href="../mode/stex/index.html">sTeX, LaTeX</a></li>
<li><a href="../mode/tiddlywiki/index.html">Tiddlywiki</a></li>
<li><a href="../mode/tiki/index.html">Tiki wiki</a></li>
<li><a href="../mode/turtle/index.html">Turtle</a></li>
<li><a href="../mode/vb/index.html">VB.NET</a></li>
<li><a href="../mode/vbscript/index.html">VBScript</a></li>
<li><a href="../mode/velocity/index.html">Velocity</a></li>
Expand Down
37 changes: 37 additions & 0 deletions doc/oldrelease.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,43 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
</pre>
</div>

<p class="rel">19-09-2012: <a href="http://codemirror.net/codemirror-2.34.zip">Version 2.34</a>:</p>

<ul class="rel-note">
<li>New mode: <a href="../mode/commonlisp/index.html">Common Lisp</a>.</li>
<li>Fix right-click select-all on most browsers.</li>
<li>Change the way highlighting happens:<br>&nbsp; Saves memory and CPU cycles.<br>&nbsp; <code>compareStates</code> is no longer needed.<br>&nbsp; <code>onHighlightComplete</code> no longer works.</li>
<li>Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite.</li>
<li>Add a <a href="manual.html#version"><code>CodeMirror.version</code></a> property.</li>
<li>More robust handling of nested modes in <a href="../demo/formatting.html">formatting</a> and <a href="../demo/closetag.html">closetag</a> plug-ins.</li>
<li>Un/redo now preserves <a href="manual.html#markText">marked text</a> and bookmarks.</li>
<li><a href="https://github.com/marijnh/CodeMirror/compare/v2.33...v2.34">Full list</a> of patches.</li>
</ul>

<p class="rel">19-09-2012: <a href="http://codemirror.net/codemirror-3.0beta1.zip">Version 3.0, beta 1</a>:</p>

<ul class="rel-note">
<li>Bi-directional text support.</li>
<li>More powerful gutter model.</li>
<li>Support for arbitrary text/widget height.</li>
<li>In-line widgets.</li>
<li>Generalized event handling.</li>
</ul>

<p class="rel">23-08-2012: <a href="http://codemirror.net/codemirror-2.33.zip">Version 2.33</a>:</p>

<ul class="rel-note">
<li>New mode: <a href="../mode/sieve/index.html">Sieve</a>.</li>
<li>New <a href="manual.html#getViewport"><code>getViewPort</code></a> and <a href="manual.html#option_onViewportChange"><code>onViewportChange</code></a> API.</li>
<li><a href="manual.html#option_cursorBlinkRate">Configurable</a> cursor blink rate.</li>
<li>Make binding a key to <code>false</code> disabling handling (again).</li>
<li>Show non-printing characters as red dots.</li>
<li>More tweaks to the scrolling model.</li>
<li>Expanded testsuite. Basic linter added.</li>
<li>Remove most uses of <code>innerHTML</code>. Remove <code>CodeMirror.htmlEscape</code>.</li>
<li><a href="https://github.com/marijnh/CodeMirror/compare/v2.32...v2.33">Full list</a> of patches.</li>
</ul>

<p class="rel">23-07-2012: <a href="http://codemirror.net/codemirror-2.32.zip">Version 2.32</a>:</p>

<p class="rel-note">Emergency fix for a bug where an editor with
Expand Down
8 changes: 7 additions & 1 deletion doc/realworld.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,26 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<ul>
<li><a href="http://brackets.io">Adobe Brackets</a> (code editor)</li>
<li><a href="http://amber-lang.net/">Amber</a> (JavaScript-based Smalltalk system)</li>
<li><a href="http://apeye.org/">APEye</a> (tool for testing &amp; documenting APIs)</li>
<li><a href="http://bluegriffon.org/">BlueGriffon</a> (HTML editor)</li>
<li><a href="http://cargocollective.com/">Cargo Collective</a> (creative publishing platform)</li>
<li><a href="http://www.codebugapp.com/">Codebug</a> (PHP Xdebug front-end)</li>
<li><a href="http://code.google.com/p/codemirror2-gwt/">CodeMirror2-GWT</a> (Google Web Toolkit wrapper)</li>
<li><a href="http://www.crunchzilla.com/code-monster">Code Monster</a> & <a href="http://www.crunchzilla.com/code-maven">Code Maven</a> (learning environment)</li>
<li><a href="http://codepen.io">Codepen</a> (gallery of animations)</li>
<li><a href="http://sasstwo.codeschool.com/levels/1/challenges/1">Code School</a> (online tech learning environment)</li>
<li><a href="http://code-snippets.bungeshea.com/">Code Snippets</a> (WordPress snippet management plugin)</li>
<li><a href="http://codev.it/">Codev</a> (collaborative IDE)</li>
<li><a href="http://ot.substance.io/demo/">Collaborative CodeMirror demo</a> (CodeMirror + operational transforms)</li>
<li><a href="http://www.communitycodecamp.com/">Community Code Camp</a> (code snippet sharing)</li>
<li><a href="http://www.ckwnc.com/">CKWNC</a> (UML editor)</li>
<li><a href="http://cssdeck.com/">CSSDeck</a> (CSS showcase)</li>
<li><a href="http://ireneros.com/deck/deck.js-codemirror/introduction/#textarea-code">Deck.js integration</a> (slides with editors)</li>
<li><a href="http://www.dbninja.com">DbNinja</a> (MySQL access interface)</li>
<li><a href="http://elm-lang.org/Examples.elm">Elm language examples</a></li>
<li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript</a> (book)</li>
<li><a href="http://www.fastfig.com/">Fastfig</a> (online computation/math tool)</li>
<li><a href="https://metacpan.org/module/Farabi">Farabi</a> (Perl editor)</li>
<li><a href="https://metacpan.org/module/Farabi">Farabi</a> (modern Perl IDE)</li>
<li><a href="http://blog.pamelafox.org/2012/02/interactive-html5-slides-with-fathomjs.html">FathomJS integration</a> (slides with editors, again)</li>
<li><a href="http://tour.golang.org">Go language tour</a></li>
<li><a href="https://github.com/github/android">GitHub's Android app</a></li>
Expand All @@ -58,6 +62,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<li><a href="http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/">Light Table</a> (experimental IDE)</li>
<li><a href="http://www.mergely.com/">Mergely</a> (interactive diffing)</li>
<li><a href="http://www.iunbug.com/mihtool">MIHTool</a> (iOS web-app debugging tool)</li>
<li><a href="http://mongo-mapreduce-webbrowser.opensagres.cloudbees.net/">Mongo MapReduce WebBrowser</a></li>
<li><a href="https://www.my2ndgeneration.com/">My2ndGeneration</a> (social coding)</li>
<li><a href="https://notex.ch">NoTex</a> (rST authoring)</li>
<li><a href="http://clrhome.org/asm/">ORG</a> (z80 assembly IDE)</li>
Expand All @@ -78,6 +83,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<li><a href="http://enjalot.com/tributary/2636296/sinwaves.js">Tributary</a> (augmented editing)</li>
<li><a href="http://blog.englard.net/post/39608000629/codeintumblr">Tumblr code highlighting shim</a></li>
<li><a href="http://turbopy.com/">TurboPY</a> (web publishing framework)</li>
<li><a href="http://cruise.eecs.uottawa.ca/umpleonline/">UmpleOnline</a> (model-oriented programming tool)</li>
<li><a href="http://webglplayground.net/">WebGL playground</a></li>
<li><a href="http://www.wescheme.org/">WeScheme</a> (learning tool)</li>
<li><a href="http://wordpress.org/extend/plugins/codemirror-for-codeeditor/">WordPress plugin</a></li>
Expand Down
83 changes: 45 additions & 38 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ <h2 style="margin-top: 0">Usage demos:</h2>
<li><a href="demo/complete.html">Autocompletion</a> (<a href="demo/xmlcomplete.html">XML</a>)</li>
<li><a href="demo/search.html">Search/replace</a></li>
<li><a href="demo/folding.html">Code folding</a></li>
<li><a href="demo/bidi.html">Bi-directional text</a></li>
<li><a href="demo/widget.html">Line widgets</a> (via JSHint)</li>
<li><a href="demo/buffers.html">Split view</a></li>
<li><a href="demo/mustache.html">Mode overlays</a></li>
<li><a href="demo/multiplex.html">Mode multiplexer</a></li>
<li><a href="demo/preview.html">HTML editor with preview</a></li>
Expand All @@ -107,7 +109,6 @@ <h2 style="margin-top: 0">Usage demos:</h2>
<li><a href="demo/fullscreen.html">Full-screen editing</a></li>
<li><a href="demo/changemode.html">Mode auto-changing</a></li>
<li><a href="demo/visibletabs.html">Visible tabs</a></li>
<li><a href="demo/formatting.html">Autoformatting of code</a></li>
<li><a href="demo/emacs.html">Emacs keybindings</a></li>
<li><a href="demo/vim.html">Vim keybindings</a></li>
<li><a href="demo/closetag.html">Automatic xml tag closing</a></li>
Expand Down Expand Up @@ -283,6 +284,49 @@ <h2>Reading material</h2>

<h2 id=releases>Releases</h2>

<p class="rel">21-02-2013: <a href="http://codemirror.net/codemirror-3.1.zip">Version 3.1</a>:</p>

<ul class="rel-note">
<li><strong>Incompatible:</strong> key handlers may
now <em>return</em>, rather
than <em>throw</em> <code>CodeMirror.Pass</code> to signal they
didn't handle the key.</li>
<li>Make documents a <a href="doc/manual.html#api_doc">first-class
construct</a>, support split views and subviews.</li>
<li>Add a <a href="doc/manual.html#addon_show-hint">new module</a>
for showing completion hints.
Deprecate <code>simple-hint.js</code>.</li>
<li>Extend <a href="mode/htmlmixed/index.html">htmlmixed mode</a>
to allow custom handling of script types.</li>
<li>Support an <code>insertLeft</code> option
to <a href="doc/manual.html#setBookmark"><code>setBookmark</code></a>.</li>
<li>Add an <a href="doc/manual.html#eachLine"><code>eachLine</code></a>
method to iterate over a document.</li>
<li>New addon modules: <a href="demo/markselection.html">selection
marking</a>, <a href="demo/lint.html">linting</a>,
and <a href="demo/closebrackets.html">automatic bracket
closing</a>.</li>
<li>Add <a href="doc/manual.html#event_beforeChange"><code>"beforeChange"</code></a>
and <a href="doc/manual.html#event_beforeSelectionChange"><code>"beforeSelectionChange"</code></a>
events.</li>
<li>Add <a href="doc/manual.html#event_hide"><code>"hide"</code></a>
and <a href="doc/manual.html#event_unhide"><code>"unhide"</code></a>
events to marked ranges.</li>
<li>Fix <a href="doc/manual.html#coordsChar"><code>coordsChar</code></a>'s
interpretation of its argument to match the documentation.</li>
<li>New modes: <a href="mode/turtle/index.html">Turtle</a>
and <a href="mode/q/index.html">Q</a>.</li>
<li>Further improvements to the <a href="demo/vim.html">vim mode</a>.</li>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/v3.01...v3.1">list of patches</a>.</li>
</ul>


<p class="rel">25-01-2013: <a href="http://codemirror.net/codemirror-3.02.zip">Version 3.02</a>:</p>

<p class="rel-note">Single-bugfix release. Fixes a problem that
prevents CodeMirror instances from being garbage-collected after
they become unused.</p>

<p class="rel">21-01-2013: <a href="http://codemirror.net/codemirror-3.01.zip">Version 3.01</a>:</p>

<ul class="rel-note">
Expand Down Expand Up @@ -403,43 +447,6 @@ <h2 id=releases>Releases</h2>
<li>Full <a href="https://github.com/marijnh/CodeMirror/compare/v3.0beta1...v3.0beta2">list of patches</a>.</li>
</ul>

<p class="rel">19-09-2012: <a href="http://codemirror.net/codemirror-2.34.zip">Version 2.34</a>:</p>

<ul class="rel-note">
<li>New mode: <a href="mode/commonlisp/index.html">Common Lisp</a>.</li>
<li>Fix right-click select-all on most browsers.</li>
<li>Change the way highlighting happens:<br>&nbsp; Saves memory and CPU cycles.<br>&nbsp; <code>compareStates</code> is no longer needed.<br>&nbsp; <code>onHighlightComplete</code> no longer works.</li>
<li>Integrate mode (Markdown, XQuery, CSS, sTex) tests in central testsuite.</li>
<li>Add a <a href="doc/manual.html#version"><code>CodeMirror.version</code></a> property.</li>
<li>More robust handling of nested modes in <a href="demo/formatting.html">formatting</a> and <a href="demo/closetag.html">closetag</a> plug-ins.</li>
<li>Un/redo now preserves <a href="doc/manual.html#markText">marked text</a> and bookmarks.</li>
<li><a href="https://github.com/marijnh/CodeMirror/compare/v2.33...v2.34">Full list</a> of patches.</li>
</ul>

<p class="rel">19-09-2012: <a href="http://codemirror.net/codemirror-3.0beta1.zip">Version 3.0, beta 1</a>:</p>

<ul class="rel-note">
<li>Bi-directional text support.</li>
<li>More powerful gutter model.</li>
<li>Support for arbitrary text/widget height.</li>
<li>In-line widgets.</li>
<li>Generalized event handling.</li>
</ul>

<p class="rel">23-08-2012: <a href="http://codemirror.net/codemirror-2.33.zip">Version 2.33</a>:</p>

<ul class="rel-note">
<li>New mode: <a href="mode/sieve/index.html">Sieve</a>.</li>
<li>New <a href="doc/manual.html#getViewport"><code>getViewPort</code></a> and <a href="doc/manual.html#option_onViewportChange"><code>onViewportChange</code></a> API.</li>
<li><a href="doc/manual.html#option_cursorBlinkRate">Configurable</a> cursor blink rate.</li>
<li>Make binding a key to <code>false</code> disabling handling (again).</li>
<li>Show non-printing characters as red dots.</li>
<li>More tweaks to the scrolling model.</li>
<li>Expanded testsuite. Basic linter added.</li>
<li>Remove most uses of <code>innerHTML</code>. Remove <code>CodeMirror.htmlEscape</code>.</li>
<li><a href="https://github.com/marijnh/CodeMirror/compare/v2.32...v2.33">Full list</a> of patches.</li>
</ul>

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

</div></div>
Expand Down
174 changes: 126 additions & 48 deletions keymap/vim.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*
* Motion:
* h, j, k, l
* gj, gk
* e, E, w, W, b, B, ge, gE
* f<character>, F<character>, t<character>, T<character>
* $, ^, 0
Expand All @@ -21,6 +22,7 @@
*
* Action:
* a, i, s, A, I, S, o, O
* zz, z., z<CR>, zt, zb, z-
* J
* u, Ctrl-r
* m<character>
Expand Down Expand Up @@ -89,6 +91,12 @@
{ keys: ['k'], type: 'motion',
motion: 'moveByLines',
motionArgs: { forward: false, linewise: true }},
{ keys: ['g','j'], type: 'motion',
motion: 'moveByDisplayLines',
motionArgs: { forward: true, linewise: true }},
{ keys: ['g','k'], type: 'motion',
motion: 'moveByDisplayLines',
motionArgs: { forward: false, linewise: true }},
{ keys: ['w'], type: 'motion',
motion: 'moveByWords',
motionArgs: { forward: true, wordEnd: false }},
Expand Down Expand Up @@ -211,7 +219,21 @@
{ keys: ['Ctrl-r'], type: 'action', action: 'redo' },
{ keys: ['m', 'character'], type: 'action', action: 'setMark' },
{ keys: ['\"', 'character'], type: 'action', action: 'setRegister' },
{ keys: [',', '/'], type: 'action', action: 'clearSearchHighlight' },
{ keys: ['z', 'z'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'center' }},
{ keys: ['z', '.'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'center' },
motion: 'moveToFirstNonWhiteSpaceCharacter' },
{ keys: ['z', 't'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'top' }},
{ keys: ['z', 'Enter'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'top' },
motion: 'moveToFirstNonWhiteSpaceCharacter' },
{ keys: ['z', '-'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'bottom' }},
{ keys: ['z', 'b'], type: 'action', action: 'scrollToCursor',
actionArgs: { position: 'bottom' },
motion: 'moveToFirstNonWhiteSpaceCharacter' },
// Text object motions
{ keys: ['a', 'character'], type: 'motion',
motion: 'textObjectManipulation' },
Expand Down Expand Up @@ -249,7 +271,7 @@
var SPECIAL_SYMBOLS = '~`!@#$%^&*()_-+=[{}]\\|/?.,<>:;\"\'';
var specialSymbols = SPECIAL_SYMBOLS.split('');
var specialKeys = ['Left', 'Right', 'Up', 'Down', 'Space', 'Backspace',
'Esc', 'Home', 'End', 'PageUp', 'PageDown'];
'Esc', 'Home', 'End', 'PageUp', 'PageDown', 'Enter'];
var validMarks = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
numbers).concat(['<', '>']);
var validRegisters = upperCaseAlphabet.concat(lowerCaseAlphabet).concat(
Expand All @@ -259,7 +281,7 @@
return alphabetRegex.test(k);
}
function isLine(cm, line) {
return line >= 0 && line < cm.lineCount();
return line >= cm.firstLine() && line <= cm.lastLine();
}
function isLowerCase(k) {
return (/^[a-z]$/).test(k);
Expand Down Expand Up @@ -319,6 +341,8 @@
// cursor should go back to its horizontal position on the longer
// line if it can. This is to keep track of the horizontal position.
lastHPos: -1,
// Doing the same with screen-position for gj/gk
lastHSPos: -1,
// The last motion command run. Cleared if a non-motion command gets
// executed in between.
lastMotion: null,
Expand Down Expand Up @@ -348,6 +372,12 @@
// Add user defined key bindings.
exCommandDispatcher.map(lhs, rhs);
},
defineEx: function(name, prefix, func){
if (name.indexOf(prefix) === 0) {
exCommands[name]=func;
exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
}else throw new Error("(Vim.defineEx) \""+prefix+"\" is not a prefix of \""+name+"\", command not registered");
},
// Initializes vim state variable on the CodeMirror object. Should only be
// called lazily by handleKey or for testing.
maybeInitState: function(cm) {
Expand Down Expand Up @@ -810,9 +840,6 @@
selectionStart.ch = lineLength(cm, selectionStart.line);
}
}
// Need to set the cursor to clear the selection. Otherwise,
// CodeMirror can't figure out that we changed directions...
cm.setCursor(selectionStart);
cm.setSelection(selectionStart, selectionEnd);
updateMark(cm, vim, '<',
cursorIsBefore(selectionStart, selectionEnd) ? selectionStart
Expand Down Expand Up @@ -903,29 +930,48 @@
return { line: cur.line, ch: ch };
},
moveByLines: function(cm, motionArgs, vim) {
var endCh = cm.getCursor().ch;
var cur = cm.getCursor();
var endCh = cur.ch;
// Depending what our last motion was, we may want to do different
// things. If our last motion was moving vertically, we want to
// preserve the HPos from our last horizontal move. If our last motion
// was going to the end of a line, moving vertically we should go to
// the end of the line, etc.
switch (vim.lastMotion) {
case this.moveByLines:
case this.moveByDisplayLines:
case this.moveToColumn:
case this.moveToEol:
endCh = vim.lastHPos;
break;
default:
vim.lastHPos = endCh;
}
var cur = cm.getCursor();
var repeat = motionArgs.repeat;
var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
if (line < 0 || line > cm.lineCount() - 1) {
if (line < cm.firstLine() || line > cm.lastLine() ) {
return null;
}
vim.lastHSPos = cm.charCoords({line:line, ch:endCh},"div").left;
return { line: line, ch: endCh };
},
moveByDisplayLines: function(cm, motionArgs, vim) {
var cur = cm.getCursor();
switch (vim.lastMotion) {
case this.moveByDisplayLines:
case this.moveByLines:
case this.moveToColumn:
case this.moveToEol:
break;
default:
vim.lastHSPos = cm.charCoords(cur,"div").left;
}
var repeat = motionArgs.repeat;
var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),"line",vim.lastHSPos);
if(res.hitSide)return null;
vim.lastHPos = res.ch;
return res;
},
moveByPage: function(cm, motionArgs) {
// CodeMirror only exposes functions that move the cursor page down, so
// doing this bad hack to move the cursor and move it back. evalInput
Expand All @@ -942,12 +988,12 @@
var repeat = motionArgs.repeat;
var inc = motionArgs.forward ? 1 : -1;
for (var i = 0; i < repeat; i++) {
if ((!motionArgs.forward && line === 0) ||
(motionArgs.forward && line == cm.lineCount() - 1)) {
if ((!motionArgs.forward && line === cm.firstLine() ) ||
(motionArgs.forward && line == cm.lastLine())) {
break;
}
line += inc;
while (line !== 0 && line != cm.lineCount - 1 && cm.getLine(line)) {
while (line !== cm.firstLine() && line != cm.lastLine() && cm.getLine(line)) {
line += inc;
}
}
Expand All @@ -974,12 +1020,17 @@
var repeat = motionArgs.repeat;
// repeat is equivalent to which column we want to move to!
vim.lastHPos = repeat - 1;
vim.lastHSPos = cm.charCoords(cm.getCursor(),"div").left;
return moveToColumn(cm, repeat);
},
moveToEol: function(cm, motionArgs, vim) {
var cur = cm.getCursor();
vim.lastHPos = Infinity;
return { line: cur.line + motionArgs.repeat - 1, ch: Infinity };
var retval={ line: cur.line + motionArgs.repeat - 1, ch: Infinity }
var end=cm.clipPos(retval);
end.ch--;
vim.lastHSPos = cm.charCoords(end,"div").left;
return retval;
},
moveToFirstNonWhiteSpaceCharacter: function(cm) {
// Go to the start of the line where the text begins, or the end for
Expand All @@ -1003,9 +1054,9 @@
return { line: cursor.line, ch: 0 };
},
moveToLineOrEdgeOfDocument: function(cm, motionArgs) {
var lineNum = motionArgs.forward ? cm.lineCount() - 1 : 0;
var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
if (motionArgs.repeatIsExplicit) {
lineNum = motionArgs.repeat - 1;
lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
}
return { line: lineNum,
ch: findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)) };
Expand Down Expand Up @@ -1104,7 +1155,25 @@
};

var actions = {
clearSearchHighlight: clearSearchHighlight,
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;
switch (actionArgs.position) {
case 'center': y = y - (height / 2) + 10;
break;
case 'bottom': y = y - height;
break;
case 'top': break;
}
cm.scrollTo(null, y);
// The calculations are slightly off, use scrollIntoView to nudge the
// view into the right place.
cm.scrollIntoView();
},
enterInsertMode: function(cm, actionArgs) {
var insertAt = (actionArgs) ? actionArgs.insertAt : null;
if (insertAt == 'eol') {
Expand Down Expand Up @@ -1202,10 +1271,10 @@
},
newLineAndEnterInsertMode: function(cm, actionArgs) {
var insertAt = cm.getCursor();
if (insertAt.line === 0 && !actionArgs.after) {
if (insertAt.line === cm.firstLine() && !actionArgs.after) {
// Special case for inserting newline before start of document.
cm.replaceRange('\n', { line: 0, ch: 0 });
cm.setCursor(0, 0);
cm.replaceRange('\n', { line: cm.firstLine(), ch: 0 });
cm.setCursor(cm.firstLine(), 0);
} else {
insertAt.line = (actionArgs.after) ? insertAt.line :
insertAt.line - 1;
Expand Down Expand Up @@ -1325,14 +1394,11 @@
*/

/**
* Clips cursor to ensure that:
* 0 <= cur.ch < lineLength
* AND
* 0 <= cur.line < lineCount
* Clips cursor to ensure that line is within the buffer's range
* If includeLineBreak is true, then allow cur.ch == lineLength.
*/
function clipCursorToContent(cm, cur, includeLineBreak) {
var line = Math.min(Math.max(0, cur.line), cm.lineCount() - 1);
var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );
var maxCh = lineLength(cm, line) - 1;
maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
var ch = Math.min(Math.max(0, cur.ch), maxCh);
Expand Down Expand Up @@ -1729,8 +1795,13 @@
nextCh = lineText.charAt(index);
if (!nextCh) {
line += increment;
index = 0;
lineText = cm.getLine(line) || '';
if (increment > 0) {
index = 0;
} else {
var lineLen = lineText.length;
index = (lineLen > 0) ? (lineLen-1) : 0;
}
nextCh = lineText.charAt(index);
}
if (nextCh === symb) {
Expand Down Expand Up @@ -2054,7 +2125,7 @@
// SearchCursor may have returned null because it hit EOF, wrap
// around and try again.
cursor = cm.getSearchCursor(query,
(prev) ? { line: cm.lineCount() - 1} : {line: 0, ch: 0} );
(prev) ? { line: cm.lastLine() } : {line: cm.firstLine(), ch: 0} );
if (!cursor.find(prev)) {
return;
}
Expand Down Expand Up @@ -2119,7 +2190,8 @@
{ name: 'write', shortName: 'w', type: 'builtIn' },
{ name: 'undo', shortName: 'u', type: 'builtIn' },
{ name: 'redo', shortName: 'red', type: 'builtIn' },
{ name: 'substitute', shortName: 's', type: 'builtIn'}
{ name: 'substitute', shortName: 's', type: 'builtIn'},
{ name: 'nohlsearch', shortName: 'noh', type: 'builtIn'}
];
Vim.ExCommandDispatcher = function() {
this.buildCommandMap_();
Expand Down Expand Up @@ -2169,8 +2241,8 @@
inputStream.eatWhile(':');
// Parse range.
if (inputStream.eat('%')) {
result.line = 0;
result.lineEnd = cm.lineCount() - 1;
result.line = cm.firstLine();
result.lineEnd = cm.lastLine();
} else {
result.line = this.parseLineSpec_(cm, inputStream);
if (result.line !== undefined && inputStream.eat(',')) {
Expand All @@ -2197,7 +2269,7 @@
case '.':
return cm.getCursor().line;
case '$':
return cm.lineCount() - 1;
return cm.lastLine();
case '\'':
var mark = getVimState(cm).marks[inputStream.next()];
if (mark && mark.find()) {
Expand Down Expand Up @@ -2300,25 +2372,28 @@
var vimKeyNotationStart = ++idx;
while (str.charAt(idx++) != '>') {}
var vimKeyNotation = str.substring(vimKeyNotationStart, idx - 1);
var mod='';
var match = (/^C-(.+)$/).exec(vimKeyNotation);
if (match) {
var key;
switch (match[1]) {
case 'BS':
key = 'Backspace';
break;
case 'CR':
key = 'Enter';
break;
case 'Del':
key = 'Delete';
break;
default:
key = match[1];
break;
}
keys.push('Ctrl-' + key);
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);
}
return keys;
}
Expand All @@ -2338,7 +2413,7 @@
commandDispatcher.processMotion(cm, getVimState(cm), {
motion: 'moveToLineOrEdgeOfDocument',
motionArgs: { forward: false, explicitRepeat: true,
linewise: true, repeat: params.line }});
linewise: true, repeat: params.line+1 }});
},
substitute: function(cm, params) {
var argString = params.argString;
Expand Down Expand Up @@ -2373,7 +2448,7 @@
}
var state = getSearchState(cm);
var query = state.getQuery();
var lineStart = params.line || 0;
var lineStart = params.line || cm.firstLine();
var lineEnd = params.lineEnd || lineStart;
if (count) {
lineStart = lineEnd;
Expand Down Expand Up @@ -2410,6 +2485,9 @@
// Saves to text area if no save command is defined.
cm.save();
}
},
nohlsearch: function(cm) {
clearSearchHighlight(cm);
}
};

Expand Down
5 changes: 5 additions & 0 deletions lib/codemirror.css
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
.CodeMirror-gutters {
position: absolute; left: 0; top: 0;
height: 100%;
padding-bottom: 30px;
z-index: 3;
}
.CodeMirror-gutter {
Expand Down Expand Up @@ -199,6 +200,10 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
overflow: auto;
}

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

.CodeMirror-wrap .CodeMirror-scroll {
overflow-x: hidden;
}
Expand Down
Loading