restore the original match-highlighter and monkeypatch it

This commit is contained in:
tophf 2017-11-23 05:25:59 +03:00
parent c1c61ed590
commit a30ef3ed14
4 changed files with 109 additions and 41 deletions

View File

@ -37,7 +37,7 @@
<link rel="stylesheet" href="vendor/codemirror/addon/search/matchesonscrollbar.css">
<script src="vendor/codemirror/addon/scroll/annotatescrollbar.js"></script>
<script src="vendor/codemirror/addon/search/matchesonscrollbar.js"></script>
<script src="vendor-overwrites/codemirror/addon/search/match-highlighter.js"></script>
<script src="vendor/codemirror/addon/search/match-highlighter.js"></script>
<script src="vendor/codemirror/addon/dialog/dialog.js"></script>
<script src="vendor/codemirror/addon/search/searchcursor.js"></script>
<script src="vendor/codemirror/addon/search/search.js"></script>
@ -61,6 +61,7 @@
<script src="vendor/codemirror/keymap/emacs.js"></script>
<script src="vendor/codemirror/keymap/vim.js"></script>
<script src="/edit/match-highlighter-helper.js"></script>
<script src="/edit/codemirror-default.js"></script>
<link rel="stylesheet" href="/edit/codemirror-default.css">
<link id="cm-theme" rel="stylesheet">

View File

@ -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;
}
})();

View File

@ -23,6 +23,7 @@
<script src="/vendor/codemirror/keymap/emacs.js"></script>
<script src="/vendor/codemirror/keymap/vim.js"></script>
<script src="/edit/match-highlighter-helper.js"></script>
<script src="/edit/codemirror-default.js"></script>
<link rel="stylesheet" href="/edit/codemirror-default.css">
@ -32,7 +33,7 @@
<link rel="stylesheet" href="/vendor/codemirror/addon/search/matchesonscrollbar.css">
<script src="/vendor/codemirror/addon/scroll/annotatescrollbar.js"></script>
<script src="/vendor/codemirror/addon/search/matchesonscrollbar.js"></script>
<script src="/vendor-overwrites/codemirror/addon/search/match-highlighter.js"></script>
<script src="/vendor/codemirror/addon/search/match-highlighter.js"></script>
<script src="/vendor/codemirror/addon/dialog/dialog.js"></script>
<script src="/vendor/codemirror/addon/search/searchcursor.js"></script>
<script src="/vendor/codemirror/addon/search/search.js"></script>

View File

@ -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();
}};