diff --git a/edit.html b/edit.html
index 86cd5e57..2f20f8ee 100644
--- a/edit.html
+++ b/edit.html
@@ -37,7 +37,7 @@
-
+
@@ -61,6 +61,7 @@
+
diff --git a/edit/match-highlighter-helper.js b/edit/match-highlighter-helper.js
new file mode 100644
index 00000000..6036d8ce
--- /dev/null
+++ b/edit/match-highlighter-helper.js
@@ -0,0 +1,99 @@
+/* global CodeMirror */
+'use strict';
+
+(() => {
+ const HL_APPROVED = 'cm-matchhighlight-approved';
+ const originalAddOverlay = CodeMirror.prototype.addOverlay;
+ const originalRemoveOverlay = CodeMirror.prototype.removeOverlay;
+ CodeMirror.prototype.addOverlay = addOverlay;
+ CodeMirror.prototype.removeOverlay = removeOverlay;
+ return;
+
+ function shouldIntercept(overlay) {
+ const hlState = this.state.matchHighlighter || {};
+ return overlay === hlState.overlay && (hlState.options || {}).showToken;
+ }
+
+ function addOverlay() {
+ return shouldIntercept.apply(this, arguments) &&
+ addOverlayForHighlighter.apply(this, arguments) ||
+ originalAddOverlay.apply(this, arguments);
+ }
+
+ function removeOverlay() {
+ return shouldIntercept.apply(this, arguments) &&
+ removeOverlayForHighlighter.apply(this, arguments) ||
+ originalRemoveOverlay.apply(this, arguments);
+ }
+
+ function addOverlayForHighlighter(overlay) {
+ const state = this.state.matchHighlighter || {};
+ const helper = state.stylusMHLHelper || {};
+ if (helper.matchesonscroll) {
+ // restore the original addon's unwanted removeOverlay effects
+ // (in case the token under cursor hasn't changed)
+ state.matchesonscroll = helper.matchesonscroll;
+ state.overlay = helper.overlay;
+ helper.matchesonscroll = null;
+ helper.overlay = null;
+ return true;
+ }
+ if (overlay.token !== tokenHook) {
+ overlay.stylusMHLHelper = {
+ token: overlay.token,
+ occurrences: 0,
+ };
+ overlay.token = tokenHook;
+ }
+ clearTimeout(helper.hookTimer);
+ }
+
+ function tokenHook(stream) {
+ const style = this.stylusMHLHelper.token.call(this, stream);
+ if (style !== 'matchhighlight') {
+ return style;
+ }
+ const num = ++this.stylusMHLHelper.occurrences;
+ if (num === 1) {
+ stream.lineOracle.doc.cm.display.wrapper.classList.remove(HL_APPROVED);
+ } else if (num === 2) {
+ stream.lineOracle.doc.cm.display.wrapper.classList.add(HL_APPROVED);
+ }
+ return style;
+ }
+
+ function removeOverlayForHighlighter() {
+ const state = this.state.matchHighlighter || {};
+ const {query} = state.matchesonscroll || {};
+ if (!query) {
+ return;
+ }
+ const {line, ch} = this.getCursor();
+ const rx = query instanceof RegExp ? query : new RegExp(`\\b${query}\\b`);
+ const start = Math.max(0, ch - rx.source.length + 4 + 1);
+ const end = ch + rx.source.length - 4;
+ const area = this.getLine(line).substring(start, end);
+ const startInArea = (area.match(rx) || {}).index;
+ if (start + startInArea <= ch) {
+ // same token on cursor => prevent the highlighter from rerunning
+ state.stylusMHLHelper = {
+ overlay: state.overlay,
+ matchesonscroll: state.matchesonscroll,
+ hookTimer: setTimeout(removeOverlayIfExpired, 0, this, state),
+ };
+ state.matchesonscroll = null;
+ return true;
+ }
+ }
+
+ function removeOverlayIfExpired(self, state) {
+ const {overlay, matchesonscroll} = state.stylusMHLHelper || {};
+ if (overlay) {
+ originalRemoveOverlay.call(self, overlay);
+ }
+ if (matchesonscroll) {
+ matchesonscroll.clear();
+ }
+ state.stylusMHLHelper = null;
+ }
+})();
diff --git a/install-usercss.html b/install-usercss.html
index 1b40c75e..208d3c4d 100644
--- a/install-usercss.html
+++ b/install-usercss.html
@@ -23,6 +23,7 @@
+
@@ -32,7 +33,7 @@
-
+
diff --git a/vendor-overwrites/codemirror/addon/search/match-highlighter.js b/vendor/codemirror/addon/search/match-highlighter.js
similarity index 81%
rename from vendor-overwrites/codemirror/addon/search/match-highlighter.js
rename to vendor/codemirror/addon/search/match-highlighter.js
index f1b49d92..73ba0e05 100644
--- a/vendor-overwrites/codemirror/addon/search/match-highlighter.js
+++ b/vendor/codemirror/addon/search/match-highlighter.js
@@ -19,12 +19,6 @@
// highlighting the matches. If annotateScrollbar is enabled, the occurences
// will be highlighted on the scrollbar via the matchesonscrollbar addon.
-/* STYLUS: hack start (part 1) */
-/* eslint curly: 1, brace-style:1, strict: 0, quotes: 0, semi: 1, indent: 1 */
-/* eslint no-var: 0, block-scoped-var: 0, no-redeclare: 0, no-unused-expressions: 1 */
-/* global CodeMirror, require, define */
-/* STYLUS: hack end (part 1) */
-
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
@@ -94,9 +88,7 @@
function addOverlay(cm, query, hasBoundary, style) {
var state = cm.state.matchHighlighter;
- /* STYLUS: hack start (part 2) */
- cm.addOverlay(state.overlay = makeOverlay(cm, query, hasBoundary, style));
- /* STYLUS: hack end (part 2) */
+ cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
var searchFor = hasBoundary ? new RegExp("\\b" + query + "\\b") : query;
state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
@@ -119,24 +111,16 @@
function highlightMatches(cm) {
cm.operation(function() {
var state = cm.state.matchHighlighter;
+ removeOverlay(cm);
if (!cm.somethingSelected() && state.options.showToken) {
var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
while (start && re.test(line.charAt(start - 1))) --start;
while (end < line.length && re.test(line.charAt(end))) ++end;
- /* STYLUS: hack start */
- const token = line.slice(start, end);
- if (token !== state.lastToken) {
- state.lastToken = token;
- removeOverlay(cm);
- if (token) {
- addOverlay(cm, token, re, state.options.style);
- }
- }
+ if (start < end)
+ addOverlay(cm, line.slice(start, end), re, state.options.style);
return;
}
- removeOverlay(cm);
- /* STYLUS: hack end */
var from = cm.getCursor("from"), to = cm.getCursor("to");
if (from.line != to.line) return;
if (state.options.wordsOnly && !isWord(cm, from, to)) return;
@@ -169,28 +153,11 @@
(stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
}
- function makeOverlay(cm, query, hasBoundary, style) {
- /* STYLUS: hack start (part 3) */
- const approvedClassName = `cm-${style}-approved`;
- let timer;
- let occurrences = 0;
+ function makeOverlay(query, hasBoundary, style) {
return {token: function(stream) {
- clearTimeout(timer);
- timer = setTimeout(() => {
- occurrences = 0;
- timer = null;
- });
if (stream.match(query) &&
- (!hasBoundary || boundariesAround(stream, hasBoundary))) {
- occurrences++;
- if (occurrences == 1) {
- cm.display.wrapper.classList.remove(approvedClassName);
- } else if (occurrences == 2) {
- cm.display.wrapper.classList.add(approvedClassName);
- }
+ (!hasBoundary || boundariesAround(stream, hasBoundary)))
return style;
- }
- /* STYLUS: hack end (part 3) */
stream.next();
stream.skipTo(query.charAt(0)) || stream.skipToEnd();
}};