diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js
index 71e2287447..499964fdb2 100644
--- a/addon/dialog/dialog.js
+++ b/addon/dialog/dialog.js
@@ -10,11 +10,22 @@
} else {
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
}
- dialog.innerHTML = template;
+ if (typeof template == "string") {
+ dialog.innerHTML = template;
+ } else { // Assuming it's a detached DOM element.
+ dialog.appendChild(template);
+ }
return dialog;
}
+ function closeNotification(cm, newVal) {
+ if (cm.state.currentNotificationClose)
+ cm.state.currentNotificationClose();
+ cm.state.currentNotificationClose = newVal;
+ }
+
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+ closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var closed = false, me = this;
function close() {
@@ -51,6 +62,7 @@
});
CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
+ closeNotification(this, null);
var dialog = dialogDiv(this, template, options && options.bottom);
var buttons = dialog.getElementsByTagName("button");
var closed = false, me = this, blurring = 1;
@@ -77,4 +89,33 @@
CodeMirror.on(b, "focus", function() { ++blurring; });
}
});
+
+ /*
+ * openNotification
+ * Opens a notification, that can be closed with an optional timer
+ * (default 5000ms timer) and always closes on click.
+ *
+ * If a notification is opened while another is opened, it will close the
+ * currently opened one and open the new one immediately.
+ */
+ CodeMirror.defineExtension("openNotification", function(template, options) {
+ closeNotification(this, close);
+ var dialog = dialogDiv(this, template, options && options.bottom);
+ var duration = options && (options.duration === undefined ? 5000 : options.duration);
+ var closed = false, doneTimer;
+
+ function close() {
+ if (closed) return;
+ closed = true;
+ clearTimeout(doneTimer);
+ dialog.parentNode.removeChild(dialog);
+ }
+
+ CodeMirror.on(dialog, 'click', function(e) {
+ CodeMirror.e_preventDefault(e);
+ close();
+ });
+ if (duration)
+ doneTimer = setTimeout(close, options.duration);
+ });
})();
diff --git a/addon/display/fullscreen.js b/addon/display/fullscreen.js
index 3c31e97a33..a442f6a433 100644
--- a/addon/display/fullscreen.js
+++ b/addon/display/fullscreen.js
@@ -12,7 +12,8 @@
var wrap = cm.getWrapperElement();
cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
width: wrap.style.width, height: wrap.style.height};
- wrap.style.width = wrap.style.height = "";
+ wrap.style.width = "";
+ wrap.style.height = "auto";
wrap.className += " CodeMirror-fullscreen";
document.documentElement.style.overflow = "hidden";
cm.refresh();
diff --git a/addon/display/placeholder.js b/addon/display/placeholder.js
index 18f9dff3ab..748afe7275 100644
--- a/addon/display/placeholder.js
+++ b/addon/display/placeholder.js
@@ -2,12 +2,10 @@
CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
var prev = old && old != CodeMirror.Init;
if (val && !prev) {
- cm.on("focus", onFocus);
cm.on("blur", onBlur);
cm.on("change", onChange);
onChange(cm);
} else if (!val && prev) {
- cm.off("focus", onFocus);
cm.off("blur", onBlur);
cm.off("change", onChange);
clearPlaceholder(cm);
@@ -33,9 +31,6 @@
cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
}
- function onFocus(cm) {
- clearPlaceholder(cm);
- }
function onBlur(cm) {
if (isEmpty(cm)) setPlaceholder(cm);
}
@@ -43,7 +38,6 @@
var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
- if (cm.hasFocus()) return;
if (empty) setPlaceholder(cm);
else clearPlaceholder(cm);
}
diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js
index d6a8fafd3c..1da89ba6dc 100644
--- a/addon/edit/closetag.js
+++ b/addon/edit/closetag.js
@@ -24,16 +24,15 @@
(function() {
CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
- if (val && (old == CodeMirror.Init || !old)) {
- var map = {name: "autoCloseTags"};
- if (typeof val != "object" || val.whenClosing)
- map["'/'"] = function(cm) { return autoCloseSlash(cm); };
- if (typeof val != "object" || val.whenOpening)
- map["'>'"] = function(cm) { return autoCloseGT(cm); };
- cm.addKeyMap(map);
- } else if (!val && (old != CodeMirror.Init && old)) {
+ if (old != CodeMirror.Init && old)
cm.removeKeyMap("autoCloseTags");
- }
+ if (!val) return;
+ var map = {name: "autoCloseTags"};
+ if (typeof val != "object" || val.whenClosing)
+ map["'/'"] = function(cm) { return autoCloseSlash(cm); };
+ if (typeof val != "object" || val.whenOpening)
+ map["'>'"] = function(cm) { return autoCloseGT(cm); };
+ cm.addKeyMap(map);
});
var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
@@ -54,7 +53,8 @@
if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
var lowerTagName = tagName.toLowerCase();
// Don't process the '>' at the end of an end-tag or self-closing tag
- if (tok.type == "tag" && state.type == "closeTag" ||
+ if (tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
+ tok.type == "tag" && state.type == "closeTag" ||
tok.string.indexOf("/") == (tok.string.length - 1) || // match something like
dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1)
return CodeMirror.Pass;
@@ -72,7 +72,9 @@
function autoCloseSlash(cm) {
var pos = cm.getCursor(), tok = cm.getTokenAt(pos);
var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
- if (tok.string.charAt(0) != "<" || tok.start != pos.ch - 1 || inner.mode.name != "xml") return CodeMirror.Pass;
+ if (tok.type == "string" || tok.string.charAt(0) != "<" ||
+ tok.start != pos.ch - 1 || inner.mode.name != "xml")
+ return CodeMirror.Pass;
var tagName = state.context && state.context.tagName;
if (tagName) cm.replaceSelection("/" + tagName + ">", "end");
diff --git a/addon/edit/matchbrackets.js b/addon/edit/matchbrackets.js
index 131fe831fd..9d9b3882f7 100644
--- a/addon/edit/matchbrackets.js
+++ b/addon/edit/matchbrackets.js
@@ -8,6 +8,7 @@
function findMatchingBracket(cm, where, strict) {
var state = cm.state.matchBrackets;
var maxScanLen = (state && state.maxScanLineLength) || 10000;
+ var maxScanLines = (state && state.maxScanLines) || 100;
var cur = where || cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
@@ -32,7 +33,7 @@
}
}
}
- for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+ for (var i = cur.line, found, e = forward ? Math.min(i + maxScanLines, cm.lineCount()) : Math.max(-1, i - maxScanLines); i != e; i+=d) {
if (i == cur.line) found = scan(line, i, pos);
else found = scan(cm.getLineHandle(i), i);
if (found) break;
diff --git a/addon/fold/foldgutter.js b/addon/fold/foldgutter.js
index e3c52bc229..57336fbff8 100644
--- a/addon/fold/foldgutter.js
+++ b/addon/fold/foldgutter.js
@@ -88,14 +88,14 @@
}
function onChange(cm) {
- var state = cm.state.foldGutter;
+ var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
state.from = state.to = 0;
clearTimeout(state.changeUpdate);
- state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, 600);
+ state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
}
function onViewportChange(cm) {
- var state = cm.state.foldGutter;
+ var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
clearTimeout(state.changeUpdate);
state.changeUpdate = setTimeout(function() {
var vp = cm.getViewport();
@@ -113,7 +113,7 @@
}
});
}
- }, 400);
+ }, opts.updateViewportTimeSpan || 400);
}
function onFold(cm, from) {
diff --git a/addon/fold/indent-fold.js b/addon/fold/indent-fold.js
index b54da34777..1bd600be42 100644
--- a/addon/fold/indent-fold.js
+++ b/addon/fold/indent-fold.js
@@ -1,26 +1,30 @@
CodeMirror.registerHelper("fold", "indent", function(cm, start) {
- var lastLine = cm.lastLine(),
- tabSize = cm.getOption("tabSize"),
- firstLine = cm.getLine(start.line),
- myIndent = CodeMirror.countColumn(firstLine, null, tabSize);
-
- function foldEnded(curColumn, prevColumn) {
- return curColumn < myIndent ||
- (curColumn == myIndent && prevColumn >= myIndent) ||
- (curColumn > myIndent && i == lastLine);
- }
-
- for (var i = start.line + 1; i <= lastLine; i++) {
- var curColumn = CodeMirror.countColumn(cm.getLine(i), null, tabSize);
- var prevColumn = CodeMirror.countColumn(cm.getLine(i-1), null, tabSize);
-
- if (foldEnded(curColumn, prevColumn)) {
- var lastFoldLineNumber = curColumn > myIndent && i == lastLine ? i : i-1;
- var lastFoldLine = cm.getLine(lastFoldLineNumber);
- return {from: CodeMirror.Pos(start.line, firstLine.length),
- to: CodeMirror.Pos(lastFoldLineNumber, lastFoldLine.length)};
+ var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line);
+ if (!/\S/.test(firstLine)) return;
+ var getIndent = function(lineNum) {
+ return CodeMirror.countColumn(lineNum, null, tabSize);
+ };
+ var myIndent = getIndent(firstLine);
+ var lastLineInFold = null;
+ // Go through lines until we find a line that definitely doesn't belong in
+ // the block we're folding, or to the end.
+ for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
+ var curLine = cm.getLine(i);
+ var curIndent = getIndent(curLine);
+ if (curIndent > myIndent) {
+ // Lines with a greater indent are considered part of the block.
+ lastLineInFold = i;
+ } else if (!/\S/.test(curLine)) {
+ // Empty lines might be breaks within the block we're trying to fold.
+ } else {
+ // A non-empty line at an indent equal to or less than ours marks the
+ // start of another block.
+ break;
}
}
+ if (lastLineInFold) return {
+ from: CodeMirror.Pos(start.line, firstLine.length),
+ to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
+ };
});
-
CodeMirror.indentRangeFinder = CodeMirror.fold.indent; // deprecated
diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js
index 513fb782b0..c66b0a7a5b 100644
--- a/addon/hint/javascript-hint.js
+++ b/addon/hint/javascript-hint.js
@@ -21,6 +21,7 @@
function scriptHint(editor, keywords, getToken, options) {
// Find the token at the cursor
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+ if (/\b(?:string|comment)\b/.test(token.type)) return;
token.state = CodeMirror.innerMode(editor.getMode(), token.state).state;
// If it's not a 'word-style' token, ignore the token.
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index dbf415527a..46103b719b 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -1,6 +1,9 @@
(function() {
"use strict";
+ var HINT_ELEMENT_CLASS = "CodeMirror-hint";
+ var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
+
CodeMirror.showHint = function(cm, getHints, options) {
// We want a single cursor position.
if (cm.somethingSelected()) return;
@@ -140,6 +143,13 @@
return ourMap;
}
+ function getHintElement(stopAt, el) {
+ while (el && el != stopAt) {
+ if (el.nodeName.toUpperCase() === "LI") return el;
+ el = el.parentNode;
+ }
+ }
+
function Widget(completion, data) {
this.completion = completion;
this.data = data;
@@ -147,12 +157,12 @@
var hints = this.hints = document.createElement("ul");
hints.className = "CodeMirror-hints";
- this.selectedHint = 0;
+ this.selectedHint = options.getDefaultSelection ? options.getDefaultSelection(cm,options,data) : 0;
var completions = data.list;
for (var i = 0; i < completions.length; ++i) {
var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
- var className = "CodeMirror-hint" + (i ? "" : " CodeMirror-hint-active");
+ var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
if (cur.className != null) className = cur.className + " " + className;
elt.className = className;
if (cur.render) cur.render(elt, data, cur);
@@ -216,13 +226,15 @@
});
CodeMirror.on(hints, "dblclick", function(e) {
- var t = e.target || e.srcElement;
- if (t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
+ var t = getHintElement(hints, e.target || e.srcElement);
+ if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
});
+
CodeMirror.on(hints, "click", function(e) {
- var t = e.target || e.srcElement;
- if (t.hintId != null) widget.changeActive(t.hintId);
+ var t = getHintElement(hints, e.target || e.srcElement);
+ if (t && t.hintId != null) widget.changeActive(t.hintId);
});
+
CodeMirror.on(hints, "mousedown", function() {
setTimeout(function(){cm.focus();}, 20);
});
@@ -257,9 +269,9 @@
i = avoidWrap ? 0 : this.data.list.length - 1;
if (this.selectedHint == i) return;
var node = this.hints.childNodes[this.selectedHint];
- node.className = node.className.replace(" CodeMirror-hint-active", "");
+ node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
node = this.hints.childNodes[this.selectedHint = i];
- node.className += " CodeMirror-hint-active";
+ node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
if (node.offsetTop < this.hints.scrollTop)
this.hints.scrollTop = node.offsetTop - 3;
else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
diff --git a/addon/tern/tern.js b/addon/tern/tern.js
index fe93fa7335..1f18a657fb 100644
--- a/addon/tern/tern.js
+++ b/addon/tern/tern.js
@@ -96,7 +96,7 @@
getHint: function(cm, c) { return hint(this, cm, c); },
- showType: function(cm) { showType(this, cm); },
+ showType: function(cm, pos) { showType(this, cm, pos); },
updateArgHints: function(cm) { updateArgHints(this, cm); },
@@ -106,10 +106,10 @@
rename: function(cm) { rename(this, cm); },
- request: function (cm, query, c) {
+ request: function (cm, query, c, pos) {
var self = this;
var doc = findDoc(this, cm.getDoc());
- var request = buildRequest(this, doc, query);
+ var request = buildRequest(this, doc, query, pos);
this.server.request(request, function (error, data) {
if (!error && self.options.responseFilter)
@@ -221,7 +221,7 @@
// Type queries
- function showType(ts, cm) {
+ function showType(ts, cm, pos) {
ts.request(cm, "type", function(error, data) {
if (error) return showError(ts, cm, error);
if (ts.options.typeTip) {
@@ -236,7 +236,7 @@
}
}
tempTooltip(cm, tip);
- });
+ }, pos);
}
// Maintaining argument hints
@@ -450,13 +450,13 @@
// Generic request-building helper
- function buildRequest(ts, doc, query) {
+ function buildRequest(ts, doc, query, pos) {
var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
if (!allowFragments) delete query.fullDocs;
if (typeof query == "string") query = {type: query};
query.lineCharPositions = true;
if (query.end == null) {
- query.end = doc.doc.getCursor("end");
+ query.end = pos || doc.doc.getCursor("end");
if (doc.doc.somethingSelected())
query.start = doc.doc.getCursor("start");
}
diff --git a/demo/tern.html b/demo/tern.html
index adc79e5c3d..6843c7f5ec 100644
--- a/demo/tern.html
+++ b/demo/tern.html
@@ -16,6 +16,7 @@
+
diff --git a/demo/variableheight.html b/demo/variableheight.html
index 1ef8fc445d..ab241c6d28 100644
--- a/demo/variableheight.html
+++ b/demo/variableheight.html
@@ -10,7 +10,13 @@
diff --git a/doc/compress.html b/doc/compress.html
index e01001d2bb..3f3bfb0465 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -33,6 +33,7 @@
Version: