Merge branch 'master' of https://github.com/openstyles/stylus into dev-sync

This commit is contained in:
eight 2019-10-30 02:00:22 +08:00
commit 37ebcf0593
7 changed files with 148 additions and 42 deletions

View File

@ -1,10 +1,11 @@
/* global regExpTester debounce messageBox CodeMirror template colorMimicry msg /* global regExpTester debounce messageBox CodeMirror template colorMimicry msg
$ $create t prefs tryCatch */ $ $create t prefs tryCatch deepEqual */
/* exported createAppliesToLineWidget */ /* exported createAppliesToLineWidget */
'use strict'; 'use strict';
function createAppliesToLineWidget(cm) { function createAppliesToLineWidget(cm) {
const THROTTLE_DELAY = 400; const THROTTLE_DELAY = 400;
const RX_SPACE = /(?:\s+|\/\*)+/y;
let TPL, EVENTS, CLICK_ROUTE; let TPL, EVENTS, CLICK_ROUTE;
let widgets = []; let widgets = [];
let fromLine, toLine, actualStyle; let fromLine, toLine, actualStyle;
@ -172,6 +173,10 @@ function createAppliesToLineWidget(cm) {
} }
function onRuntimeMessage(msg) { function onRuntimeMessage(msg) {
if (msg.reason === 'editPreview' && !$(`#stylus-${msg.style.id}`)) {
// no style element with this id means the style doesn't apply to the editor URL
return;
}
if (msg.style || msg.styles || if (msg.style || msg.styles ||
msg.prefs && 'disableAll' in msg.prefs || msg.prefs && 'disableAll' in msg.prefs ||
msg.method === 'styleDeleted') { msg.method === 'styleDeleted') {
@ -294,21 +299,15 @@ function createAppliesToLineWidget(cm) {
const toPos = {line: widgets[j] ? widgets[j].line.lineNo() : toLine + 1, ch: 0}; const toPos = {line: widgets[j] ? widgets[j].line.lineNo() : toLine + 1, ch: 0};
// calc index->pos lookup table // calc index->pos lookup table
let line = 0;
let index = 0; let index = 0;
let fromIndex, toIndex; const lineIndexes = [0];
const lineIndexes = [index]; cm.doc.iter(0, toPos.line + 1, ({text}) => {
cm.doc.iter(({text}) => {
fromIndex = line === fromPos.line ? index : fromIndex;
lineIndexes.push((index += text.length + 1)); lineIndexes.push((index += text.length + 1));
line++;
toIndex = line >= toPos.line ? index : toIndex;
return toIndex;
}); });
// splice // splice
i = Math.max(0, i); 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; fromLine = null;
toLine = null; toLine = null;
@ -317,12 +316,17 @@ function createAppliesToLineWidget(cm) {
function *createWidgets(start, end, removed, lineIndexes) { function *createWidgets(start, end, removed, lineIndexes) {
let i = 0; let i = 0;
let itemHeight; let itemHeight;
for (const section of findAppliesTo(start, end)) { for (const section of findAppliesTo(start, end, lineIndexes)) {
let removedWidget = removed[i]; let removedWidget = removed[i];
while (removedWidget && removedWidget.line.lineNo() < section.pos.line) { while (removedWidget && removedWidget.line.lineNo() < section.pos.line) {
clearWidget(removed[i]); clearWidget(removed[i]);
removedWidget = removed[++i]; removedWidget = removed[++i];
} }
if (removedWidget && deepEqual(removedWidget.node.__applies, section.applies, ['mark'])) {
yield removedWidget;
i++;
continue;
}
for (const a of section.applies) { for (const a of section.applies) {
setupApplyMarkers(a, lineIndexes); setupApplyMarkers(a, lineIndexes);
} }
@ -488,40 +492,84 @@ function createAppliesToLineWidget(cm) {
}; };
} }
function *findAppliesTo(posStart, posEnd) { function *findAppliesTo(posStart, posEnd, lineIndexes) {
const text = cm.getValue(); const funcRe = /^(url|url-prefix|domain|regexp)$/i;
const re = /^[\t ]*@-moz-document[\s\n]+/gm; let pos;
const applyRe = new RegExp([ const eatToken = sticky => {
/(?:\/\*[\s\S]*?(?:\*\/\s*|$))*/, if (!sticky) skipSpace(pos, posEnd);
/(url|url-prefix|domain|regexp)/, pos.ch++;
/\(((['"])(?:\\\\|\\\n|\\\3|[^\n])*?\3|[^)\n]*)\)\s*(,\s*)?/, const token = cm.getTokenAt(pos, true);
].map(rx => rx.source).join(''), 'giy'); pos.ch = token.end;
let match; return CodeMirror.cmpPos(pos, posEnd) <= 0 ? token : {};
re.lastIndex = posStart; };
while ((match = re.exec(text))) { const docCur = cm.getSearchCursor('@-moz-document', posStart);
if (match.index >= posEnd) { while (docCur.findNext() &&
return; CodeMirror.cmpPos(docCur.pos.to, posEnd) <= 0) {
} // CM can be nitpicky at token boundary so we'll check the next character
const safePos = {line: docCur.pos.from.line, ch: docCur.pos.from.ch + 1};
if (/\b(string|comment)\b/.test(cm.getTokenTypeAt(safePos))) continue;
const applies = []; const applies = [];
let m; pos = docCur.pos.to;
applyRe.lastIndex = re.lastIndex; do {
while ((m = applyRe.exec(text))) { 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( const apply = createApply(
m.index, funcIndex,
m[1], func,
unquote(m[2]), unquotedUrl,
unquote(m[2]) !== m[2] unquotedUrl !== url.string
); );
applies.push(apply); applies.push(apply);
re.lastIndex = applyRe.lastIndex; } while (eatToken().string === ',');
}
yield { yield {
pos: cm.posFromIndex(match.index), pos: docCur.pos.from,
applies 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;
}
// EOL is a whitespace so we'll check the next line
if (ch >= text.length) {
ch = 0;
return;
}
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) { function unquote(s) {
const first = s.charAt(0); const first = s.charAt(0);
return (first === '"' || first === "'") && s.endsWith(first) ? s.slice(1, -1) : s; return (first === '"' || first === "'") && s.endsWith(first) ? s.slice(1, -1) : s;

View File

@ -242,6 +242,23 @@
CodeMirror.commands[name] = (...args) => editor[name](...args); CodeMirror.commands[name] = (...args) => editor[name](...args);
} }
// speedup: reuse the old folding marks
// TODO: remove when https://github.com/codemirror/CodeMirror/pull/6010 is shipped in /vendor
const {setGutterMarker} = CodeMirror.prototype;
CodeMirror.prototype.setGutterMarker = function (line, gutterID, value) {
const o = this.state.foldGutter.options;
if (typeof o.indicatorOpen === 'string' ||
typeof o.indicatorFolded === 'string') {
const old = line.gutterMarkers && line.gutterMarkers[gutterID];
// old className can contain other names set by CodeMirror so we'll use classList
if (old && value && old.classList.contains(value.className) ||
!old && !value) {
return line;
}
}
return setGutterMarker.apply(this, arguments);
};
// CodeMirror convenience commands // CodeMirror convenience commands
Object.assign(CodeMirror.commands, { Object.assign(CodeMirror.commands, {
toggleEditorFocus, toggleEditorFocus,

View File

@ -1,5 +1,5 @@
/* exported getActiveTab onTabReady stringAsRegExp getTabRealURL openURL /* exported getActiveTab onTabReady stringAsRegExp getTabRealURL openURL
getStyleWithNoCode tryRegExp sessionStorageHash download getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
closeCurrentTab capitalize */ closeCurrentTab capitalize */
'use strict'; 'use strict';
@ -10,6 +10,9 @@ const VIVALDI = Boolean(chrome.app) && navigator.userAgent.includes('Vivaldi');
// const ANDROID = !chrome.windows; // const ANDROID = !chrome.windows;
let FIREFOX = !chrome.app && parseFloat(navigator.userAgent.match(/\bFirefox\/(\d+\.\d+)|$/)[1]); let FIREFOX = !chrome.app && parseFloat(navigator.userAgent.match(/\bFirefox\/(\d+\.\d+)|$/)[1]);
// see PR #781
const CHROME_HAS_BORDER_BUG = CHROME >= 3167 && CHROME <= 3704;
if (!CHROME && !chrome.browserAction.openPopup) { if (!CHROME && !chrome.browserAction.openPopup) {
// in FF pre-57 legacy addons can override useragent so we assume the worst // in FF pre-57 legacy addons can override useragent so we assume the worst
// until we know for sure in the async getBrowserInfo() // until we know for sure in the async getBrowserInfo()
@ -357,6 +360,31 @@ function deepCopy(obj) {
} }
function deepEqual(a, b, ignoredKeys) {
if (!a || !b) return a === b;
const type = typeof a;
if (type !== typeof b) return false;
if (type !== 'object') return a === b;
if (Array.isArray(a)) {
return Array.isArray(b) &&
a.length === b.length &&
a.every((v, i) => deepEqual(v, b[i], ignoredKeys));
}
for (const key in a) {
if (!Object.hasOwnProperty.call(a, key) ||
ignoredKeys && ignoredKeys.includes(key)) continue;
if (!Object.hasOwnProperty.call(b, key)) return false;
if (!deepEqual(a[key], b[key], ignoredKeys)) return false;
}
for (const key in b) {
if (!Object.hasOwnProperty.call(b, key) ||
ignoredKeys && ignoredKeys.includes(key)) continue;
if (!Object.hasOwnProperty.call(a, key)) return false;
}
return true;
}
function sessionStorageHash(name) { function sessionStorageHash(name) {
return { return {
name, name,

View File

@ -109,7 +109,7 @@
<span></span> <span></span>
</span> </span>
</label> </label>
<label class="chromium-only"> <label class="chromium-only chrome-no-popup-border">
<span i18n-text="popupBorders" i18n-title="popupBordersTooltip"></span> <span i18n-text="popupBorders" i18n-title="popupBordersTooltip"></span>
<span class="onoffswitch"> <span class="onoffswitch">
<input type="checkbox" id="popup.borders" class="slider"> <input type="checkbox" id="popup.borders" class="slider">

View File

@ -17,6 +17,7 @@ body {
min-width: 480px; min-width: 480px;
max-width: 800px; max-width: 800px;
width: max-content; width: max-content;
overflow-x: hidden;
} }
@supports (-moz-appearance:none) { @supports (-moz-appearance:none) {
@ -32,7 +33,8 @@ body {
} }
} }
.firefox .chromium-only { .firefox .chromium-only,
.chromium-only.chrome-no-popup-border {
display: none; display: none;
} }

View File

@ -1,6 +1,7 @@
/* global messageBox msg setupLivePrefs enforceInputRange /* global messageBox msg setupLivePrefs enforceInputRange
$ $$ $create $createLink $ $$ $create $createLink
FIREFOX OPERA CHROME URLS openURL prefs t API ignoreChromeError capitalize */ FIREFOX OPERA CHROME URLS openURL prefs t API ignoreChromeError
CHROME_HAS_BORDER_BUG capitalize */
'use strict'; 'use strict';
setupLivePrefs(); setupLivePrefs();
@ -8,6 +9,13 @@ setupRadioButtons();
enforceInputRange($('#popupWidth')); enforceInputRange($('#popupWidth'));
setTimeout(splitLongTooltips); setTimeout(splitLongTooltips);
if (CHROME_HAS_BORDER_BUG) {
const borderOption = $('.chrome-no-popup-border');
if (borderOption) {
borderOption.classList.remove('chrome-no-popup-border');
}
}
// collapse #advanced block in Chrome pre-66 (classic chrome://extensions UI) // collapse #advanced block in Chrome pre-66 (classic chrome://extensions UI)
if (!FIREFOX && !OPERA && CHROME < 3343) { if (!FIREFOX && !OPERA && CHROME < 3343) {
const block = $('#advanced'); const block = $('#advanced');

View File

@ -1,7 +1,7 @@
/* global configDialog hotkeys onTabReady msg /* global configDialog hotkeys onTabReady msg
getActiveTab FIREFOX getTabRealURL URLS API onDOMready $ $$ prefs CHROME getActiveTab FIREFOX getTabRealURL URLS API onDOMready $ $$ prefs CHROME
setupLivePrefs template t $create tWordBreak animateElement setupLivePrefs template t $create tWordBreak animateElement
tryJSONparse debounce */ tryJSONparse debounce CHROME_HAS_BORDER_BUG */
'use strict'; 'use strict';
@ -42,7 +42,10 @@ prefs.subscribe(['popup.stylesFirst'], (key, stylesFirst) => {
document.body.insertBefore(installed, before); document.body.insertBefore(installed, before);
}); });
prefs.subscribe(['popupWidth'], (key, value) => setPopupWidth(value)); prefs.subscribe(['popupWidth'], (key, value) => setPopupWidth(value));
prefs.subscribe(['popup.borders'], (key, value) => toggleSideBorders(value));
if (CHROME_HAS_BORDER_BUG) {
prefs.subscribe(['popup.borders'], (key, value) => toggleSideBorders(value));
}
function onRuntimeMessage(msg) { function onRuntimeMessage(msg) {
switch (msg.method) { switch (msg.method) {
@ -68,7 +71,7 @@ function setPopupWidth(width = prefs.get('popupWidth')) {
function toggleSideBorders(state = prefs.get('popup.borders')) { function toggleSideBorders(state = prefs.get('popup.borders')) {
// runs before <body> is parsed // runs before <body> is parsed
const style = document.documentElement.style; const style = document.documentElement.style;
if (CHROME >= 3167 && state) { if (CHROME_HAS_BORDER_BUG && state) {
style.cssText += style.cssText +=
'border-left: 2px solid white !important;' + 'border-left: 2px solid white !important;' +
'border-right: 2px solid white !important;'; 'border-right: 2px solid white !important;';