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.

This addon depends on the google-diff-match-patch library to compute the diffs.

+ + + + + + +
+

Panel Demo

+
+ + + +

The panel +addon allows you to display panels above or below an editor. Click the +links in the previous paragraph to add panels to the editor.

+ +
diff --git a/demo/resize.html b/demo/resize.html index 69eae2c39b..1c1ef390ab 100644 --- a/demo/resize.html +++ b/demo/resize.html @@ -12,10 +12,6 @@ border: 1px solid #eee; height: auto; } - .CodeMirror-scroll { - overflow-y: hidden; - overflow-x: auto; - }