From 4cda5e69ea89c6e48bdc4f340c8c535a017c86ad Mon Sep 17 00:00:00 2001 From: markhillard Date: Wed, 15 Mar 2017 23:36:30 -0400 Subject: [PATCH] Upgrade to CodeMirror 5.24.2 --- codemirror/AUTHORS | 7 + codemirror/CHANGELOG.md | 50 + codemirror/LICENSE | 2 + codemirror/addon/comment/comment.js | 11 +- codemirror/addon/edit/closebrackets.js | 2 +- codemirror/addon/edit/continuelist.js | 2 +- codemirror/addon/fold/indent-fold.js | 34 +- codemirror/addon/merge/merge.js | 265 ++++- codemirror/addon/scroll/annotatescrollbar.js | 6 +- codemirror/demo/folding.html | 29 + codemirror/demo/search.html | 2 +- codemirror/doc/manual.html | 30 +- codemirror/doc/realworld.html | 1 + codemirror/doc/releases.html | 28 + codemirror/index.html | 2 +- codemirror/keymap/sublime.js | 17 +- codemirror/keymap/vim.js | 66 +- codemirror/lib/codemirror.js | 1034 +++++++++-------- codemirror/mode/clike/clike.js | 15 +- codemirror/mode/commonlisp/commonlisp.js | 2 +- codemirror/mode/crystal/crystal.js | 88 +- codemirror/mode/crystal/index.html | 17 +- codemirror/mode/css/css.js | 6 + codemirror/mode/css/index.html | 2 +- codemirror/mode/dylan/dylan.js | 18 +- codemirror/mode/go/go.js | 5 +- codemirror/mode/htmlmixed/htmlmixed.js | 2 +- codemirror/mode/javascript/javascript.js | 7 +- codemirror/mode/javascript/test.js | 22 + codemirror/mode/julia/julia.js | 2 +- codemirror/mode/markdown/markdown.js | 7 +- codemirror/mode/octave/octave.js | 6 +- codemirror/mode/powershell/powershell.js | 2 + codemirror/mode/powershell/test.js | 10 +- codemirror/mode/python/python.js | 12 +- codemirror/mode/sass/index.html | 4 +- codemirror/mode/sass/sass.js | 94 +- codemirror/mode/sass/test.js | 122 ++ codemirror/mode/shell/shell.js | 23 +- codemirror/mode/shell/test.js | 3 + codemirror/mode/soy/soy.js | 1 + codemirror/package.json | 2 +- codemirror/src/display/scrollbars.js | 2 +- codemirror/src/edit/commands.js | 23 +- codemirror/src/edit/main.js | 2 +- codemirror/src/edit/methods.js | 44 +- codemirror/src/input/ContentEditableInput.js | 1 + codemirror/src/input/TextareaInput.js | 10 +- codemirror/src/input/movement.js | 109 ++ codemirror/src/line/line_data.js | 13 +- codemirror/src/line/pos.js | 10 +- codemirror/src/line/spans.js | 9 +- .../src/measurement/position_measurement.js | 167 +-- codemirror/src/model/chunk.js | 89 +- codemirror/src/model/line_widget.js | 69 +- codemirror/src/model/mark_text.js | 235 ++-- codemirror/src/model/selection.js | 50 +- codemirror/src/util/StringStream.js | 62 +- codemirror/src/util/bidi.js | 87 +- codemirror/src/util/browser.js | 11 +- codemirror/src/util/misc.js | 27 +- codemirror/test/doc_test.js | 24 +- codemirror/test/driver.js | 8 +- codemirror/test/emacs_test.js | 22 +- codemirror/test/index.html | 4 +- codemirror/test/multi_test.js | 18 +- codemirror/test/search_test.js | 8 +- codemirror/test/sql-hint-test.js | 4 +- codemirror/test/sublime_test.js | 8 +- codemirror/test/test.js | 660 +++++++---- codemirror/test/vim_test.js | 271 +++-- 71 files changed, 2611 insertions(+), 1496 deletions(-) create mode 100644 codemirror/mode/sass/test.js create mode 100644 codemirror/src/input/movement.js diff --git a/codemirror/AUTHORS b/codemirror/AUTHORS index 866c78f..bc894e9 100644 --- a/codemirror/AUTHORS +++ b/codemirror/AUTHORS @@ -68,6 +68,7 @@ Apollo Zhu AQNOUCH Mohammed areos Arnab Bose +Arthur Müller as3boyan atelierbram AtomicPages LLC @@ -102,6 +103,7 @@ Brett Zamir Brian Grinstead Brian Sletten Bruce Mitchener +Bryan Massoth Caitlin Potter Calin Barbat callodacity @@ -180,6 +182,7 @@ Enam Mijbah Noor Eric Allam Erik Welander eustas +Fabien Dubosson Fabien O'Carroll Fabio Zendhi Nagao Faiza Alsaied @@ -205,6 +208,7 @@ Gary Sheng Gautam Mehta Gavin Douglas gekkoe +Geordie Hall geowarin Gerard Braad Gergely Hegykozi @@ -302,6 +306,7 @@ Jon Malmaud Jon Sangster Joost-Wim Boekesteijn Joseph Pecoraro +Josh Barnes Josh Cohen Josh Soref Joshua Newman @@ -324,6 +329,7 @@ Ken Newman ken restivo Ken Rockot Kevin Earls +Kevin Muret Kevin Sawicki Kevin Ushey Klaus Silveira @@ -510,6 +516,7 @@ Roberto Abdelkader Martínez Pérez robertop23 Robert Plummer Rrandom +Rrrandom Ruslan Osmanov Ryan Prior sabaca diff --git a/codemirror/CHANGELOG.md b/codemirror/CHANGELOG.md index 07ed029..588d031 100644 --- a/codemirror/CHANGELOG.md +++ b/codemirror/CHANGELOG.md @@ -1,3 +1,53 @@ +## 5.24.2 (2017-02-22) + +### Bug fixes + +[javascript mode](http://codemirror.net/mode/javascript/): Support computed class method names. + +[merge addon](http://codemirror.net/doc/manual.html#addon_merge): Improve aligning of unchanged code in the presence of marks and line widgets. + +## 5.24.0 (2017-02-20) + +### Bug fixes + +A cursor directly before a line-wrapping break is now drawn before or after the line break depending on which direction you arrived from. + +Visual cursor motion in line-wrapped right-to-left text should be much more correct. + +Fix bug in handling of read-only marked text. + +[shell mode](http://codemirror.net/mode/shell/): Properly tokenize nested parentheses. + +[python mode](http://codemirror.net/mode/python/): Support underscores in number literals. + +[sass mode](http://codemirror.net/mode/sass/): Uses the full list of CSS properties and keywords from the CSS mode, rather than defining its own incomplete subset. + +[css mode](http://codemirror.net/mode/css/): Expose `lineComment` property for LESS and SCSS dialects. Recognize vendor prefixes on pseudo-elements. + +[julia mode](http://codemirror.net/mode/julia/): Properly indent `elseif` lines. + +[markdown mode](http://codemirror.net/mode/markdown/): Properly recognize the end of fenced code blocks when inside other markup. + +[scala mode](http://codemirror.net/mode/clike/): Improve handling of operators containing #, @, and : chars. + +[xml mode](http://codemirror.net/mode/xml/): Allow dashes in HTML tag names. + +[javascript mode](http://codemirror.net/mode/javascript/): Improve parsing of async methods, TypeScript-style comma-separated superclass lists. + +[indent-fold addon](http://codemirror.net/demo/folding.html): Ignore comment lines. + +### New features + +Positions now support a `sticky` property which determines whether they should be associated with the character before (value `"before"`) or after (value `"after"`) them. + +[vim bindings](http://codemirror.net/mode/demo/vim.html): Make it possible to remove built-in bindings through the API. + +[comment addon](http://codemirror.net/doc/manual.html#addon_comment): Support a per-mode useInnerComments option to optionally suppress descending to the inner modes to get comment strings. + +### Breaking changes + +The [sass mode](http://codemirror.net/mode/sass/) now depends on the [css mode](http://codemirror.net/mode/css/). + ## 5.23.0 (2017-01-19) ### Bug fixes diff --git a/codemirror/LICENSE b/codemirror/LICENSE index 1bca6bf..ff7db4b 100644 --- a/codemirror/LICENSE +++ b/codemirror/LICENSE @@ -1,3 +1,5 @@ +MIT License + Copyright (C) 2017 by Marijn Haverbeke and others Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/codemirror/addon/comment/comment.js b/codemirror/addon/comment/comment.js index d71cf43..1c98787 100644 --- a/codemirror/addon/comment/comment.js +++ b/codemirror/addon/comment/comment.js @@ -49,9 +49,14 @@ return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"`]/.test(line) } + function getMode(cm, pos) { + var mode = cm.getMode() + return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos) + } + CodeMirror.defineExtension("lineComment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); + var self = this, mode = getMode(self, from); var firstLine = self.getLine(from.line); if (firstLine == null || probablyInsideString(self, from, firstLine)) return; @@ -95,7 +100,7 @@ CodeMirror.defineExtension("blockComment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); + var self = this, mode = getMode(self, from); var startString = options.blockCommentStart || mode.blockCommentStart; var endString = options.blockCommentEnd || mode.blockCommentEnd; if (!startString || !endString) { @@ -129,7 +134,7 @@ CodeMirror.defineExtension("uncomment", function(from, to, options) { if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); + var self = this, mode = getMode(self, from); var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end); // Try finding line comments diff --git a/codemirror/addon/edit/closebrackets.js b/codemirror/addon/edit/closebrackets.js index 62b99c1..01fdd96 100644 --- a/codemirror/addon/edit/closebrackets.js +++ b/codemirror/addon/edit/closebrackets.js @@ -185,7 +185,7 @@ function enteringString(cm, pos, ch) { var line = cm.getLine(pos.line); var token = cm.getTokenAt(pos); - if (/\bstring2?\b/.test(token.type)) return false; + if (/\bstring2?\b/.test(token.type) || stringStartsAfter(cm, pos)) return false; var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4); stream.pos = stream.start = token.start; for (;;) { diff --git a/codemirror/addon/edit/continuelist.js b/codemirror/addon/edit/continuelist.js index 6574ea3..5a84590 100644 --- a/codemirror/addon/edit/continuelist.js +++ b/codemirror/addon/edit/continuelist.js @@ -30,7 +30,7 @@ return; } if (emptyListRE.test(line)) { - cm.replaceRange("", { + if (!/>\s*$/.test(line)) cm.replaceRange("", { line: pos.line, ch: 0 }, { line: pos.line, ch: pos.ch + 1 diff --git a/codemirror/addon/fold/indent-fold.js b/codemirror/addon/fold/indent-fold.js index e29f15e..743150c 100644 --- a/codemirror/addon/fold/indent-fold.js +++ b/codemirror/addon/fold/indent-fold.js @@ -11,32 +11,36 @@ })(function(CodeMirror) { "use strict"; +function lineIndent(cm, lineNo) { + var text = cm.getLine(lineNo) + var spaceTo = text.search(/\S/) + if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1)))) + return -1 + return CodeMirror.countColumn(text, null, cm.getOption("tabSize")) +} + ! CodeMirror.registerHelper("fold", "indent", function(cm, start) { - var tabSize = cm.getOption("tabSize"), firstLine = cm.getLine(start.line); - if (!/\S/.test(firstLine)) return; - var getIndent = function(line) { - return CodeMirror.countColumn(line, null, tabSize); - }; - var myIndent = getIndent(firstLine); - var lastLineInFold = null; + var myIndent = lineIndent(cm, start.line) + if (myIndent < 0) return + var lastLineInFold = null + // Go through lines until we find a line that definitely doesn't belong in // the block we're folding, or to the end. for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) { - var curLine = cm.getLine(i); - var curIndent = getIndent(curLine); - if (curIndent > myIndent) { + var indent = lineIndent(cm, i) + if (indent == -1) { + } else if (indent > myIndent) { // Lines with a greater indent are considered part of the block. lastLineInFold = i; - } else if (!/\S/.test(curLine)) { - // Empty lines might be breaks within the block we're trying to fold. } else { - // A non-empty line at an indent equal to or less than ours marks the - // start of another block. + // If this line has non-space, non-comment content, and is + // indented less or equal to the start line, it is the start of + // another block. break; } } if (lastLineInFold) return { - from: CodeMirror.Pos(start.line, firstLine.length), + from: CodeMirror.Pos(start.line, cm.getLine(start.line).length), to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length) }; }); diff --git a/codemirror/addon/merge/merge.js b/codemirror/addon/merge/merge.js index a8132b6..66d160f 100644 --- a/codemirror/addon/merge/merge.js +++ b/codemirror/addon/merge/merge.js @@ -37,8 +37,13 @@ constructor: DiffView, init: function(pane, orig, options) { this.edit = this.mv.edit; - (this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this); + ;(this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this); this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options))); + if (this.mv.options.connect == "align") { + if (!this.edit.state.trackAlignable) this.edit.state.trackAlignable = new TrackAlignable(this.edit) + this.orig.state.trackAlignable = new TrackAlignable(this.orig) + } + this.orig.state.diffViews = [this]; var classLocation = options.chunkClassLocation || "background"; if (Object.prototype.toString.call(classLocation) != "[object Array]") classLocation = [classLocation] @@ -47,6 +52,7 @@ this.diff = getDiff(asString(orig), asString(options.value)); this.chunks = getChunks(this.diff); this.diffOutOfDate = this.dealigned = false; + this.needsScrollSync = null this.showDifferences = options.showDifferences !== false; }, @@ -97,6 +103,7 @@ if (dv.mv.options.connect == "align") alignChunks(dv); makeConnections(dv); + if (dv.needsScrollSync != null) syncScroll(dv, dv.needsScrollSync) updating = false; } @@ -127,10 +134,10 @@ dv.orig.on("change", change); dv.edit.on("swapDoc", swapDoc); dv.orig.on("swapDoc", swapDoc); - dv.edit.on("markerAdded", setDealign); - dv.edit.on("markerCleared", setDealign); - dv.orig.on("markerAdded", setDealign); - dv.orig.on("markerCleared", setDealign); + if (dv.mv.options.connect == "align") { + CodeMirror.on(dv.edit.state.trackAlignable, "realign", setDealign) + CodeMirror.on(dv.orig.state.trackAlignable, "realign", setDealign) + } dv.edit.on("viewportChange", function() { set(false); }); dv.orig.on("viewportChange", function() { set(false); }); update(); @@ -139,23 +146,27 @@ function registerScroll(dv) { dv.edit.on("scroll", function() { - syncScroll(dv, DIFF_INSERT) && makeConnections(dv); + syncScroll(dv, true) && makeConnections(dv); }); dv.orig.on("scroll", function() { - syncScroll(dv, DIFF_DELETE) && makeConnections(dv); + syncScroll(dv, false) && makeConnections(dv); }); } - function syncScroll(dv, type) { + function syncScroll(dv, toOrig) { // Change handler will do a refresh after a timeout when diff is out of date - if (dv.diffOutOfDate) return false; + if (dv.diffOutOfDate) { + if (dv.lockScroll && dv.needsScrollSync == null) dv.needsScrollSync = toOrig + return false + } + dv.needsScrollSync = null if (!dv.lockScroll) return true; var editor, other, now = +new Date; - if (type == DIFF_INSERT) { editor = dv.edit; other = dv.orig; } + if (toOrig) { editor = dv.edit; other = dv.orig; } else { editor = dv.orig; other = dv.edit; } // Don't take action if the position of this editor was recently set // (to prevent feedback loops) - if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 50 > now) return false; + if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 250 > now) return false; var sInfo = editor.getScrollInfo(); if (dv.mv.options.connect == "align") { @@ -163,9 +174,9 @@ } else { var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen; var mid = editor.lineAtHeight(midY, "local"); - var around = chunkBoundariesAround(dv.chunks, 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 around = chunkBoundariesAround(dv.chunks, mid, toOrig); + var off = getOffsets(editor, toOrig ? around.edit : around.orig); + var offOther = getOffsets(other, toOrig ? around.orig : around.edit); var ratio = (midY - off.top) / (off.bot - off.top); var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top); @@ -271,7 +282,7 @@ } } - var chunkStart = 0; + var chunkStart = 0, pending = false; for (var i = 0; i < diff.length; ++i) { var part = diff[i], tp = part[0], str = part[1]; if (tp == DIFF_EQUAL) { @@ -279,10 +290,11 @@ moveOver(pos, str); var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0); if (cleanTo > cleanFrom) { - if (i) markChunk(chunkStart, cleanFrom); + if (pending) { markChunk(chunkStart, cleanFrom); pending = false } chunkStart = cleanTo; } } else { + pending = true if (tp == type) { var end = moveOver(pos, str, true); var a = posMax(top, pos), b = posMin(bot, end); @@ -292,7 +304,7 @@ } } } - if (chunkStart <= pos.line) markChunk(chunkStart, pos.line + 1); + if (pending) markChunk(chunkStart, pos.line + 1); } // Updating the gap between editor and original @@ -331,24 +343,73 @@ return origStart + (editLine - editStart); } - function findAlignedLines(dv, other) { - var linesToAlign = []; - for (var i = 0; i < dv.chunks.length; i++) { - var chunk = dv.chunks[i]; - linesToAlign.push([chunk.origTo, chunk.editTo, other ? getMatchingOrigLine(chunk.editTo, other.chunks) : null]); - } - if (other) { - chunkLoop: for (var i = 0; i < other.chunks.length; i++) { - var chunk = other.chunks[i]; - for (var j = 0; j < linesToAlign.length; j++) { - var diff = linesToAlign[j][1] - chunk.editTo; - if (diff == 0) continue chunkLoop - if (diff > 0) break; - } - linesToAlign.splice(j, 0, [getMatchingOrigLine(chunk.editTo, dv.chunks), chunk.editTo, chunk.origTo]); + // Combines information about chunks and widgets/markers to return + // an array of lines, in a single editor, that probably need to be + // aligned with their counterparts in the editor next to it. + function alignableFor(cm, chunks, isOrig) { + var tracker = cm.state.trackAlignable + var start = cm.firstLine(), trackI = 0 + var result = [] + for (var i = 0;; i++) { + var chunk = chunks[i] + var chunkStart = !chunk ? cm.lastLine() + 1 : isOrig ? chunk.origFrom : chunk.editFrom + for (; trackI < tracker.alignable.length; trackI += 2) { + var n = tracker.alignable[trackI] + 1 + if (n <= start) continue + if (n < chunkStart) result.push(n) + else break + } + if (!chunk) break + result.push(start = isOrig ? chunk.origTo : chunk.editTo) + } + return result + } + + // Given information about alignable lines in two editors, fill in + // the result (an array of three-element arrays) to reflect the + // lines that need to be aligned with each other. + function mergeAlignable(result, origAlignable, chunks, setIndex) { + var rI = 0, origI = 0, chunkI = 0, diff = 0 + for (;; rI++) { + var nextR = result[rI], nextO = origAlignable[origI] + if (!nextR && nextO == null) break + + var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO + while (chunkI < chunks.length) { + var chunk = chunks[chunkI] + if (chunk.editTo > rLine) break + diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom) + chunkI++ + } + if (rLine == oLine - diff) { + nextR[setIndex] = oLine + origI++ + } else if (rLine < oLine - diff) { + nextR[setIndex] = rLine + diff + } else { + var record = [oLine - diff, null, null] + record[setIndex] = oLine + result.splice(rI, 0, record) + origI++ } } - return linesToAlign; + } + + function findAlignedLines(dv, other) { + var alignable = alignableFor(dv.edit, dv.chunks, false), result = [] + if (other) for (var i = 0, j = 0; i < other.chunks.length; i++) { + var n = other.chunks[i].editTo + while (j < alignable.length && alignable[j] < n) j++ + if (j == alignable.length || alignable[j] != n) alignable.splice(j++, 0, n) + } + for (var i = 0; i < alignable.length; i++) + result.push([alignable[i], null, null]) + + mergeAlignable(result, alignableFor(dv.orig, dv.chunks, true), dv.chunks, 1) + if (other) + mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2) + + return result } function alignChunks(dv, force) { @@ -371,7 +432,7 @@ aligners[i].clear(); aligners.length = 0; - var cm = [dv.orig, dv.edit], scroll = []; + var cm = [dv.edit, dv.orig], scroll = []; if (other) cm.push(other.orig); for (var i = 0; i < cm.length; i++) scroll.push(cm[i].getScrollInfo().top); @@ -406,7 +467,7 @@ var elt = document.createElement("div"); elt.className = "CodeMirror-merge-spacer"; elt.style.height = size + "px"; elt.style.minWidth = "1px"; - return cm.addLineWidget(line, elt, {height: size, above: above}); + return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true}); } function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) { @@ -448,9 +509,15 @@ function copyChunk(dv, to, from, chunk) { if (dv.diffOutOfDate) return; - var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0) var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0) - to.replaceRange(from.getRange(origStart, Pos(chunk.origTo, 0)), editStart, Pos(chunk.editTo, 0)) + var origEnd = Pos(chunk.origTo, 0) + var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0) + var editEnd = Pos(chunk.editTo, 0) + var handler = dv.mv.options.revertChunk + if (handler) + handler(dv.mv, from, origStart, origEnd, to, editStart, editEnd) + else + to.replaceRange(from.getRange(origStart, origEnd), editStart, editEnd) } // Merge view, containing 0, 1, or 2 diff views. @@ -614,10 +681,10 @@ function endOfLineClean(diff, i) { if (i == diff.length - 1) return true; var next = diff[i + 1][1]; - if (next.length == 1 || next.charCodeAt(0) != 10) return false; + if ((next.length == 1 && i < diff.length - 2) || next.charCodeAt(0) != 10) return false; if (i == diff.length - 2) return true; next = diff[i + 2][1]; - return next.length > 1 && next.charCodeAt(0) == 10; + return (next.length > 1 || i == diff.length - 3) && next.charCodeAt(0) == 10; } function startOfLineClean(diff, i) { @@ -751,6 +818,126 @@ return out; } + // Tracks collapsed markers and line widgets, in order to be able to + // accurately align the content of two editors. + + var F_WIDGET = 1, F_WIDGET_BELOW = 2, F_MARKER = 4 + + function TrackAlignable(cm) { + this.cm = cm + this.alignable = [] + var self = this + cm.on("markerAdded", function(_, marker) { + if (!marker.collapsed) return + var found = marker.find(1) + if (found != null) self.set(found.line, F_MARKER) + }) + cm.on("markerCleared", function(_, marker, _min, max) { + if (max != null && marker.collapsed) + self.check(max, F_MARKER, self.hasMarker) + }) + cm.on("markerChanged", this.signal.bind(this)) + cm.on("lineWidgetAdded", function(_, widget, lineNo) { + if (widget.mergeSpacer) return + if (widget.above) self.set(lineNo - 1, F_WIDGET_BELOW) + else self.set(lineNo, F_WIDGET) + }) + cm.on("lineWidgetCleared", function(_, widget, lineNo) { + if (widget.mergeSpacer) return + if (widget.above) self.check(lineNo - 1, F_WIDGET_BELOW, self.hasWidgetBelow) + else self.check(lineNo, F_WIDGET, self.hasWidget) + }) + cm.on("lineWidgetChanged", this.signal.bind(this)) + cm.on("change", function(_, change) { + var start = change.from.line, nBefore = change.to.line - change.from.line + var nAfter = change.text.length - 1, end = start + nAfter + if (nBefore || nAfter) self.map(start, nBefore, nAfter) + self.check(end, F_MARKER, self.hasMarker) + if (nBefore || nAfter) self.check(change.from.line, F_MARKER, self.hasMarker) + }) + } + + TrackAlignable.prototype = { + signal: function() { + CodeMirror.signal(this, "realign") + }, + + set: function(n, flags) { + var pos = -1 + for (; pos < this.alignable.length; pos += 2) { + var diff = this.alignable[pos] - n + if (diff == 0) { + if ((this.alignable[pos + 1] & flags) == flags) return + this.alignable[pos + 1] |= flags + this.signal() + return + } + if (diff > 0) break + } + this.signal() + this.alignable.splice(pos, 0, n, flags) + }, + + find: function(n) { + for (var i = 0; i < this.alignable.length; i += 2) + if (this.alignable[i] == n) return i + return -1 + }, + + check: function(n, flag, pred) { + var found = this.find(n) + if (found == -1 || !(this.alignable[found + 1] & flag)) return + if (!pred.call(this, n)) { + this.signal() + var flags = this.alignable[found + 1] & ~flag + if (flags) this.alignable[found + 1] = flags + else this.alignable.splice(found, 2) + } + }, + + hasMarker: function(n) { + var handle = this.cm.getLineHandle(n) + if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++) + if (handle.markedSpans[i].mark.collapsed && handle.markedSpans[i].to != null) + return true + return false + }, + + hasWidget: function(n) { + var handle = this.cm.getLineHandle(n) + if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++) + if (!handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true + return false + }, + + hasWidgetBelow: function(n) { + if (n == this.cm.lastLine()) return false + var handle = this.cm.getLineHandle(n + 1) + if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++) + if (handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true + return false + }, + + map: function(from, nBefore, nAfter) { + var diff = nAfter - nBefore, to = from + nBefore, widgetFrom = -1, widgetTo = -1 + for (var i = 0; i < this.alignable.length; i += 2) { + var n = this.alignable[i] + if (n == from && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetFrom = i + if (n == to && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetTo = i + if (n <= from) continue + else if (n < to) this.alignable.splice(i--, 2) + else this.alignable[i] += diff + } + if (widgetFrom > -1) { + var flags = this.alignable[widgetFrom + 1] + if (flags == F_WIDGET_BELOW) this.alignable.splice(widgetFrom, 2) + else this.alignable[widgetFrom + 1] = flags & ~F_WIDGET_BELOW + } + if (widgetTo > -1 && nAfter) + this.set(from + nAfter, F_WIDGET_BELOW) + } + } + function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; } function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; } function posEq(a, b) { return a.line == b.line && a.ch == b.ch; } diff --git a/codemirror/addon/scroll/annotatescrollbar.js b/codemirror/addon/scroll/annotatescrollbar.js index 5e748e8..f2276fc 100644 --- a/codemirror/addon/scroll/annotatescrollbar.js +++ b/codemirror/addon/scroll/annotatescrollbar.js @@ -77,17 +77,21 @@ curLine = pos.line; curLineObj = cm.getLineHandle(curLine); } - if (wrapping && curLineObj.height > singleLineH) + if ((curLineObj.widgets && curLineObj.widgets.length) || + (wrapping && curLineObj.height > singleLineH)) return cm.charCoords(pos, "local")[top ? "top" : "bottom"]; var topY = cm.heightAtLine(curLineObj, "local"); return topY + (top ? 0 : curLineObj.height); } + var lastLine = cm.lastLine() if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) { var ann = anns[i]; + if (ann.to.line > lastLine) continue; var top = nextTop || getY(ann.from, true) * hScale; var bottom = getY(ann.to, false) * hScale; while (i < anns.length - 1) { + if (anns[i + 1].to.line > lastLine) break; nextTop = getY(anns[i + 1].from, true) * hScale; if (nextTop > bottom + .9) break; ann = anns[++i]; diff --git a/codemirror/demo/folding.html b/codemirror/demo/folding.html index 81cbf98..06d5830 100644 --- a/codemirror/demo/folding.html +++ b/codemirror/demo/folding.html @@ -12,10 +12,12 @@ + +