diff --git a/AUTHORS b/AUTHORS
index 0c2f67c31a..acc10fe16d 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -24,12 +24,14 @@ Alexandre Bique
alexey-k
Alex Piggott
Amsul
+amuntean
Amy
Ananya Sen
anaran
AndersMad
Anders Nawroth
Anderson Mesquita
+Andreas Reischuck
Andre von Houck
Andrey Lushnikov
Andy Joslin
@@ -128,6 +130,7 @@ Gabriel Horner
Gabriel Nahmias
galambalazs
Gautam Mehta
+gekkoe
Gergely Hegykozi
Glenn Jorde
Glenn Ruehle
@@ -138,6 +141,7 @@ greengiant
Guillaume Massé
Guillaume Massé
Gustavo Rodrigues
+Hakan Tunc
Hans Engel
Hardest
Hasan Karahan
@@ -268,6 +272,7 @@ nextrevision
nguillaumin
Ng Zhi An
Nicholas Bollweg
+Nick Small
Niels van Groningen
Nikita Beloglazov
Nikita Vasilyev
@@ -334,6 +339,7 @@ Takuji Shimokawa
Tarmil
tfjgeorge
Thaddee Tyl
+TheHowl
think
Thomas Dvornik
Thomas Schmid
diff --git a/addon/comment/comment.js b/addon/comment/comment.js
index cb78340231..d7f569cc00 100644
--- a/addon/comment/comment.js
+++ b/addon/comment/comment.js
@@ -157,12 +157,12 @@
// Positions of the last startString before the start of the selection, and the first endString after it.
var lastStart = startLine.lastIndexOf(startString, from.ch);
var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
- if (lastStart != -1 && firstEnd != -1) return false;
+ if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
// Positions of the first endString after the end of the selection, and the last startString before it.
firstEnd = endLine.indexOf(endString, to.ch);
var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
- if (firstEnd != -1 && lastStart != -1) return false;
+ if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
self.operation(function() {
self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js
index 622d689ed1..946040cb91 100644
--- a/addon/dialog/dialog.js
+++ b/addon/dialog/dialog.js
@@ -15,11 +15,11 @@
var wrap = cm.getWrapperElement();
var dialog;
dialog = wrap.appendChild(document.createElement("div"));
- if (bottom) {
+ if (bottom)
dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
- } else {
+ else
dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
- }
+
if (typeof template == "string") {
dialog.innerHTML = template;
} else { // Assuming it's a detached DOM element.
@@ -35,8 +35,11 @@
}
CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+ if (!options) options = {};
+
closeNotification(this, null);
- var dialog = dialogDiv(this, template, options && options.bottom);
+
+ var dialog = dialogDiv(this, template, options.bottom);
var closed = false, me = this;
function close(newVal) {
if (typeof newVal == 'string') {
@@ -45,34 +48,43 @@
if (closed) return;
closed = true;
dialog.parentNode.removeChild(dialog);
+ me.focus();
+
+ if (options.onClose) options.onClose(dialog);
}
}
+
var inp = dialog.getElementsByTagName("input")[0], button;
if (inp) {
- if (options && options.value) inp.value = options.value;
+ if (options.value) inp.value = options.value;
+
+ if (options.onInput)
+ CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
+ if (options.onKeyUp)
+ CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+
CodeMirror.on(inp, "keydown", function(e) {
if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
- if (e.keyCode == 13 || e.keyCode == 27) {
+ if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
inp.blur();
CodeMirror.e_stop(e);
close();
- me.focus();
- if (e.keyCode == 13) callback(inp.value);
}
+ if (e.keyCode == 13) callback(inp.value);
});
- if (options && options.onKeyUp) {
- CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
- }
- if (options && options.value) inp.value = options.value;
+
+ if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
+
inp.focus();
- CodeMirror.on(inp, "blur", close);
} else if (button = dialog.getElementsByTagName("button")[0]) {
CodeMirror.on(button, "click", function() {
close();
me.focus();
});
+
+ if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
+
button.focus();
- CodeMirror.on(button, "blur", close);
}
return close;
});
diff --git a/addon/edit/closetag.js b/addon/edit/closetag.js
index 414498bcd9..69ea4446be 100644
--- a/addon/edit/closetag.js
+++ b/addon/edit/closetag.js
@@ -109,6 +109,10 @@
replacements[i] = "/" + state.context.tagName + ">";
}
cm.replaceSelections(replacements);
+ ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++)
+ if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+ cm.indentLine(ranges[i].head.line);
}
function indexOf(collection, elt) {
diff --git a/addon/fold/xml-fold.js b/addon/fold/xml-fold.js
index a45da58422..504727f38c 100644
--- a/addon/fold/xml-fold.js
+++ b/addon/fold/xml-fold.js
@@ -151,8 +151,9 @@
if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
var start = end && toTagStart(iter);
- if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return;
+ if (!end || !start || cmp(iter, pos) > 0) return;
var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
+ if (end == "selfClose") return {open: here, close: null, at: "open"};
if (start[1]) { // closing tag
return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js
index fd58b8834e..20653b5318 100644
--- a/addon/hint/sql-hint.js
+++ b/addon/hint/sql-hint.js
@@ -21,7 +21,7 @@
function getKeywords(editor) {
var mode = editor.doc.modeOption;
- if(mode === "sql") mode = "text/x-sql";
+ if (mode === "sql") mode = "text/x-sql";
return CodeMirror.resolveMode(mode).keywords;
}
@@ -32,12 +32,12 @@
}
function addMatches(result, search, wordlist, formatter) {
- for(var word in wordlist) {
- if(!wordlist.hasOwnProperty(word)) continue;
- if(Array.isArray(wordlist)) {
+ for (var word in wordlist) {
+ if (!wordlist.hasOwnProperty(word)) continue;
+ if (Array.isArray(wordlist)) {
word = wordlist[word];
}
- if(match(search, word)) {
+ if (match(search, word)) {
result.push(formatter(word));
}
}
@@ -49,33 +49,30 @@
var string = token.string.substr(1);
var prevCur = Pos(cur.line, token.start);
var table = editor.getTokenAt(prevCur).string;
- if( !tables.hasOwnProperty( table ) ){
+ if (!tables.hasOwnProperty(table))
table = findTableByAlias(table, editor);
- }
var columns = tables[table];
- if(!columns) {
- return;
- }
- addMatches(result, string, columns,
- function(w) {return "." + w;});
+ if (!columns) return;
+
+ addMatches(result, string, columns, function(w) {return "." + w;});
}
function eachWord(lineText, f) {
- if( !lineText ){return;}
+ if (!lineText) return;
var excepted = /[,;]/g;
- var words = lineText.split( " " );
- for( var i = 0; i < words.length; i++ ){
- f( words[i]?words[i].replace( excepted, '' ) : '' );
+ var words = lineText.split(" ");
+ for (var i = 0; i < words.length; i++) {
+ f(words[i]?words[i].replace(excepted, '') : '');
}
}
- function convertCurToNumber( cur ){
+ function convertCurToNumber(cur) {
// max characters of a line is 999,999.
- return cur.line + cur.ch / Math.pow( 10, 6 );
+ return cur.line + cur.ch / Math.pow(10, 6);
}
- function convertNumberToCur( num ){
- return Pos(Math.floor( num ), +num.toString().split( '.' ).pop());
+ function convertNumberToCur(num) {
+ return Pos(Math.floor(num), +num.toString().split('.').pop());
}
function findTableByAlias(alias, editor) {
@@ -86,26 +83,26 @@
var table = "";
var separator = [];
var validRange = {
- start: Pos( 0, 0 ),
- end: Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).length )
+ start: Pos(0, 0),
+ end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
};
//add separator
- var indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV );
- while( indexOfSeparator != -1 ){
- separator.push( doc.posFromIndex(indexOfSeparator));
- indexOfSeparator = fullQuery.indexOf( CONS.QUERY_DIV, indexOfSeparator+1);
+ var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
+ while(indexOfSeparator != -1) {
+ separator.push(doc.posFromIndex(indexOfSeparator));
+ indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
}
- separator.unshift( Pos( 0, 0 ) );
- separator.push( Pos( editor.lastLine(), editor.getLineHandle( editor.lastLine() ).text.length ) );
+ separator.unshift(Pos(0, 0));
+ separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
- //find valieRange
+ //find valid range
var prevItem = 0;
- var current = convertCurToNumber( editor.getCursor() );
- for( var i=0; i< separator.length; i++){
- var _v = convertCurToNumber( separator[i] );
- if( current > prevItem && current <= _v ){
- validRange = { start: convertNumberToCur( prevItem ), end: convertNumberToCur( _v ) };
+ var current = convertCurToNumber(editor.getCursor());
+ for (var i=0; i< separator.length; i++) {
+ var _v = convertCurToNumber(separator[i]);
+ if (current > prevItem && current <= _v) {
+ validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
break;
}
prevItem = _v;
@@ -113,52 +110,51 @@
var query = doc.getRange(validRange.start, validRange.end, false);
- for(var i=0; i < query.length; i++){
+ for (var i = 0; i < query.length; i++) {
var lineText = query[i];
- eachWord( lineText, function( word ){
+ eachWord(lineText, function(word) {
var wordUpperCase = word.toUpperCase();
- if( wordUpperCase === aliasUpperCase && tables.hasOwnProperty( previousWord ) ){
+ if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
table = previousWord;
}
- if( wordUpperCase !== CONS.ALIAS_KEYWORD ){
+ if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
previousWord = word;
}
});
- if( table ){ break; }
+ if (table) break;
}
return table;
}
- function sqlHint(editor, options) {
+ CodeMirror.registerHelper("hint", "sql", function(editor, options) {
tables = (options && options.tables) || {};
keywords = keywords || getKeywords(editor);
var cur = editor.getCursor();
- var token = editor.getTokenAt(cur), end = token.end;
var result = [];
- var search = token.string.trim();
-
+ var token = editor.getTokenAt(cur), start, end, search;
+ if (token.string.match(/^[.\w@]\w*$/)) {
+ search = token.string;
+ start = token.start;
+ end = token.end;
+ } else {
+ start = end = cur.ch;
+ search = "";
+ }
if (search.charAt(0) == ".") {
columnCompletion(result, editor);
if (!result.length) {
- while (token.start && search.charAt(0) == ".") {
+ while (start && search.charAt(0) == ".") {
token = editor.getTokenAt(Pos(cur.line, token.start - 1));
+ start = token.start;
search = token.string + search;
}
- addMatches(result, search, tables,
- function(w) {return w;});
+ addMatches(result, search, tables, function(w) {return w;});
}
} else {
- addMatches(result, search, keywords,
- function(w) {return w.toUpperCase();});
- addMatches(result, search, tables,
- function(w) {return w;});
+ addMatches(result, search, tables, function(w) {return w;});
+ addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
}
- return {
- list: result,
- from: Pos(cur.line, token.start),
- to: Pos(cur.line, end)
- };
- }
- CodeMirror.registerHelper("hint", "sql", sqlHint);
+ return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
+ });
});
diff --git a/addon/merge/merge.css b/addon/merge/merge.css
index 63237fc8e9..5d24b9bb7f 100644
--- a/addon/merge/merge.css
+++ b/addon/merge/merge.css
@@ -62,6 +62,12 @@
color: #44c;
}
+.CodeMirror-merge-copy-reverse {
+ position: absolute;
+ cursor: pointer;
+ color: #44c;
+}
+
.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
diff --git a/addon/merge/merge.js b/addon/merge/merge.js
index 3583936198..d9b277664b 100644
--- a/addon/merge/merge.js
+++ b/addon/merge/merge.js
@@ -37,7 +37,7 @@
constructor: DiffView,
init: function(pane, orig, options) {
this.edit = this.mv.edit;
- this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options)));
+ this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options)));
this.diff = getDiff(asString(orig), asString(options.value));
this.diffOutOfDate = false;
@@ -71,7 +71,7 @@
function update(mode) {
if (mode == "full") {
if (dv.svg) clear(dv.svg);
- clear(dv.copyButtons);
+ if (dv.copyButtons) clear(dv.copyButtons);
clearMarks(dv.edit, edit.marked, dv.classes);
clearMarks(dv.orig, orig.marked, dv.classes);
edit.from = edit.to = orig.from = orig.to = 0;
@@ -257,7 +257,7 @@
var w = dv.gap.offsetWidth;
attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
}
- clear(dv.copyButtons);
+ if (dv.copyButtons) clear(dv.copyButtons);
var flip = dv.type == "left";
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
@@ -279,17 +279,30 @@
"d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
"class", dv.classes.connect);
}
- var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
- "CodeMirror-merge-copy"));
- copy.title = "Revert chunk";
- copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
- copy.style.top = top + "px";
+ if (dv.copyButtons) {
+ var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
+ "CodeMirror-merge-copy"));
+ var editOriginals = dv.mv.options.allowEditingOriginals;
+ copy.title = editOriginals ? "Push to left" : "Revert chunk";
+ copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
+ copy.style.top = top + "px";
+
+ if (editOriginals) {
+ var topReverse = dv.orig.heightAtLine(topEdit, "local") - sTopEdit;
+ var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc",
+ "CodeMirror-merge-copy-reverse"));
+ copyReverse.title = "Push to right";
+ copyReverse.chunk = {topEdit: topOrig, botEdit: botOrig, topOrig: topEdit, botOrig: botEdit};
+ copyReverse.style.top = topReverse + "px";
+ dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px";
+ }
+ }
});
}
- function copyChunk(dv, chunk) {
+ function copyChunk(dv, to, from, chunk) {
if (dv.diffOutOfDate) return;
- dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
+ to.replaceRange(from.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0));
}
@@ -298,6 +311,7 @@
var MergeView = CodeMirror.MergeView = function(node, options) {
if (!(this instanceof MergeView)) return new MergeView(node, options);
+ this.options = options;
var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
var hasLeft = origLeft != null, hasRight = origRight != null;
var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
@@ -323,6 +337,7 @@
(hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
wrap.push(elt("div", null, null, "height: 0; clear: both;"));
+
var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
this.edit = CodeMirror(editPane, copyObj(options));
@@ -345,12 +360,20 @@
lock.title = "Toggle locked scrolling";
var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
- dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
- CodeMirror.on(dv.copyButtons, "click", function(e) {
- var node = e.target || e.srcElement;
- if (node.chunk) copyChunk(dv, node.chunk);
- });
- var gapElts = [dv.copyButtons, lockWrap];
+ var gapElts = [lockWrap];
+ if (dv.mv.options.revertButtons !== false) {
+ dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
+ CodeMirror.on(dv.copyButtons, "click", function(e) {
+ var node = e.target || e.srcElement;
+ if (!node.chunk) return;
+ if (node.className == "CodeMirror-merge-copy-reverse") {
+ copyChunk(dv, dv.orig, dv.edit, node.chunk);
+ return;
+ }
+ copyChunk(dv, dv.edit, dv.orig, node.chunk);
+ });
+ gapElts.unshift(dv.copyButtons);
+ }
var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
if (svg && !svg.createSVGRect) svg = null;
dv.svg = svg;
diff --git a/addon/search/search.js b/addon/search/search.js
index b177dce6ed..c25aeda8b2 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -71,7 +71,7 @@
return query;
}
var queryDialog =
- 'Search: (Use /re/ syntax for regexp search)';
+ 'Search: (Use /re/ syntax for regexp search)';
function doSearch(cm, rev) {
var state = getSearchState(cm);
if (state.query) return findNext(cm, rev);
@@ -106,8 +106,8 @@
});}
var replaceQueryDialog =
- 'Replace: (Use /re/ syntax for regexp search)';
- var replacementQueryDialog = 'With: ';
+ 'Replace: (Use /re/ syntax for regexp search)';
+ var replacementQueryDialog = 'With: ';
var doReplaceConfirm = "Replace? ";
function replace(cm, all) {
if (cm.getOption("readOnly")) return;
diff --git a/bower.json b/bower.json
index 8c57fcdd4b..9cf2abecfe 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"4.4.0",
+ "version":"4.5.0",
"main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [
"**/.*",
diff --git a/doc/compress.html b/doc/compress.html
index 859210c45c..46c102af47 100644
--- a/doc/compress.html
+++ b/doc/compress.html
@@ -36,6 +36,7 @@
Version: