From d8e2dcb2e8701c7299ad89025f3d23155724c20a Mon Sep 17 00:00:00 2001 From: tophf Date: Sun, 22 Sep 2019 15:24:59 +0300 Subject: [PATCH] find @-moz-doc sections faster in the editor --- edit/applies-to-line-widget.js | 100 ++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/edit/applies-to-line-widget.js b/edit/applies-to-line-widget.js index 1d41afbd..17471c2b 100644 --- a/edit/applies-to-line-widget.js +++ b/edit/applies-to-line-widget.js @@ -5,6 +5,7 @@ function createAppliesToLineWidget(cm) { const THROTTLE_DELAY = 400; + const RX_SPACE = /(?:\s+|\s*\/\*)+/y; let TPL, EVENTS, CLICK_ROUTE; let widgets = []; let fromLine, toLine, actualStyle; @@ -294,21 +295,15 @@ function createAppliesToLineWidget(cm) { const toPos = {line: widgets[j] ? widgets[j].line.lineNo() : toLine + 1, ch: 0}; // calc index->pos lookup table - let line = 0; let index = 0; - let fromIndex, toIndex; - const lineIndexes = [index]; - cm.doc.iter(({text}) => { - fromIndex = line === fromPos.line ? index : fromIndex; + const lineIndexes = [0]; + cm.doc.iter(0, toPos.line + 1, ({text}) => { lineIndexes.push((index += text.length + 1)); - line++; - toIndex = line >= toPos.line ? index : toIndex; - return toIndex; }); // splice i = Math.max(0, i); - widgets.splice(i, 0, ...createWidgets(fromIndex, toIndex, widgets.splice(i, j - i), lineIndexes)); + widgets.splice(i, 0, ...createWidgets(fromPos, toPos, widgets.splice(i, j - i), lineIndexes)); fromLine = null; toLine = null; @@ -317,7 +312,7 @@ function createAppliesToLineWidget(cm) { function *createWidgets(start, end, removed, lineIndexes) { let i = 0; let itemHeight; - for (const section of findAppliesTo(start, end)) { + for (const section of findAppliesTo(start, end, lineIndexes)) { let removedWidget = removed[i]; while (removedWidget && removedWidget.line.lineNo() < section.pos.line) { clearWidget(removed[i]); @@ -488,40 +483,77 @@ function createAppliesToLineWidget(cm) { }; } - function *findAppliesTo(posStart, posEnd) { - const text = cm.getValue(); - const re = /^[\t ]*@-moz-document[\s\n]+/gm; - const applyRe = new RegExp([ - /(?:\/\*[\s\S]*?(?:\*\/\s*|$))*/, - /(url|url-prefix|domain|regexp)/, - /\(((['"])(?:\\\\|\\\n|\\\3|[^\n])*?\3|[^)\n]*)\)\s*(,\s*)?/, - ].map(rx => rx.source).join(''), 'giy'); - let match; - re.lastIndex = posStart; - while ((match = re.exec(text))) { - if (match.index >= posEnd) { - return; - } + function *findAppliesTo(posStart, posEnd, lineIndexes) { + const funcRe = /^(url|url-prefix|domain|regexp)$/i; + let pos; + const eatToken = sticky => { + if (!sticky) skipSpace(pos, posEnd); + pos.ch++; + const token = cm.getTokenAt(pos); + pos.ch = token.end; + return CodeMirror.cmpPos(pos, posEnd) <= 0 ? token : {}; + }; + const docCur = cm.getSearchCursor('@-moz-document', posStart); + while (docCur.findNext() && + CodeMirror.cmpPos(docCur.pos.to, posEnd) <= 0) { + if (!/\bdef\b/.test(cm.getTokenTypeAt(docCur.pos.from))) continue; const applies = []; - let m; - applyRe.lastIndex = re.lastIndex; - while ((m = applyRe.exec(text))) { + pos = docCur.pos.to; + do { + skipSpace(pos, posEnd); + const funcIndex = lineIndexes[pos.line] + pos.ch; + const func = eatToken().string; + // no space allowed before the opening parenthesis + if (!funcRe.test(func) || eatToken(true).string !== '(') break; + const url = eatToken(); + if (url.type !== 'string' || eatToken().string !== ')') break; + const unquotedUrl = unquote(url.string); const apply = createApply( - m.index, - m[1], - unquote(m[2]), - unquote(m[2]) !== m[2] + funcIndex, + func, + unquotedUrl, + unquotedUrl !== url.string ); applies.push(apply); - re.lastIndex = applyRe.lastIndex; - } + } while (eatToken().string === ','); yield { - pos: cm.posFromIndex(match.index), + pos: docCur.pos.from, applies }; } } + function skipSpace(pos, posEnd) { + let {ch, line} = pos; + let lookForEnd; + line--; + cm.doc.iter(pos.line, posEnd.line + 1, ({text}) => { + line++; + while (true) { + if (lookForEnd) { + ch = text.indexOf('*/', ch) + 1; + if (!ch) { + return; + } + ch++; + lookForEnd = false; + } + RX_SPACE.lastIndex = ch; + const m = RX_SPACE.exec(text); + if (!m) { + return true; + } + ch += m[0].length; + lookForEnd = m[0].includes('/*'); + if (ch < text.length && !lookForEnd) { + return true; + } + } + }); + pos.line = line; + pos.ch = ch; + } + function unquote(s) { const first = s.charAt(0); return (first === '"' || first === "'") && s.endsWith(first) ? s.slice(1, -1) : s;