refactor CSSLint
* reduce linting delay * parse mozformat in worker * allow empty functions in 'filter:' property https://drafts.fxtf.org/filter-effects/#supported-filter-functions * support comma-separated list in :lang() * strip vendor prefix in isLiteral()
This commit is contained in:
parent
b8506e1e45
commit
d2cba96e10
|
@ -1,3 +1,4 @@
|
||||||
vendor/
|
vendor/
|
||||||
vendor-overwrites/*
|
vendor-overwrites/*
|
||||||
!vendor-overwrites/colorpicker
|
!vendor-overwrites/colorpicker
|
||||||
|
!vendor-overwrites/csslint
|
||||||
|
|
|
@ -239,13 +239,13 @@ rules:
|
||||||
object-curly-spacing: [2, never]
|
object-curly-spacing: [2, never]
|
||||||
object-shorthand: [0]
|
object-shorthand: [0]
|
||||||
one-var-declaration-per-line: [1]
|
one-var-declaration-per-line: [1]
|
||||||
one-var: [0]
|
one-var: [2, {initialized: never}]
|
||||||
operator-assignment: [2, always]
|
operator-assignment: [2, always]
|
||||||
operator-linebreak: [2, after, overrides: {"?": ignore, ":": ignore, "&&": ignore, "||": ignore}]
|
operator-linebreak: [2, after, overrides: {"?": ignore, ":": ignore, "&&": ignore, "||": ignore}]
|
||||||
padded-blocks: [0]
|
padded-blocks: [0]
|
||||||
prefer-numeric-literals: [2]
|
prefer-numeric-literals: [2]
|
||||||
prefer-rest-params: [0]
|
prefer-rest-params: [0]
|
||||||
prefer-const: [1, {destructuring: any, ignoreReadBeforeAssign: true}]
|
prefer-const: [1, {destructuring: all, ignoreReadBeforeAssign: true}]
|
||||||
quote-props: [0]
|
quote-props: [0]
|
||||||
quotes: [1, single, avoid-escape]
|
quotes: [1, single, avoid-escape]
|
||||||
radix: [2, as-needed]
|
radix: [2, as-needed]
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
<link href="global.css" rel="stylesheet">
|
<link href="global.css" rel="stylesheet">
|
||||||
<link href="edit/edit.css" rel="stylesheet">
|
<link href="edit/edit.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="msgbox/msgbox.css">
|
||||||
|
|
||||||
<style id="firefox-transitions-bug-suppressor">
|
<style id="firefox-transitions-bug-suppressor">
|
||||||
/* restrict to FF */
|
/* restrict to FF */
|
||||||
|
@ -22,7 +23,6 @@
|
||||||
<script src="js/prefs.js"></script>
|
<script src="js/prefs.js"></script>
|
||||||
<script src="js/localization.js"></script>
|
<script src="js/localization.js"></script>
|
||||||
<script src="js/script-loader.js"></script>
|
<script src="js/script-loader.js"></script>
|
||||||
<script src="js/moz-parser.js"></script>
|
|
||||||
<script src="js/storage-util.js"></script>
|
<script src="js/storage-util.js"></script>
|
||||||
<script src="content/apply.js"></script>
|
<script src="content/apply.js"></script>
|
||||||
<script src="edit/lint.js"></script>
|
<script src="edit/lint.js"></script>
|
||||||
|
@ -37,6 +37,8 @@
|
||||||
<script src="edit/codemirror-editing-hooks.js"></script>
|
<script src="edit/codemirror-editing-hooks.js"></script>
|
||||||
<script src="edit/edit.js"></script>
|
<script src="edit/edit.js"></script>
|
||||||
|
|
||||||
|
<script src="msgbox/msgbox.js" async></script>
|
||||||
|
|
||||||
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
|
<link href="vendor/codemirror/lib/codemirror.css" rel="stylesheet">
|
||||||
<script src="vendor/codemirror/lib/codemirror.js"></script>
|
<script src="vendor/codemirror/lib/codemirror.js"></script>
|
||||||
|
|
||||||
|
|
25
edit/edit.js
25
edit/edit.js
|
@ -1,11 +1,12 @@
|
||||||
/*
|
/*
|
||||||
global CodeMirror parserlib loadScript
|
global CodeMirror parserlib loadScript
|
||||||
global CSSLint initLint linterConfig updateLintReport renderLintReport updateLinter
|
global CSSLint initLint linterConfig updateLintReport renderLintReport updateLinter
|
||||||
global mozParser createSourceEditor
|
global createSourceEditor
|
||||||
global closeCurrentTab regExpTester messageBox
|
global closeCurrentTab regExpTester messageBox
|
||||||
global setupCodeMirror
|
global setupCodeMirror
|
||||||
global beautify
|
global beautify
|
||||||
global initWithSectionStyle addSections removeSection getSectionsHashes
|
global initWithSectionStyle addSections removeSection getSectionsHashes
|
||||||
|
global sectionsToMozFormat
|
||||||
*/
|
*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -415,7 +416,7 @@ function showMozillaFormat() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toMozillaFormat() {
|
function toMozillaFormat() {
|
||||||
return mozParser.format({sections: getSectionsHashes()});
|
return sectionsToMozFormat({sections: getSectionsHashes()});
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromMozillaFormat() {
|
function fromMozillaFormat() {
|
||||||
|
@ -450,8 +451,24 @@ function fromMozillaFormat() {
|
||||||
function doImport({replaceOldStyle = false}) {
|
function doImport({replaceOldStyle = false}) {
|
||||||
lockPageUI(true);
|
lockPageUI(true);
|
||||||
new Promise(setTimeout)
|
new Promise(setTimeout)
|
||||||
.then(() => mozParser.parse(popup.codebox.getValue().trim()))
|
.then(() => {
|
||||||
.then(sections => {
|
const worker = linterConfig.worker.csslint;
|
||||||
|
if (!worker.instance) worker.instance = new Worker(worker.path);
|
||||||
|
})
|
||||||
|
.then(() => linterConfig.invokeWorker({
|
||||||
|
linter: 'csslint',
|
||||||
|
action: 'parse',
|
||||||
|
code: popup.codebox.getValue().trim(),
|
||||||
|
}))
|
||||||
|
.then(({sections, errors}) => {
|
||||||
|
// shouldn't happen but just in case
|
||||||
|
if (!sections.length && errors.length) {
|
||||||
|
return Promise.reject(errors);
|
||||||
|
}
|
||||||
|
// show the errors in case linting is disabled or stylelint misses what csslint has found
|
||||||
|
if (errors.length && prefs.get('editor.linter') !== 'csslint') {
|
||||||
|
showError(errors);
|
||||||
|
}
|
||||||
removeOldSections(replaceOldStyle);
|
removeOldSections(replaceOldStyle);
|
||||||
return addSections(sections, div => setCleanItem(div, false));
|
return addSections(sections, div => setCleanItem(div, false));
|
||||||
})
|
})
|
||||||
|
|
24
edit/lint.js
24
edit/lint.js
|
@ -20,7 +20,7 @@ var linterConfig = {
|
||||||
stylelint: 'editorStylelintConfig',
|
stylelint: 'editorStylelintConfig',
|
||||||
},
|
},
|
||||||
worker: {
|
worker: {
|
||||||
csslint: {path: '/vendor-overwrites/csslint/csslint-worker.js'},
|
csslint: {path: '/vendor-overwrites/csslint/csslint-loader.js'},
|
||||||
stylelint: {path: '/vendor-overwrites/stylelint/stylelint-bundle.min.js'},
|
stylelint: {path: '/vendor-overwrites/stylelint/stylelint-bundle.min.js'},
|
||||||
},
|
},
|
||||||
allRuleIds: {
|
allRuleIds: {
|
||||||
|
@ -48,7 +48,7 @@ var linterConfig = {
|
||||||
},
|
},
|
||||||
onUpdateLinting(annotationsNotSorted, annotations, cm) {
|
onUpdateLinting(annotationsNotSorted, annotations, cm) {
|
||||||
cm.endOperation();
|
cm.endOperation();
|
||||||
updateLintReport(cm, 0);
|
updateLintReport(cm);
|
||||||
},
|
},
|
||||||
} : false;
|
} : false;
|
||||||
},
|
},
|
||||||
|
@ -561,15 +561,17 @@ function setupLinterPopup(config) {
|
||||||
|
|
||||||
function loadLinterAssets(name = linterConfig.getName()) {
|
function loadLinterAssets(name = linterConfig.getName()) {
|
||||||
const worker = linterConfig.worker[name];
|
const worker = linterConfig.worker[name];
|
||||||
return !name || !worker || worker.instance ? Promise.resolve() :
|
if (!name || !worker) return Promise.resolve();
|
||||||
loadScript((worker.instance ? [] : [
|
const scripts = [];
|
||||||
(worker.instance = new Worker(worker.path)),
|
if (!worker.instance) {
|
||||||
`/edit/lint-defaults-${name}.js`,
|
worker.instance = new Worker(worker.path);
|
||||||
]).concat(CodeMirror.lint ? [] : [
|
scripts.push(`/edit/lint-defaults-${name}.js`);
|
||||||
|
}
|
||||||
|
if (!CodeMirror.lint) {
|
||||||
|
scripts.push(
|
||||||
'/vendor/codemirror/addon/lint/lint.css',
|
'/vendor/codemirror/addon/lint/lint.css',
|
||||||
'/msgbox/msgbox.css',
|
|
||||||
'/vendor/codemirror/addon/lint/lint.js',
|
'/vendor/codemirror/addon/lint/lint.js',
|
||||||
'/edit/lint-codemirror-helper.js',
|
'/edit/lint-codemirror-helper.js');
|
||||||
'/msgbox/msgbox.js'
|
}
|
||||||
]));
|
return scripts.length ? loadScript(scripts) : Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* global CodeMirror dirtyReporter initLint */
|
/* global CodeMirror dirtyReporter initLint */
|
||||||
/* global showToggleStyleHelp goBackToManage updateLintReportIfEnabled */
|
/* global showToggleStyleHelp goBackToManage updateLintReportIfEnabled */
|
||||||
/* global editors linterConfig updateLinter regExpTester mozParser */
|
/* global editors linterConfig updateLinter regExpTester sectionsToMozFormat */
|
||||||
/* global createAppliesToLineWidget messageBox */
|
/* global createAppliesToLineWidget messageBox */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -100,10 +100,10 @@ function createSourceEditor(style) {
|
||||||
|
|
||||||
function setupNewStyle(style) {
|
function setupNewStyle(style) {
|
||||||
style.sections[0].code = ' '.repeat(prefs.get('editor.tabSize')) + '/* Insert code here... */';
|
style.sections[0].code = ' '.repeat(prefs.get('editor.tabSize')) + '/* Insert code here... */';
|
||||||
let section = mozParser.format(style);
|
let section = sectionsToMozFormat(style);
|
||||||
if (!section.includes('@-moz-document')) {
|
if (!section.includes('@-moz-document')) {
|
||||||
style.sections[0].domains = ['example.com'];
|
style.sections[0].domains = ['example.com'];
|
||||||
section = mozParser.format(style);
|
section = sectionsToMozFormat(style);
|
||||||
}
|
}
|
||||||
const DEFAULT_CODE = `
|
const DEFAULT_CODE = `
|
||||||
/* ==UserStyle==
|
/* ==UserStyle==
|
||||||
|
|
23
edit/util.js
23
edit/util.js
|
@ -93,3 +93,26 @@ function dirtyReporter() {
|
||||||
|
|
||||||
return wrap({add, remove, modify, clear, isDirty, onChange, has});
|
return wrap({add, remove, modify, clear, isDirty, onChange, has});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function sectionsToMozFormat(style) {
|
||||||
|
const propertyToCss = {
|
||||||
|
urls: 'url',
|
||||||
|
urlPrefixes: 'url-prefix',
|
||||||
|
domains: 'domain',
|
||||||
|
regexps: 'regexp',
|
||||||
|
};
|
||||||
|
return style.sections.map(section => {
|
||||||
|
let cssMds = [];
|
||||||
|
for (const i in propertyToCss) {
|
||||||
|
if (section[i]) {
|
||||||
|
cssMds = cssMds.concat(section[i].map(v =>
|
||||||
|
propertyToCss[i] + '("' + v.replace(/\\/g, '\\\\') + '")'
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cssMds.length ?
|
||||||
|
'@-moz-document ' + cssMds.join(', ') + ' {\n' + section.code + '\n}' :
|
||||||
|
section.code;
|
||||||
|
}).join('\n\n');
|
||||||
|
}
|
||||||
|
|
254
js/moz-parser.js
254
js/moz-parser.js
|
@ -1,165 +1,109 @@
|
||||||
/* global parserlib, loadScript */
|
/* global parserlib */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// eslint-disable-next-line no-var
|
function parseMozFormat(mozStyle) {
|
||||||
var mozParser = (() => {
|
const CssToProperty = {
|
||||||
// direct & reverse mapping of @-moz-document keywords and internal property names
|
'url': 'urls',
|
||||||
const propertyToCss = {urls: 'url', urlPrefixes: 'url-prefix', domains: 'domain', regexps: 'regexp'};
|
'url-prefix': 'urlPrefixes',
|
||||||
const CssToProperty = {'url': 'urls', 'url-prefix': 'urlPrefixes', 'domain': 'domains', 'regexp': 'regexps'};
|
'domain': 'domains',
|
||||||
|
'regexp': 'regexps',
|
||||||
|
};
|
||||||
|
const parser = new parserlib.css.Parser();
|
||||||
|
const sectionStack = [{code: '', start: 0}];
|
||||||
|
const errors = [];
|
||||||
|
const sections = [];
|
||||||
|
|
||||||
function parseMozFormat(mozStyle) {
|
parser.addListener('startdocument', e => {
|
||||||
return new Promise((resolve, reject) => {
|
const lastSection = sectionStack[sectionStack.length - 1];
|
||||||
const parser = new parserlib.css.Parser();
|
let outerText = mozStyle.slice(lastSection.start, e.offset);
|
||||||
const lines = mozStyle.split('\n');
|
const lastCmt = getLastComment(outerText);
|
||||||
const sectionStack = [{code: '', start: {line: 1, col: 1}}];
|
const section = {
|
||||||
const errors = [];
|
code: '',
|
||||||
const sections = [];
|
start: parser._tokenStream._token.offset + 1,
|
||||||
|
};
|
||||||
parser.addListener('startdocument', e => {
|
// move last comment before @-moz-document inside the section
|
||||||
const lastSection = sectionStack[sectionStack.length - 1];
|
if (!lastCmt.includes('AGENT_SHEET') &&
|
||||||
let outerText = getRange(lastSection.start, {line: e.line, col: e.col - 1});
|
!lastCmt.includes('==') &&
|
||||||
const lastCmt = getLastComment(outerText);
|
!/==userstyle==/iu.test(lastCmt)) {
|
||||||
const {endLine: line, endCol: col} = parser._tokenStream._token;
|
if (lastCmt) {
|
||||||
const section = {code: '', start: {line, col}};
|
section.code = lastCmt + '\n';
|
||||||
// move last comment before @-moz-document inside the section
|
outerText = outerText.slice(0, -lastCmt.length);
|
||||||
if (!/\/\*[\s\n]*AGENT_SHEET[\s\n]*\*\//.test(lastCmt)) {
|
|
||||||
if (lastCmt) {
|
|
||||||
section.code = lastCmt + '\n';
|
|
||||||
outerText = outerText.slice(0, -lastCmt.length);
|
|
||||||
}
|
|
||||||
outerText = outerText.match(/^\s*/)[0] + outerText.trim();
|
|
||||||
}
|
|
||||||
if (outerText.trim()) {
|
|
||||||
lastSection.code = outerText;
|
|
||||||
doAddSection(lastSection);
|
|
||||||
lastSection.code = '';
|
|
||||||
}
|
|
||||||
for (const f of e.functions) {
|
|
||||||
const m = f && f.match(/^([\w-]*)\((.+?)\)$/);
|
|
||||||
if (!m || !/^(url|url-prefix|domain|regexp)$/.test(m[1])) {
|
|
||||||
errors.push(`${e.line}:${e.col + 1} invalid function "${m ? m[1] : f || ''}"`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const aType = CssToProperty[m[1]];
|
|
||||||
const aValue = unquote(aType !== 'regexps' ? m[2] : m[2].replace(/\\\\/g, '\\'));
|
|
||||||
(section[aType] = section[aType] || []).push(aValue);
|
|
||||||
}
|
|
||||||
sectionStack.push(section);
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.addListener('enddocument', e => {
|
|
||||||
const section = sectionStack.pop();
|
|
||||||
const lastSection = sectionStack[sectionStack.length - 1];
|
|
||||||
const end = {line: e.line, col: e.col - 1};
|
|
||||||
section.code += getRange(section.start, end);
|
|
||||||
end.col += 2;
|
|
||||||
lastSection.start = end;
|
|
||||||
doAddSection(section);
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.addListener('endstylesheet', () => {
|
|
||||||
// add nonclosed outer sections (either broken or the last global one)
|
|
||||||
const lastLine = lines[lines.length - 1];
|
|
||||||
const endOfText = {line: lines.length, col: lastLine.length + 1};
|
|
||||||
const lastSection = sectionStack[sectionStack.length - 1];
|
|
||||||
lastSection.code += getRange(lastSection.start, endOfText);
|
|
||||||
sectionStack.forEach(doAddSection);
|
|
||||||
|
|
||||||
if (errors.length) {
|
|
||||||
reject(errors);
|
|
||||||
} else {
|
|
||||||
resolve(sections);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.addListener('error', e => {
|
|
||||||
errors.push(e.line + ':' + e.col + ' ' +
|
|
||||||
e.message.replace(/ at line \d.+$/, ''));
|
|
||||||
});
|
|
||||||
|
|
||||||
parser.parse(mozStyle);
|
|
||||||
|
|
||||||
function getRange(start, end) {
|
|
||||||
const L1 = start.line - 1;
|
|
||||||
const C1 = start.col - 1;
|
|
||||||
const L2 = end.line - 1;
|
|
||||||
const C2 = end.col - 1;
|
|
||||||
if (L1 === L2) {
|
|
||||||
return lines[L1].substr(C1, C2 - C1 + 1);
|
|
||||||
} else {
|
|
||||||
const middle = lines.slice(L1 + 1, L2).join('\n');
|
|
||||||
return lines[L1].substr(C1) + '\n' + middle +
|
|
||||||
(L2 >= lines.length ? '' : ((middle ? '\n' : '') + lines[L2].substring(0, C2)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
outerText = outerText.match(/^\s*/)[0] + outerText.trim();
|
||||||
|
}
|
||||||
|
if (outerText.trim()) {
|
||||||
|
lastSection.code = outerText;
|
||||||
|
doAddSection(lastSection);
|
||||||
|
lastSection.code = '';
|
||||||
|
}
|
||||||
|
for (const {name, expr, uri} of e.functions) {
|
||||||
|
const aType = CssToProperty[name.toLowerCase()];
|
||||||
|
(section[aType] = section[aType] || []).push(uri || expr && expr.parts[0].value || '');
|
||||||
|
}
|
||||||
|
sectionStack.push(section);
|
||||||
|
});
|
||||||
|
|
||||||
function doAddSection(section) {
|
parser.addListener('enddocument', e => {
|
||||||
section.code = section.code.trim();
|
const section = sectionStack.pop();
|
||||||
// don't add empty sections
|
const lastSection = sectionStack[sectionStack.length - 1];
|
||||||
if (
|
section.code += mozStyle.slice(section.start, e.offset);
|
||||||
!section.code &&
|
lastSection.start = e.offset + 1;
|
||||||
!section.urls &&
|
doAddSection(section);
|
||||||
!section.urlPrefixes &&
|
});
|
||||||
!section.domains &&
|
|
||||||
!section.regexps
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* ignore boilerplate NS */
|
|
||||||
if (section.code === '@namespace url(http://www.w3.org/1999/xhtml);') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sections.push(Object.assign({}, section));
|
|
||||||
}
|
|
||||||
|
|
||||||
function unquote(s) {
|
parser.addListener('endstylesheet', () => {
|
||||||
const first = s.charAt(0);
|
// add nonclosed outer sections (either broken or the last global one)
|
||||||
return (first === '"' || first === "'") && s.endsWith(first) ? s.slice(1, -1) : s;
|
const lastSection = sectionStack[sectionStack.length - 1];
|
||||||
}
|
lastSection.code += mozStyle.slice(lastSection.start);
|
||||||
|
sectionStack.forEach(doAddSection);
|
||||||
|
});
|
||||||
|
|
||||||
function getLastComment(text) {
|
parser.addListener('error', e => {
|
||||||
let open = text.length;
|
errors.push(`${e.line}:${e.col} ${e.message.replace(/ at line \d.+$/, '')}`);
|
||||||
let close;
|
});
|
||||||
while (open) {
|
|
||||||
// at this point we're guaranteed to be outside of a comment
|
parser.parse(mozStyle);
|
||||||
close = text.lastIndexOf('*/', open - 2);
|
return {sections, errors};
|
||||||
if (close < 0) {
|
|
||||||
break;
|
function doAddSection(section) {
|
||||||
}
|
section.code = section.code.trim();
|
||||||
// stop if a non-whitespace precedes and return what we currently have
|
// don't add empty sections
|
||||||
const tailEmpty = !text.substring(close + 2, open).trim();
|
if (
|
||||||
if (!tailEmpty) {
|
!section.code &&
|
||||||
break;
|
!section.urls &&
|
||||||
}
|
!section.urlPrefixes &&
|
||||||
// find a closed preceding comment
|
!section.domains &&
|
||||||
const prevClose = text.lastIndexOf('*/', close);
|
!section.regexps
|
||||||
// then find the real start of current comment
|
) {
|
||||||
// e.g. /* preceding */ /* current /* current /* current */
|
return;
|
||||||
open = text.indexOf('/*', prevClose < 0 ? 0 : prevClose + 2);
|
}
|
||||||
}
|
/* ignore boilerplate NS */
|
||||||
return text.substr(open);
|
if (section.code === '@namespace url(http://www.w3.org/1999/xhtml);') {
|
||||||
}
|
return;
|
||||||
});
|
}
|
||||||
|
sections.push(Object.assign({}, section));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
function getLastComment(text) {
|
||||||
// Parse mozilla-format userstyle into sections
|
let open = text.length;
|
||||||
parse(text) {
|
let close;
|
||||||
return Promise.resolve(self.CSSLint || loadScript('/vendor-overwrites/csslint/csslint-worker.js'))
|
while (open) {
|
||||||
.then(() => parseMozFormat(text));
|
// at this point we're guaranteed to be outside of a comment
|
||||||
},
|
close = text.lastIndexOf('*/', open - 2);
|
||||||
format(style) {
|
if (close < 0) {
|
||||||
return style.sections.map(section => {
|
break;
|
||||||
let cssMds = [];
|
}
|
||||||
for (const i in propertyToCss) {
|
// stop if a non-whitespace precedes and return what we currently have
|
||||||
if (section[i]) {
|
const tailEmpty = !text.substring(close + 2, open).trim();
|
||||||
cssMds = cssMds.concat(section[i].map(v =>
|
if (!tailEmpty) {
|
||||||
propertyToCss[i] + '("' + v.replace(/\\/g, '\\\\') + '")'
|
break;
|
||||||
));
|
}
|
||||||
}
|
// find a closed preceding comment
|
||||||
}
|
const prevClose = text.lastIndexOf('*/', close - 2);
|
||||||
return cssMds.length ? '@-moz-document ' + cssMds.join(', ') + ' {\n' + section.code + '\n}' : section.code;
|
// then find the real start of current comment
|
||||||
}).join('\n\n');
|
// e.g. /* preceding */ /* current /* current /* current */
|
||||||
|
open = text.indexOf('/*', prevClose < 0 ? 0 : prevClose + 2);
|
||||||
}
|
}
|
||||||
};
|
return open ? text.slice(open) : text;
|
||||||
})();
|
}
|
||||||
|
}
|
||||||
|
|
12
js/prefs.js
12
js/prefs.js
|
@ -57,9 +57,9 @@ var prefs = new function Prefs() {
|
||||||
end_with_newline: false,
|
end_with_newline: false,
|
||||||
indent_conditional: true,
|
indent_conditional: true,
|
||||||
},
|
},
|
||||||
'editor.lintDelay': 500, // lint gutter marker update delay, ms
|
'editor.lintDelay': 300, // lint gutter marker update delay, ms
|
||||||
'editor.linter': 'csslint', // 'csslint' or 'stylelint' or ''
|
'editor.linter': 'csslint', // 'csslint' or 'stylelint' or ''
|
||||||
'editor.lintReportDelay': 4500, // lint report update delay, ms
|
'editor.lintReportDelay': 500, // lint report update delay, ms
|
||||||
'editor.matchHighlight': 'token', // token = token/word under cursor even if nothing is selected
|
'editor.matchHighlight': 'token', // token = token/word under cursor even if nothing is selected
|
||||||
// selection = only when something is selected
|
// selection = only when something is selected
|
||||||
// '' (empty string) = disabled
|
// '' (empty string) = disabled
|
||||||
|
@ -234,6 +234,7 @@ var prefs = new function Prefs() {
|
||||||
// Unlike chrome.storage or messaging, HTML5 localStorage is synchronous and always ready,
|
// Unlike chrome.storage or messaging, HTML5 localStorage is synchronous and always ready,
|
||||||
// so we'll mirror the prefs to avoid using the wrong defaults during the startup phase
|
// so we'll mirror the prefs to avoid using the wrong defaults during the startup phase
|
||||||
const importFromLocalStorage = () => {
|
const importFromLocalStorage = () => {
|
||||||
|
forgetOutdatedDefaults(localStorage);
|
||||||
for (const key in defaults) {
|
for (const key in defaults) {
|
||||||
const defaultValue = defaults[key];
|
const defaultValue = defaults[key];
|
||||||
let value = localStorage[key];
|
let value = localStorage[key];
|
||||||
|
@ -323,6 +324,7 @@ var prefs = new function Prefs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function importFromSync(synced = {}) {
|
function importFromSync(synced = {}) {
|
||||||
|
forgetOutdatedDefaults(synced);
|
||||||
for (const key in defaults) {
|
for (const key in defaults) {
|
||||||
if (key in synced) {
|
if (key in synced) {
|
||||||
this.set(key, synced[key], {sync: false});
|
this.set(key, synced[key], {sync: false});
|
||||||
|
@ -330,6 +332,12 @@ var prefs = new function Prefs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function forgetOutdatedDefaults(storage) {
|
||||||
|
// our linter runs as a worker so we can reduce the delay and forget the old default values
|
||||||
|
if (Number(storage['editor.lintDelay']) === 500) delete storage['editor.lintDelay'];
|
||||||
|
if (Number(storage['editor.lintReportDelay']) === 4500) delete storage['editor.lintReportDelay'];
|
||||||
|
}
|
||||||
|
|
||||||
function defineReadonlyProperty(obj, key, value) {
|
function defineReadonlyProperty(obj, key, value) {
|
||||||
const copy = deepCopy(value);
|
const copy = deepCopy(value);
|
||||||
if (typeof copy === 'object') {
|
if (typeof copy === 'object') {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/* global loadScript mozParser semverCompare colorConverter styleCodeEmpty */
|
/* global loadScript semverCompare colorConverter styleCodeEmpty */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// eslint-disable-next-line no-var
|
// eslint-disable-next-line no-var
|
||||||
|
@ -485,11 +485,17 @@ var usercss = (() => {
|
||||||
|
|
||||||
const sVars = simpleVars(vars);
|
const sVars = simpleVars(vars);
|
||||||
|
|
||||||
return Promise.resolve(builder.preprocess && builder.preprocess(sourceCode, sVars) || sourceCode)
|
return (
|
||||||
|
Promise.resolve(
|
||||||
|
builder.preprocess && builder.preprocess(sourceCode, sVars) ||
|
||||||
|
sourceCode)
|
||||||
.then(mozStyle => invokeWorker({action: 'parse', code: mozStyle}))
|
.then(mozStyle => invokeWorker({action: 'parse', code: mozStyle}))
|
||||||
.then(sections => (style.sections = sections))
|
.then(({sections, errors}) => sections.length && sections || Promise.reject(errors))
|
||||||
.then(() => builder.postprocess && builder.postprocess(style.sections, sVars))
|
.then(sections => {
|
||||||
.then(() => style);
|
style.sections = sections;
|
||||||
|
if (builder.postprocess) builder.postprocess(style.sections, sVars);
|
||||||
|
return style;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function simpleVars(vars) {
|
function simpleVars(vars) {
|
||||||
|
@ -568,7 +574,7 @@ var usercss = (() => {
|
||||||
|
|
||||||
function invokeWorker(message) {
|
function invokeWorker(message) {
|
||||||
if (!worker.queue) {
|
if (!worker.queue) {
|
||||||
worker.instance = new Worker('/vendor-overwrites/csslint/csslint-worker.js');
|
worker.instance = new Worker('/vendor-overwrites/csslint/csslint-loader.js');
|
||||||
worker.queue = [];
|
worker.queue = [];
|
||||||
worker.instance.onmessage = ({data}) => {
|
worker.instance.onmessage = ({data}) => {
|
||||||
worker.queue.shift().resolve(data.__ERROR__ ? Promise.reject(data.__ERROR__) : data);
|
worker.queue.shift().resolve(data.__ERROR__ ? Promise.reject(data.__ERROR__) : data);
|
||||||
|
|
35
vendor-overwrites/csslint/csslint-loader.js
Normal file
35
vendor-overwrites/csslint/csslint-loader.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/* global parserlib CSSLint parseMozFormat */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
self.importScripts('./parserlib.js');
|
||||||
|
parserlib.css.Tokens[parserlib.css.Tokens.COMMENT].hide = false;
|
||||||
|
|
||||||
|
self.onmessage = ({data: {action = 'run', code, config}}) => {
|
||||||
|
|
||||||
|
if (action === 'parse') {
|
||||||
|
if (!self.parseMozFormat) self.importScripts('/js/moz-parser.js');
|
||||||
|
self.postMessage(parseMozFormat(code));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!self.CSSLint) self.importScripts('./csslint.js');
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case 'getAllRuleIds':
|
||||||
|
// the functions are non-tranferable and we need only an id
|
||||||
|
self.postMessage(CSSLint.getRules().map(rule => rule.id));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'getAllRuleInfos':
|
||||||
|
// the functions are non-tranferable
|
||||||
|
self.postMessage(CSSLint.getRules().map(rule => JSON.parse(JSON.stringify(rule))));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'run': {
|
||||||
|
const results = CSSLint.verify(code, config).messages
|
||||||
|
//.filter(m => !m.message.includes('/*[[') && !m.message.includes(']]*/'))
|
||||||
|
.map(m => Object.assign(m, {rule: {id: m.rule.id}}));
|
||||||
|
self.postMessage(results);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
1723
vendor-overwrites/csslint/csslint.js
Normal file
1723
vendor-overwrites/csslint/csslint.js
Normal file
File diff suppressed because it is too large
Load Diff
5963
vendor-overwrites/csslint/parserlib.js
Normal file
5963
vendor-overwrites/csslint/parserlib.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user