diff --git a/AUTHORS b/AUTHORS
index 455d7e68a5..afa3cc083a 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -89,6 +89,7 @@ Curtis Gagliardi
dagsta
daines
Dale Jung
+Dan Bentley
Dan Heberden
Daniel, Dao Quang Minh
Daniele Di Sarli
@@ -98,12 +99,15 @@ Daniel KJ
Daniel Neel
Daniel Parnell
Danny Yoo
+darealshinji
Darius Roberts
Dave Myers
David Mignot
David Pathakjee
+David Vázquez
deebugger
Deep Thought
+Devon Carew
dignifiedquire
Dimage Sapelkin
domagoj412
@@ -135,11 +139,13 @@ ForbesLindesay
Forbes Lindesay
Ford_Lawnmower
Frank Wiegand
+Gabriel Gheorghian
Gabriel Horner
Gabriel Nahmias
galambalazs
Gautam Mehta
gekkoe
+Gerard Braad
Gergely Hegykozi
Glenn Jorde
Glenn Ruehle
@@ -172,6 +178,7 @@ Ivan Kurnosov
Jacob Lee
Jakob Miland
Jakub Vrana
+Jakub Vrána
James Campos
James Thorne
Jamie Hill
@@ -182,6 +189,7 @@ Jan Odvarko
Jan T. Sott
Jared Forsyth
Jason
+Jason Barnabe
Jason Grout
Jason Johnston
Jason San Jose
@@ -217,6 +225,7 @@ kaniga
Ken Newman
Ken Rockot
Kevin Sawicki
+Kevin Ushey
Klaus Silveira
Koh Zi Han, Cliff
komakino
@@ -224,6 +233,7 @@ Konstantin Lopuhin
koops
ks-ifware
kubelsmieci
+Lanfei
Lanny
Laszlo Vidacs
leaf corcoran
@@ -285,6 +295,7 @@ MinRK
Miraculix87
misfo
mloginov
+Moritz Schwörer
mps
mtaran-google
Narciso Jaramillo
@@ -311,6 +322,7 @@ Page
Panupong Pasupat
paris
Patil Arpith
+Patrick Stoica
Patrick Strawderman
Paul Garvin
Paul Ivanov
@@ -325,11 +337,13 @@ prasanthj
Prasanth J
Radek Piórkowski
Rahul
+Randall Mason
Randy Burden
Randy Edmunds
Rasmus Erik Voel Jensen
Richard van der Meer
Richard Z.H. Wang
+Robert Crossfield
Roberto Abdelkader Martínez Pérez
robertop23
Robert Plummer
@@ -354,6 +368,7 @@ sheopory
Shiv Deepak
Shmuel Englard
Shubham Jain
+silverwind
snasa
soliton4
sonson
@@ -393,11 +408,13 @@ Vincent Woo
Volker Mische
wenli
Wesley Wiser
+Will Binns-Smith
William Jamieson
William Stein
Willy
Wojtek Ptak
Xavier Mendez
+Yassin N. Hassan
YNH Webdev
Yunchi Luo
Yuvi Panda
diff --git a/addon/dialog/dialog.js b/addon/dialog/dialog.js
index 5a88d99e64..e0e8ad4eb7 100644
--- a/addon/dialog/dialog.js
+++ b/addon/dialog/dialog.js
@@ -73,7 +73,7 @@
CodeMirror.e_stop(e);
close();
}
- if (e.keyCode == 13) callback(inp.value);
+ if (e.keyCode == 13) callback(inp.value, e);
});
if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
diff --git a/addon/display/panel.js b/addon/display/panel.js
new file mode 100644
index 0000000000..22c0453e8f
--- /dev/null
+++ b/addon/display/panel.js
@@ -0,0 +1,94 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ CodeMirror.defineExtension("addPanel", function(node, options) {
+ if (!this.state.panels) initPanels(this);
+
+ var info = this.state.panels;
+ if (options && options.position == "bottom")
+ info.wrapper.appendChild(node);
+ else
+ info.wrapper.insertBefore(node, info.wrapper.firstChild);
+ var height = (options && options.height) || node.offsetHeight;
+ this._setSize(null, info.heightLeft -= height);
+ info.panels++;
+ return new Panel(this, node, options, height);
+ });
+
+ function Panel(cm, node, options, height) {
+ this.cm = cm;
+ this.node = node;
+ this.options = options;
+ this.height = height;
+ this.cleared = false;
+ }
+
+ Panel.prototype.clear = function() {
+ if (this.cleared) return;
+ this.cleared = true;
+ var info = this.cm.state.panels;
+ this.cm._setSize(null, info.heightLeft += this.height);
+ info.wrapper.removeChild(this.node);
+ if (--info.panels == 0) removePanels(this.cm);
+ };
+
+ Panel.prototype.changed = function(height) {
+ var newHeight = height == null ? this.node.offsetHeight : height;
+ var info = this.cm.state.panels;
+ this.cm._setSize(null, info.height += (newHeight - this.height));
+ this.height = newHeight;
+ };
+
+ function initPanels(cm) {
+ var wrap = cm.getWrapperElement();
+ var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
+ var height = parseInt(style.height);
+ var info = cm.state.panels = {
+ setHeight: wrap.style.height,
+ heightLeft: height,
+ panels: 0,
+ wrapper: document.createElement("div")
+ };
+ wrap.parentNode.insertBefore(info.wrapper, wrap);
+ var hasFocus = cm.hasFocus();
+ info.wrapper.appendChild(wrap);
+ if (hasFocus) cm.focus();
+
+ cm._setSize = cm.setSize;
+ if (height != null) cm.setSize = function(width, newHeight) {
+ if (newHeight == null) return this._setSize(width, newHeight);
+ info.setHeight = newHeight;
+ if (typeof newHeight != "number") {
+ var px = /^(\d+\.?\d*)px$/.exec(newHeight);
+ if (px) {
+ newHeight = Number(px[1]);
+ } else {
+ info.wrapper.style.height = newHeight;
+ newHeight = info.wrapper.offsetHeight;
+ info.wrapper.style.height = "";
+ }
+ }
+ cm._setSize(width, info.heightLeft += (newHeight - height));
+ height = newHeight;
+ };
+ }
+
+ function removePanels(cm) {
+ var info = cm.state.panels;
+ cm.state.panels = null;
+
+ var wrap = cm.getWrapperElement();
+ info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
+ wrap.style.height = info.setHeight;
+ cm.setSize = cm._setSize;
+ cm.setSize();
+ }
+});
diff --git a/addon/edit/continuelist.js b/addon/edit/continuelist.js
index 9ad0a98fa0..ca8d26751a 100644
--- a/addon/edit/continuelist.js
+++ b/addon/edit/continuelist.js
@@ -11,9 +11,9 @@
})(function(CodeMirror) {
"use strict";
- var listRE = /^(\s*)([> ]+|[*+-]|(\d+)\.)(\s+)/,
- emptyListRE = /^(\s*)([> ]+|[*+-]|(\d+)\.)(\s*)$/,
- unorderedBullets = "*+-";
+ var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)\.)(\s*)/,
+ emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)\.)(\s*)$/,
+ unorderedListRE = /[*+-]\s/;
CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
if (cm.getOption("disableInput")) return CodeMirror.Pass;
@@ -38,7 +38,7 @@
} else {
var indent = match[1], after = match[4];
- var bullet = unorderedBullets.indexOf(match[2]) >= 0 || match[2].indexOf(">") >= 0
+ var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0
? match[2]
: (parseInt(match[3], 10) + 1) + ".";
diff --git a/addon/hint/anyword-hint.js b/addon/hint/anyword-hint.js
index be72e2bc75..8e74a92080 100644
--- a/addon/hint/anyword-hint.js
+++ b/addon/hint/anyword-hint.js
@@ -17,8 +17,7 @@
var word = options && options.word || WORD;
var range = options && options.range || RANGE;
var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
- var start = cur.ch, end = start;
- while (end < curLine.length && word.test(curLine.charAt(end))) ++end;
+ var end = cur.ch, start = end;
while (start && word.test(curLine.charAt(start - 1))) --start;
var curWord = start != end && curLine.slice(start, end);
diff --git a/addon/hint/css-hint.js b/addon/hint/css-hint.js
index 3300f4bd5a..488da3449e 100644
--- a/addon/hint/css-hint.js
+++ b/addon/hint/css-hint.js
@@ -20,7 +20,7 @@
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "css") return;
- var word = token.string, start = token.start, end = token.end;
+ var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
if (/[^\w$_-]/.test(word)) {
word = ""; start = end = cur.ch;
}
diff --git a/addon/hint/html-hint.js b/addon/hint/html-hint.js
old mode 100755
new mode 100644
index 992218f288..c6769bcae5
--- a/addon/hint/html-hint.js
+++ b/addon/hint/html-hint.js
@@ -3,7 +3,7 @@
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror", "./xml-hint"));
+ mod(require("../../lib/codemirror"), require("./xml-hint"));
else if (typeof define == "function" && define.amd) // AMD
define(["../../lib/codemirror", "./xml-hint"], mod);
else // Plain browser env
diff --git a/addon/hint/javascript-hint.js b/addon/hint/javascript-hint.js
index f6c2440062..7bcbf4a057 100644
--- a/addon/hint/javascript-hint.js
+++ b/addon/hint/javascript-hint.js
@@ -30,15 +30,20 @@
function scriptHint(editor, keywords, getToken, options) {
// Find the token at the cursor
- var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
+ var cur = editor.getCursor(), token = getToken(editor, cur);
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.
if (!/^[\w$_]*$/.test(token.string)) {
- token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
- type: token.string == "." ? "property" : null};
+ token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+ type: token.string == "." ? "property" : null};
+ } else if (token.end > cur.ch) {
+ token.end = cur.ch;
+ token.string = token.string.slice(0, cur.ch - token.start);
}
+
+ var tprop = token;
// If it is a property, find out what it is a property of.
while (tprop.type == "property") {
tprop = getToken(editor, Pos(cur.line, tprop.start));
diff --git a/addon/hint/python-hint.js b/addon/hint/python-hint.js
deleted file mode 100644
index 1b97f6ab0a..0000000000
--- a/addon/hint/python-hint.js
+++ /dev/null
@@ -1,102 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-(function(mod) {
- if (typeof exports == "object" && typeof module == "object") // CommonJS
- mod(require("../../lib/codemirror"));
- else if (typeof define == "function" && define.amd) // AMD
- define(["../../lib/codemirror"], mod);
- else // Plain browser env
- mod(CodeMirror);
-})(function(CodeMirror) {
- "use strict";
-
- function forEach(arr, f) {
- for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
- }
-
- function arrayContains(arr, item) {
- if (!Array.prototype.indexOf) {
- var i = arr.length;
- while (i--) {
- if (arr[i] === item) {
- return true;
- }
- }
- return false;
- }
- return arr.indexOf(item) != -1;
- }
-
- function scriptHint(editor, _keywords, getToken) {
- // Find the token at the cursor
- var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token;
- // If it's not a 'word-style' token, ignore the token.
-
- if (!/^[\w$_]*$/.test(token.string)) {
- token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state,
- className: token.string == ":" ? "python-type" : null};
- }
-
- if (!context) var context = [];
- context.push(tprop);
-
- var completionList = getCompletions(token, context);
- completionList = completionList.sort();
-
- return {list: completionList,
- from: CodeMirror.Pos(cur.line, token.start),
- to: CodeMirror.Pos(cur.line, token.end)};
- }
-
- function pythonHint(editor) {
- return scriptHint(editor, pythonKeywordsU, function (e, cur) {return e.getTokenAt(cur);});
- }
- CodeMirror.registerHelper("hint", "python", pythonHint);
-
- var pythonKeywords = "and del from not while as elif global or with assert else if pass yield"
-+ "break except import print class exec in raise continue finally is return def for lambda try";
- var pythonKeywordsL = pythonKeywords.split(" ");
- var pythonKeywordsU = pythonKeywords.toUpperCase().split(" ");
-
- var pythonBuiltins = "abs divmod input open staticmethod all enumerate int ord str "
-+ "any eval isinstance pow sum basestring execfile issubclass print super"
-+ "bin file iter property tuple bool filter len range type"
-+ "bytearray float list raw_input unichr callable format locals reduce unicode"
-+ "chr frozenset long reload vars classmethod getattr map repr xrange"
-+ "cmp globals max reversed zip compile hasattr memoryview round __import__"
-+ "complex hash min set apply delattr help next setattr buffer"
-+ "dict hex object slice coerce dir id oct sorted intern ";
- var pythonBuiltinsL = pythonBuiltins.split(" ").join("() ").split(" ");
- var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(" ").join("() ").split(" ");
-
- function getCompletions(token, context) {
- var found = [], start = token.string;
- function maybeAdd(str) {
- if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
- }
-
- function gatherCompletions(_obj) {
- forEach(pythonBuiltinsL, maybeAdd);
- forEach(pythonBuiltinsU, maybeAdd);
- forEach(pythonKeywordsL, maybeAdd);
- forEach(pythonKeywordsU, maybeAdd);
- }
-
- if (context) {
- // If this is a property, see if it belongs to some object we can
- // find in the current environment.
- var obj = context.pop(), base;
-
- if (obj.type == "variable")
- base = obj.string;
- else if(obj.type == "variable-3")
- base = ":" + obj.string;
-
- while (base != null && context.length)
- base = base[context.pop().string];
- if (base != null) gatherCompletions(base);
- }
- return found;
- }
-});
diff --git a/addon/hint/show-hint.js b/addon/hint/show-hint.js
index 27b770bdef..fda5ffaa16 100644
--- a/addon/hint/show-hint.js
+++ b/addon/hint/show-hint.js
@@ -243,7 +243,7 @@
}
}
}
- var overlapX = box.left - winW;
+ var overlapX = box.right - winW;
if (overlapX > 0) {
if (box.right - box.left > winW) {
hints.style.width = (winW - 5) + "px";
diff --git a/addon/hint/sql-hint.js b/addon/hint/sql-hint.js
index c2b511fa2f..92c889e139 100644
--- a/addon/hint/sql-hint.js
+++ b/addon/hint/sql-hint.js
@@ -44,9 +44,7 @@
}
}
- function nameCompletion(result, editor) {
- var cur = editor.getCursor();
- var token = editor.getTokenAt(cur);
+ function nameCompletion(cur, token, result, editor) {
var useBacktick = (token.string.charAt(0) == "`");
var string = token.string.substr(1);
var prevToken = editor.getTokenAt(Pos(cur.line, token.start));
@@ -173,6 +171,11 @@
var cur = editor.getCursor();
var result = [];
var token = editor.getTokenAt(cur), start, end, search;
+ if (token.end > cur.ch) {
+ token.end = cur.ch;
+ token.string = token.string.slice(0, cur.ch - token.start);
+ }
+
if (token.string.match(/^[.`\w@]\w*$/)) {
search = token.string;
start = token.start;
@@ -182,7 +185,7 @@
search = "";
}
if (search.charAt(0) == "." || search.charAt(0) == "`") {
- nameCompletion(result, editor);
+ nameCompletion(cur, token, result, editor);
} else {
addMatches(result, search, tables, function(w) {return w;});
addMatches(result, search, defaultTable, function(w) {return w;});
diff --git a/addon/hint/xml-hint.js b/addon/hint/xml-hint.js
index cc95b20f34..9b9baa0c67 100644
--- a/addon/hint/xml-hint.js
+++ b/addon/hint/xml-hint.js
@@ -18,10 +18,9 @@
var quote = (options && options.quoteChar) || '"';
if (!tags) return;
var cur = cm.getCursor(), token = cm.getTokenAt(cur);
- if (/^<\/?$/.test(token.string) && token.end == cur.ch) {
- var nextToken = cm.getTokenAt(Pos(cur.line, cur.ch + 1));
- if (nextToken.start == cur.ch && /\btag\b/.test(nextToken.type))
- token = nextToken;
+ if (token.end > cur.ch) {
+ token.end = cur.ch;
+ token.string = token.string.slice(0, cur.ch - token.start);
}
var inner = CodeMirror.innerMode(cm.getMode(), token.state);
if (inner.mode.name != "xml") return;
diff --git a/addon/merge/merge.css b/addon/merge/merge.css
index 5d24b9bb7f..a6a80e43e0 100644
--- a/addon/merge/merge.css
+++ b/addon/merge/merge.css
@@ -96,3 +96,17 @@
.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
+
+.CodeMirror-merge-collapsed-widget:before {
+ content: "(...)";
+}
+.CodeMirror-merge-collapsed-widget {
+ cursor: pointer;
+ color: #88b;
+ background: #eef;
+ border: 1px solid #ddf;
+ font-size: 90%;
+ padding: 0 3px;
+ border-radius: 4px;
+}
+.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }
diff --git a/addon/merge/merge.js b/addon/merge/merge.js
index da3ea47ccf..ed22b60024 100644
--- a/addon/merge/merge.js
+++ b/addon/merge/merge.js
@@ -31,6 +31,8 @@
insert: "CodeMirror-merge-r-inserted",
del: "CodeMirror-merge-r-deleted",
connect: "CodeMirror-merge-r-connect"};
+ if (mv.options.connect == "align")
+ this.aligners = [];
}
DiffView.prototype = {
@@ -81,7 +83,7 @@
updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
}
- drawConnectors(dv);
+ makeConnections(dv);
}
function set(slow) {
clearTimeout(debounceChange);
@@ -108,10 +110,10 @@
function registerScroll(dv) {
dv.edit.on("scroll", function() {
- syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
+ syncScroll(dv, DIFF_INSERT) && makeConnections(dv);
});
dv.orig.on("scroll", function() {
- syncScroll(dv, DIFF_DELETE) && drawConnectors(dv);
+ syncScroll(dv, DIFF_DELETE) && makeConnections(dv);
});
}
@@ -126,24 +128,29 @@
// (to prevent feedback loops)
if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 50 > now) return false;
- var sInfo = editor.getScrollInfo(), halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
- var mid = editor.lineAtHeight(midY, "local");
- var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
- var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
- var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
- var ratio = (midY - off.top) / (off.bot - off.top);
- var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
-
- var botDist, mix;
- // Some careful tweaking to make sure no space is left out of view
- // when scrolling to top or bottom.
- if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
- targetPos = targetPos * mix + sInfo.top * (1 - mix);
- } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
- var otherInfo = other.getScrollInfo();
- var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
- if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
- targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
+ var sInfo = editor.getScrollInfo();
+ if (dv.mv.options.connect == "align") {
+ targetPos = sInfo.top;
+ } else {
+ var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
+ var mid = editor.lineAtHeight(midY, "local");
+ var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
+ var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
+ var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
+ var ratio = (midY - off.top) / (off.bot - off.top);
+ var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
+
+ var botDist, mix;
+ // Some careful tweaking to make sure no space is left out of view
+ // when scrolling to top or bottom.
+ if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
+ targetPos = targetPos * mix + sInfo.top * (1 - mix);
+ } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
+ var otherInfo = other.getScrollInfo();
+ var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
+ if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
+ targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
+ }
}
other.scrollTo(sInfo.left, targetPos);
@@ -161,7 +168,7 @@
function setScrollLock(dv, val, action) {
dv.lockScroll = val;
- if (val && action != false) syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
+ if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv);
dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db \u21da";
}
@@ -249,9 +256,20 @@
// Updating the gap between editor and original
- function drawConnectors(dv) {
+ function makeConnections(dv) {
if (!dv.showDifferences) return;
+ var align = dv.mv.options.connect == "align";
+ if (align) {
+ if (!dv.orig.curOp) return dv.orig.operation(function() {
+ makeConnections(dv);
+ });
+ for (var i = 0; i < dv.aligners.length; i++)
+ dv.aligners[i].clear();
+ dv.aligners.length = 0;
+ var extraSpaceAbove = {edit: 0, orig: 0};
+ }
+
if (dv.svg) {
clear(dv.svg);
var w = dv.gap.offsetWidth;
@@ -259,45 +277,82 @@
}
if (dv.copyButtons) clear(dv.copyButtons);
- var flip = dv.type == "left";
var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top;
iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
- if (topEdit > vpEdit.to || botEdit < vpEdit.from ||
- topOrig > vpOrig.to || botOrig < vpOrig.from)
- return;
- var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx;
- if (dv.svg) {
- var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
- if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
- var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
- var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
- if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
- var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
- var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
- attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
- "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
- "class", dv.classes.connect);
- }
- 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";
- }
+ if (topEdit <= vpEdit.to && botEdit >= vpEdit.from &&
+ topOrig <= vpOrig.to && botOrig >= vpOrig.from)
+ drawConnectorsForChunk(dv, topOrig, botOrig, topEdit, botEdit, sTopOrig, sTopEdit, w);
+ if (align && (topEdit <= vpEdit.to || topOrig <= vpOrig.to)) {
+ var above = (botEdit < vpEdit.from && botOrig < vpOrig.from);
+ alignChunks(dv, topOrig, botOrig, topEdit, botEdit, above && extraSpaceAbove);
}
});
+ if (align) {
+ if (extraSpaceAbove.edit)
+ dv.aligners.push(padBelow(dv.edit, 0, extraSpaceAbove.edit));
+ if (extraSpaceAbove.orig)
+ dv.aligners.push(padBelow(dv.orig, 0, extraSpaceAbove.orig));
+ }
+ }
+
+ function drawConnectorsForChunk(dv, topOrig, botOrig, topEdit, botEdit, sTopOrig, sTopEdit, w) {
+ var flip = dv.type == "left";
+ var top = dv.orig.heightAtLine(topOrig, "local") - sTopOrig;
+ if (dv.svg) {
+ var topLpx = top;
+ var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
+ if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
+ var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
+ var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
+ if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
+ var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
+ var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
+ attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
+ "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
+ "class", dv.classes.connect);
+ }
+ 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 alignChunks(dv, topOrig, botOrig, topEdit, botEdit, aboveViewport) {
+ var topOrigPx = dv.orig.heightAtLine(topOrig, "local");
+ var botOrigPx = dv.orig.heightAtLine(botOrig, "local");
+ var topEditPx = dv.edit.heightAtLine(topEdit, "local");
+ var botEditPx = dv.edit.heightAtLine(botEdit, "local");
+ var origH = botOrigPx -topOrigPx, editH = botEditPx - topEditPx;
+ var diff = editH - origH;
+ if (diff > 1) {
+ if (aboveViewport) aboveViewport.orig += diff;
+ else dv.aligners.push(padBelow(dv.orig, botOrig - 1, diff));
+ } else if (diff < -1) {
+ if (aboveViewport) aboveViewport.edit -= diff;
+ else dv.aligners.push(padBelow(dv.edit, botEdit - 1, -diff));
+ }
+ return 0;
+ }
+
+ function padBelow(cm, line, size) {
+ var elt = document.createElement("div");
+ elt.style.height = size + "px"; elt.style.minWidth = "1px";
+ return cm.addLineWidget(line, elt, {height: size});
}
function copyChunk(dv, to, from, chunk) {
@@ -313,6 +368,13 @@
this.options = options;
var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
+ if (origLeft && origRight) {
+ if (options.connect == "align")
+ throw new Error("connect: \"align\" is not supported for three-way merge views");
+ if (options.collapseIdentical)
+ throw new Error("collapseIdentical option is not supported for three-way merge views");
+ }
+
var hasLeft = origLeft != null, hasRight = origRight != null;
var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
var wrap = [], left = this.left = null, right = this.right = null;
@@ -344,9 +406,12 @@
if (left) left.init(leftPane, origLeft, options);
if (right) right.init(rightPane, origRight, options);
+ if (options.collapseIdentical)
+ collapseIdenticalStretches(left || right, options.collapseIdentical);
+
var onResize = function() {
- if (left) drawConnectors(left);
- if (right) drawConnectors(right);
+ if (left) makeConnections(left);
+ if (right) makeConnections(right);
};
CodeMirror.on(window, "resize", onResize);
var resizeInterval = setInterval(function() {
@@ -374,10 +439,12 @@
});
gapElts.unshift(dv.copyButtons);
}
- var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
- if (svg && !svg.createSVGRect) svg = null;
- dv.svg = svg;
- if (svg) gapElts.push(svg);
+ if (dv.mv.options.connect != "align") {
+ var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
+ if (svg && !svg.createSVGRect) svg = null;
+ dv.svg = svg;
+ if (svg) gapElts.push(svg);
+ }
return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap");
}
@@ -489,6 +556,46 @@
return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
}
+ function collapseSingle(cm, from, to) {
+ cm.addLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
+ var widget = document.createElement("span");
+ widget.className = "CodeMirror-merge-collapsed-widget";
+ widget.title = "Identical text collapsed. Click to expand.";
+ var mark = cm.markText(Pos(from, 0), Pos(to - 1), {
+ inclusiveLeft: true,
+ inclusiveRight: true,
+ replacedWith: widget,
+ clearOnEnter: true
+ });
+ function clear() {
+ mark.clear();
+ cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
+ }
+ widget.addEventListener("click", clear);
+ return {mark: mark, clear: clear};
+ }
+
+ function collapseStretch(dv, origStart, editStart, size) {
+ var mOrig = collapseSingle(dv.orig, origStart, origStart + size);
+ var mEdit = collapseSingle(dv.edit, editStart, editStart + size);
+ mOrig.mark.on("clear", function() { mEdit.clear(); });
+ mEdit.mark.on("clear", function() { mOrig.clear(); });
+ }
+
+ function collapseIdenticalStretches(dv, margin) {
+ if (typeof margin != "number") margin = 2;
+ var lastOrig = dv.orig.firstLine(), lastEdit = dv.edit.firstLine();
+ iterateChunks(dv.diff, function(topOrig, botOrig, _topEdit, botEdit) {
+ var identicalSize = topOrig - margin - lastOrig;
+ if (identicalSize > margin)
+ collapseStretch(dv, lastOrig, lastEdit, identicalSize);
+ lastOrig = botOrig + margin; lastEdit = botEdit + margin;
+ });
+ var bottomSize = dv.orig.lastLine() + 1 - lastOrig;
+ if (bottomSize > margin)
+ collapseStretch(dv, lastOrig, lastEdit, bottomSize);
+ }
+
// General utilities
function elt(tag, content, className, style) {
diff --git a/addon/mode/simple.js b/addon/mode/simple.js
index a4a86b9a65..0a48a95367 100644
--- a/addon/mode/simple.js
+++ b/addon/mode/simple.js
@@ -11,9 +11,9 @@
})(function(CodeMirror) {
"use strict";
- CodeMirror.defineSimpleMode = function(name, states, props) {
+ CodeMirror.defineSimpleMode = function(name, states) {
CodeMirror.defineMode(name, function(config) {
- return CodeMirror.simpleMode(config, states, props);
+ return CodeMirror.simpleMode(config, states);
});
};
@@ -194,12 +194,15 @@
var pos = state.indent.length - 1, rules = states[state.state];
scan: for (;;) {
for (var i = 0; i < rules.length; i++) {
- var rule = rules[i], m = rule.regex.exec(textAfter);
- if (m && m[0]) {
- if (rule.data.dedent && rule.data.dedentIfLineStart !== false) pos--;
- if (rule.next || rule.push) rules = states[rule.next || rule.push];
- textAfter = textAfter.slice(m[0].length);
- continue scan;
+ var rule = rules[i];
+ if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
+ var m = rule.regex.exec(textAfter);
+ if (m && m[0]) {
+ pos--;
+ if (rule.next || rule.push) rules = states[rule.next || rule.push];
+ textAfter = textAfter.slice(m[0].length);
+ continue scan;
+ }
}
}
break;
diff --git a/addon/scroll/annotatescrollbar.js b/addon/scroll/annotatescrollbar.js
new file mode 100644
index 0000000000..6dfff1a6a4
--- /dev/null
+++ b/addon/scroll/annotatescrollbar.js
@@ -0,0 +1,76 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineExtension("annotateScrollbar", function(className) {
+ return new Annotation(this, className);
+ });
+
+ function Annotation(cm, className) {
+ this.cm = cm;
+ this.className = className;
+ this.annotations = [];
+ this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
+ this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
+ this.computeScale();
+
+ var self = this;
+ cm.on("refresh", this.resizeHandler = function(){
+ if (self.computeScale()) self.redraw();
+ });
+ }
+
+ Annotation.prototype.computeScale = function() {
+ var cm = this.cm;
+ var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight) /
+ cm.heightAtLine(cm.lastLine() + 1, "local");
+ if (hScale != this.hScale) {
+ this.hScale = hScale;
+ return true;
+ }
+ };
+
+ Annotation.prototype.update = function(annotations) {
+ this.annotations = annotations;
+ this.redraw();
+ };
+
+ Annotation.prototype.redraw = function() {
+ var cm = this.cm, hScale = this.hScale;
+ if (!cm.display.barWidth) return;
+
+ var frag = document.createDocumentFragment(), anns = this.annotations;
+ for (var i = 0, nextTop; i < anns.length; i++) {
+ var ann = anns[i];
+ var top = nextTop || cm.charCoords(ann.from, "local").top * hScale;
+ var bottom = cm.charCoords(ann.to, "local").bottom * hScale;
+ while (i < anns.length - 1) {
+ nextTop = cm.charCoords(anns[i + 1].from, "local").top * hScale;
+ if (nextTop > bottom + .9) break;
+ ann = anns[++i];
+ bottom = cm.charCoords(ann.to, "local").bottom * hScale;
+ }
+ var height = Math.max(bottom - top, 3);
+
+ var elt = frag.appendChild(document.createElement("div"));
+ elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: " + top + "px; height: " + height + "px";
+ elt.className = this.className;
+ }
+ this.div.textContent = "";
+ this.div.appendChild(frag);
+ };
+
+ Annotation.prototype.clear = function() {
+ this.cm.off("refresh", this.resizeHandler);
+ this.div.parentNode.removeChild(this.div);
+ };
+});
diff --git a/addon/scroll/simplescrollbars.css b/addon/scroll/simplescrollbars.css
new file mode 100644
index 0000000000..5eea7aa1b3
--- /dev/null
+++ b/addon/scroll/simplescrollbars.css
@@ -0,0 +1,66 @@
+.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
+ position: absolute;
+ background: #ccc;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ border: 1px solid #bbb;
+ border-radius: 2px;
+}
+
+.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
+ position: absolute;
+ z-index: 6;
+ background: #eee;
+}
+
+.CodeMirror-simplescroll-horizontal {
+ bottom: 0; left: 0;
+ height: 8px;
+}
+.CodeMirror-simplescroll-horizontal div {
+ bottom: 0;
+ height: 100%;
+}
+
+.CodeMirror-simplescroll-vertical {
+ right: 0; top: 0;
+ width: 8px;
+}
+.CodeMirror-simplescroll-vertical div {
+ right: 0;
+ width: 100%;
+}
+
+
+.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
+ display: none;
+}
+
+.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
+ position: absolute;
+ background: #bcd;
+ border-radius: 3px;
+}
+
+.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
+ position: absolute;
+ z-index: 6;
+}
+
+.CodeMirror-overlayscroll-horizontal {
+ bottom: 0; left: 0;
+ height: 6px;
+}
+.CodeMirror-overlayscroll-horizontal div {
+ bottom: 0;
+ height: 100%;
+}
+
+.CodeMirror-overlayscroll-vertical {
+ right: 0; top: 0;
+ width: 6px;
+}
+.CodeMirror-overlayscroll-vertical div {
+ right: 0;
+ width: 100%;
+}
diff --git a/addon/scroll/simplescrollbars.js b/addon/scroll/simplescrollbars.js
new file mode 100644
index 0000000000..739aa7beb4
--- /dev/null
+++ b/addon/scroll/simplescrollbars.js
@@ -0,0 +1,139 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ function Bar(cls, orientation, scroll) {
+ this.orientation = orientation;
+ this.scroll = scroll;
+ this.screen = this.total = this.size = 1;
+ this.pos = 0;
+
+ this.node = document.createElement("div");
+ this.node.className = cls + "-" + orientation;
+ this.inner = this.node.appendChild(document.createElement("div"));
+
+ var self = this;
+ CodeMirror.on(this.inner, "mousedown", function(e) {
+ if (e.which != 1) return;
+ CodeMirror.e_preventDefault(e);
+ var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
+ var start = e[axis], startpos = self.pos;
+ function move(e) {
+ if (e.which != 1) {
+ CodeMirror.off(document, "mousemove", move);
+ return;
+ }
+ self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
+ }
+ CodeMirror.on(document, "mousemove", move);
+ });
+
+ CodeMirror.on(this.node, "click", function(e) {
+ CodeMirror.e_preventDefault(e);
+ var innerBox = self.inner.getBoundingClientRect(), where;
+ if (self.orientation == "horizontal")
+ where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
+ else
+ where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
+ self.moveTo(self.pos + where * self.screen);
+ });
+
+ function onWheel(e) {
+ var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
+ var oldPos = self.pos;
+ self.moveTo(self.pos + moved);
+ if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
+ }
+ CodeMirror.on(this.node, "mousewheel", onWheel);
+ CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
+ }
+
+ Bar.prototype.moveTo = function(pos, update) {
+ if (pos < 0) pos = 0;
+ if (pos > this.total - this.screen) pos = this.total - this.screen;
+ if (pos == this.pos) return;
+ this.pos = pos;
+ this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
+ (pos * (this.size / this.total)) + "px";
+ if (update !== false) this.scroll(pos, this.orientation);
+ };
+
+ Bar.prototype.update = function(scrollSize, clientSize, barSize) {
+ this.screen = clientSize;
+ this.total = scrollSize;
+ this.size = barSize;
+
+ // FIXME clip to min size?
+ this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
+ this.screen * (this.size / this.total) + "px";
+ this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
+ this.pos * (this.size / this.total) + "px";
+ };
+
+ function SimpleScrollbars(cls, place, scroll) {
+ this.addClass = cls;
+ this.horiz = new Bar(cls, "horizontal", scroll);
+ place(this.horiz.node);
+ this.vert = new Bar(cls, "vertical", scroll);
+ place(this.vert.node);
+ this.width = null;
+ }
+
+ SimpleScrollbars.prototype.update = function(measure) {
+ if (this.width == null) {
+ var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
+ if (style) this.width = parseInt(style.height);
+ }
+ var width = this.width || 0;
+
+ var needsH = measure.scrollWidth > measure.clientWidth + 1;
+ var needsV = measure.scrollHeight > measure.clientHeight + 1;
+ this.vert.node.style.display = needsV ? "block" : "none";
+ this.horiz.node.style.display = needsH ? "block" : "none";
+
+ if (needsV) {
+ this.vert.update(measure.scrollHeight, measure.clientHeight,
+ measure.viewHeight - (needsH ? width : 0));
+ this.vert.node.style.display = "block";
+ this.vert.node.style.bottom = needsH ? width + "px" : "0";
+ }
+ if (needsH) {
+ this.horiz.update(measure.scrollWidth, measure.clientWidth,
+ measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
+ this.horiz.node.style.right = needsV ? width + "px" : "0";
+ this.horiz.node.style.left = measure.barLeft + "px";
+ }
+
+ return {right: needsV ? width : 0, bottom: needsH ? width : 0};
+ };
+
+ SimpleScrollbars.prototype.setScrollTop = function(pos) {
+ this.vert.moveTo(pos, false);
+ };
+
+ SimpleScrollbars.prototype.setScrollLeft = function(pos) {
+ this.horiz.moveTo(pos, false);
+ };
+
+ SimpleScrollbars.prototype.clear = function() {
+ var parent = this.horiz.node.parentNode;
+ parent.removeChild(this.horiz.node);
+ parent.removeChild(this.vert.node);
+ };
+
+ CodeMirror.scrollbarModel.simple = function(place, scroll) {
+ return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
+ };
+ CodeMirror.scrollbarModel.overlay = function(place, scroll) {
+ return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
+ };
+});
diff --git a/addon/search/matchesonscrollbar.css b/addon/search/matchesonscrollbar.css
new file mode 100644
index 0000000000..77932cc908
--- /dev/null
+++ b/addon/search/matchesonscrollbar.css
@@ -0,0 +1,8 @@
+.CodeMirror-search-match {
+ background: gold;
+ border-top: 1px solid orange;
+ border-bottom: 1px solid orange;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+ opacity: .5;
+}
diff --git a/addon/search/matchesonscrollbar.js b/addon/search/matchesonscrollbar.js
new file mode 100644
index 0000000000..937d3f786e
--- /dev/null
+++ b/addon/search/matchesonscrollbar.js
@@ -0,0 +1,90 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, className) {
+ return new SearchAnnotation(this, query, caseFold, className);
+ });
+
+ function SearchAnnotation(cm, query, caseFold, className) {
+ this.cm = cm;
+ this.annotation = cm.annotateScrollbar(className || "CodeMirror-search-match");
+ this.query = query;
+ this.caseFold = caseFold;
+ this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
+ this.matches = [];
+ this.update = null;
+
+ this.findMatches();
+ this.annotation.update(this.matches);
+
+ var self = this;
+ cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
+ }
+
+ var MAX_MATCHES = 1000;
+
+ SearchAnnotation.prototype.findMatches = function() {
+ if (!this.gap) return;
+ for (var i = 0; i < this.matches.length; i++) {
+ var match = this.matches[i];
+ if (match.from.line >= this.gap.to) break;
+ if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
+ }
+ var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), this.caseFold);
+ while (cursor.findNext()) {
+ var match = {from: cursor.from(), to: cursor.to()};
+ if (match.from.line >= this.gap.to) break;
+ this.matches.splice(i++, 0, match);
+ if (this.matches.length > MAX_MATCHES) break;
+ }
+ this.gap = null;
+ };
+
+ function offsetLine(line, changeStart, sizeChange) {
+ if (line <= changeStart) return line;
+ return Math.max(changeStart, line + sizeChange);
+ }
+
+ SearchAnnotation.prototype.onChange = function(change) {
+ var startLine = change.from.line;
+ var endLine = CodeMirror.changeEnd(change).line;
+ var sizeChange = endLine - change.to.line;
+ if (this.gap) {
+ this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
+ this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
+ } else {
+ this.gap = {from: change.from.line, to: endLine + 1};
+ }
+
+ if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
+ var match = this.matches[i];
+ var newFrom = offsetLine(match.from.line, startLine, sizeChange);
+ if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
+ var newTo = offsetLine(match.to.line, startLine, sizeChange);
+ if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
+ }
+ clearTimeout(this.update);
+ var self = this;
+ this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
+ };
+
+ SearchAnnotation.prototype.updateAfterChange = function() {
+ this.findMatches();
+ this.annotation.update(this.matches);
+ };
+
+ SearchAnnotation.prototype.clear = function() {
+ this.cm.off("change", this.changeHandler);
+ this.annotation.clear();
+ };
+});
diff --git a/addon/search/search.js b/addon/search/search.js
index c25aeda8b2..0251067a73 100644
--- a/addon/search/search.js
+++ b/addon/search/search.js
@@ -63,11 +63,11 @@
function parseQuery(query) {
var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
if (isRE) {
- query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i");
- if (query.test("")) query = /x^/;
- } else if (query == "") {
- query = /x^/;
+ try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
+ catch(e) {} // Not a regular expression after all, do a string search
}
+ if (typeof query == "string" ? query == "" : query.test(""))
+ query = /x^/;
return query;
}
var queryDialog =
@@ -82,6 +82,10 @@
cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
cm.addOverlay(state.overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+ }
state.posFrom = state.posTo = cm.getCursor();
findNext(cm, rev);
});
@@ -103,6 +107,7 @@
if (!state.query) return;
state.query = null;
cm.removeOverlay(state.overlay);
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
});}
var replaceQueryDialog =
diff --git a/addon/tern/tern.js b/addon/tern/tern.js
index 9bd69446a2..86729e2d3f 100644
--- a/addon/tern/tern.js
+++ b/addon/tern/tern.js
@@ -106,7 +106,9 @@
cm.showHint({hint: this.getHint});
},
- showType: function(cm, pos, c) { showType(this, cm, pos, c); },
+ showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
+
+ showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
updateArgHints: function(cm) { updateArgHints(this, cm); },
@@ -239,8 +241,8 @@
// Type queries
- function showType(ts, cm, pos, c) {
- ts.request(cm, "type", function(error, data) {
+ function showContextInfo(ts, cm, pos, queryName, c) {
+ ts.request(cm, queryName, function(error, data) {
if (error) return showError(ts, cm, error);
if (ts.options.typeTip) {
var tip = ts.options.typeTip(data);
diff --git a/bower.json b/bower.json
index bf28a95f87..dfbe3d42fd 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "codemirror",
- "version":"4.8.0",
+ "version":"4.9.0",
"main": ["lib/codemirror.js", "lib/codemirror.css"],
"ignore": [
"**/.*",
diff --git a/demo/merge.html b/demo/merge.html
index 4517c548ee..dad1952757 100644
--- a/demo/merge.html
+++ b/demo/merge.html
@@ -41,33 +41,41 @@
The merge
addon provides an interface for displaying and merging diffs,
-either two-way
-or three-way. The left
-(or center) pane is editable, and the differences with the other
-pane(s) are optionally shown live as you edit it.
+either two-way
+or three-way.
+The left (or center) pane is editable, and the differences with the
+other pane(s) are optionally shown live as you edit
+it. In the two-way configuration, there are also options to pad changed
+sections to align them, and to collapse unchanged
+stretches of text.
By setting an editor's height style
+to auto and giving
the viewportMargin
a value of Infinity, CodeMirror can be made to
automatically resize to fit its content.
This is a list of every mode in the distribution. Each mode lives
+
This is a list of every mode in the distribution. Each mode lives
in a subdirectory of the mode/ directory, and typically
defines a single JavaScript file that implements the mode. Loading
such file will make the language available to CodeMirror, through
-the mode
+the mode
option.