worker for stylelint; hints in linter config popup
This commit is contained in:
parent
c2d68612ec
commit
493c1a65c0
|
@ -215,6 +215,10 @@
|
|||
"message": "Use default",
|
||||
"description": "'Set to default' button in a confirm dialog"
|
||||
},
|
||||
"confirmDiscardChanges": {
|
||||
"message": "Discard the changes?",
|
||||
"description": "Generic label or title displayed when trying to close something (not a style) with unsaved changes"
|
||||
},
|
||||
"confirmSave": {
|
||||
"message": "Save",
|
||||
"description": "'Save' button in a confirm dialog"
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
.CodeMirror-hints {
|
||||
z-index: 999;
|
||||
}
|
||||
.CodeMirror-hint:hover {
|
||||
color: white;
|
||||
background: #08f;
|
||||
|
|
29
edit/edit.js
29
edit/edit.js
|
@ -1718,6 +1718,7 @@ function fromMozillaFormat() {
|
|||
popup.codebox.focus();
|
||||
popup.codebox.on('changes', cm => {
|
||||
popup.classList.toggle('ready', !cm.isBlank());
|
||||
cm.markClean();
|
||||
});
|
||||
// overwrite default extraKeys as those are inapplicable in popup context
|
||||
popup.codebox.options.extraKeys = {
|
||||
|
@ -1885,15 +1886,18 @@ function showKeyMapHelp() {
|
|||
}
|
||||
}
|
||||
|
||||
function showHelp(title, body) {
|
||||
function showHelp(title = '', body) {
|
||||
const div = $('#help-popup');
|
||||
div.classList.remove('big');
|
||||
$('.contents', div).textContent = '';
|
||||
$('.contents', div).appendChild(typeof body === 'string' ? tHTML(body) : body);
|
||||
const contents = $('.contents', div);
|
||||
contents.textContent = '';
|
||||
if (body) {
|
||||
contents.appendChild(typeof body === 'string' ? tHTML(body) : body);
|
||||
}
|
||||
$('.title', div).textContent = title;
|
||||
|
||||
if (getComputedStyle(div).display === 'none') {
|
||||
document.addEventListener('keydown', closeHelp);
|
||||
window.addEventListener('keydown', closeHelp, true);
|
||||
// avoid chaining on multiple showHelp() calls
|
||||
$('.dismiss', div).onclick = closeHelp;
|
||||
}
|
||||
|
@ -1902,16 +1906,19 @@ function showHelp(title, body) {
|
|||
return div;
|
||||
|
||||
function closeHelp(e) {
|
||||
if (
|
||||
!e ||
|
||||
e.type === 'click' ||
|
||||
((e.keyCode || e.which) === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey)
|
||||
) {
|
||||
if (!e || e.type === 'click' ||
|
||||
(e.which === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey &&
|
||||
!$('.CodeMirror-hints, #message-box') && !(document.activeElement instanceof HTMLInputElement))) {
|
||||
if (e && div.codebox && !div.codebox.options.readOnly && !div.codebox.isClean()) {
|
||||
messageBox.confirm(t('confirmDiscardChanges')).then(ok => ok && closeHelp());
|
||||
return;
|
||||
}
|
||||
div.style.display = '';
|
||||
const contents = $('.contents');
|
||||
contents.textContent = '';
|
||||
clearTimeout(contents.timer);
|
||||
document.removeEventListener('keydown', closeHelp);
|
||||
window.removeEventListener('keydown', closeHelp, true);
|
||||
window.dispatchEvent(new Event('closeHelp'));
|
||||
(editors.lastActive || editors[0]).focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,26 @@
|
|||
/* global CodeMirror CSSLint parserlib stylelint linterConfig */
|
||||
/* global CodeMirror linterConfig */
|
||||
'use strict';
|
||||
|
||||
CodeMirror.registerHelper('lint', 'csslint', code => new Promise(resolve => {
|
||||
CSSLint.onmessage = ({data}) => {
|
||||
resolve(
|
||||
data.map(({line, col, message, rule, type}) => line && {
|
||||
CodeMirror.registerHelper('lint', 'csslint', code =>
|
||||
linterConfig.invokeWorker({code, config: linterConfig.getCurrent()}).then(results =>
|
||||
results.map(({line, col: ch, message, rule, type: severity}) => line && {
|
||||
message,
|
||||
from: {line: line - 1, ch: col - 1},
|
||||
to: {line: line - 1, ch: col},
|
||||
from: {line: line - 1, ch: ch - 1},
|
||||
to: {line: line - 1, ch},
|
||||
rule: rule.id,
|
||||
severity: type
|
||||
}).filter(Boolean));
|
||||
};
|
||||
const config = deepCopy(linterConfig.getCurrent('csslint'));
|
||||
CSSLint.postMessage({action: 'verify', code, config});
|
||||
}));
|
||||
severity,
|
||||
}).filter(Boolean)));
|
||||
|
||||
CodeMirror.registerHelper('lint', 'stylelint', code =>
|
||||
stylelint.lint({
|
||||
code,
|
||||
config: deepCopy(linterConfig.getCurrent('stylelint')),
|
||||
}).then(({results}) => {
|
||||
if (!results[0]) {
|
||||
return [];
|
||||
}
|
||||
return results[0].warnings.map(warning => ({
|
||||
from: CodeMirror.Pos(warning.line - 1, warning.column - 1),
|
||||
to: CodeMirror.Pos(warning.line - 1, warning.column),
|
||||
message: warning.text
|
||||
linterConfig.invokeWorker({code, config: linterConfig.getCurrent()}).then(({results}) =>
|
||||
!results[0] && [] ||
|
||||
results[0].warnings.map(({line, column:ch, text, severity}) => ({
|
||||
from: {line: line - 1, ch: ch - 1},
|
||||
to: {line: line - 1, ch},
|
||||
message: text
|
||||
.replace('Unexpected ', '')
|
||||
.replace(/^./, firstLetter => firstLetter.toUpperCase())
|
||||
.replace(/\s*\([^(]+\)$/, ''), // strip the rule,
|
||||
rule: warning.text.replace(/^.*?\s*\(([^(]+)\)$/, '$1'),
|
||||
severity : warning.severity
|
||||
}));
|
||||
})
|
||||
);
|
||||
rule: text.replace(/^.*?\s*\(([^(]+)\)$/, '$1'),
|
||||
severity,
|
||||
}))));
|
||||
|
|
340
edit/lint.js
340
edit/lint.js
|
@ -1,5 +1,5 @@
|
|||
/* global CodeMirror messageBox */
|
||||
/* global editors makeSectionVisible showCodeMirrorPopup showHelp */
|
||||
/* global editors makeSectionVisible showCodeMirrorPopup showHelp hotkeyRerouter */
|
||||
/* global loadScript require CSSLint stylelint */
|
||||
/* global makeLink */
|
||||
'use strict';
|
||||
|
@ -20,8 +20,16 @@ var linterConfig = {
|
|||
csslint: 'editorCSSLintConfig',
|
||||
stylelint: 'editorStylelintConfig',
|
||||
},
|
||||
worker: {
|
||||
csslint: {path: '/vendor-overwrites/csslint/csslint-worker.js'},
|
||||
stylelint: {path: '/vendor-overwrites/stylelint/stylelint-bundle.min.js'},
|
||||
},
|
||||
allRuleIds: {
|
||||
csslint: null,
|
||||
stylelint: null,
|
||||
},
|
||||
|
||||
getDefault() {
|
||||
getName() {
|
||||
// some dirty hacks to override editor.linter getting from prefs
|
||||
const linter = prefs.get('editor.linter');
|
||||
if (linter && editors[0] && editors[0].getOption('mode') !== 'css') {
|
||||
|
@ -30,11 +38,11 @@ var linterConfig = {
|
|||
return linter;
|
||||
},
|
||||
|
||||
getCurrent(linter = linterConfig.getDefault()) {
|
||||
getCurrent(linter = linterConfig.getName()) {
|
||||
return this.fallbackToDefaults(this[linter] || {});
|
||||
},
|
||||
|
||||
getForCodeMirror(linter = linterConfig.getDefault()) {
|
||||
getForCodeMirror(linter = linterConfig.getName()) {
|
||||
return CodeMirror.lint && CodeMirror.lint[linter] ? {
|
||||
getAnnotations: CodeMirror.lint[linter],
|
||||
delay: prefs.get('editor.lintDelay'),
|
||||
|
@ -44,7 +52,7 @@ var linterConfig = {
|
|||
} : false;
|
||||
},
|
||||
|
||||
fallbackToDefaults(config, linter = linterConfig.getDefault()) {
|
||||
fallbackToDefaults(config, linter = linterConfig.getName()) {
|
||||
if (config && Object.keys(config).length) {
|
||||
if (linter === 'stylelint') {
|
||||
// always use default syntax because we don't expose it in config UI
|
||||
|
@ -56,33 +64,52 @@ var linterConfig = {
|
|||
}
|
||||
},
|
||||
|
||||
setLinter(linter = linterConfig.getDefault()) {
|
||||
setLinter(linter = linterConfig.getName()) {
|
||||
linter = linter.toLowerCase();
|
||||
linter = linter === 'csslint' || linter === 'stylelint' ? linter : '';
|
||||
if (linterConfig.getDefault() !== linter) {
|
||||
if (linterConfig.getName() !== linter) {
|
||||
prefs.set('editor.linter', linter);
|
||||
}
|
||||
return linter;
|
||||
},
|
||||
|
||||
findInvalidRules(config, linter = linterConfig.getDefault()) {
|
||||
const rules = linter === 'stylelint' ? config.rules : config;
|
||||
return new Promise(resolve => {
|
||||
if (linter === 'stylelint') {
|
||||
resolve(Object.keys(stylelint.rules));
|
||||
} else {
|
||||
CSSLint.onmessage = ({data}) =>
|
||||
resolve(data.map(rule => rule.id));
|
||||
CSSLint.postMessage({action: 'getRules'});
|
||||
invokeWorker(message) {
|
||||
const worker = linterConfig.worker[message.linter || linterConfig.getName()];
|
||||
if (!worker.queue) {
|
||||
worker.queue = [];
|
||||
worker.instance.onmessage = ({data}) => {
|
||||
worker.queue.shift().resolve(data);
|
||||
if (worker.queue.length) {
|
||||
worker.instance.postMessage(worker.queue[0].message);
|
||||
}
|
||||
}).then(allRules => {
|
||||
allRules = new Set(allRules);
|
||||
return Object.keys(rules).filter(rule => !allRules.has(rule));
|
||||
};
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
worker.queue.push({message, resolve});
|
||||
if (worker.queue.length === 1) {
|
||||
worker.instance.postMessage(message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getAllRuleIds(linter = linterConfig.getName()) {
|
||||
return Promise.resolve(
|
||||
this.allRuleIds[linter] ||
|
||||
this.invokeWorker({linter, action: 'getAllRuleIds'})
|
||||
.then(ids => (this.allRuleIds[linter] = ids.sort()))
|
||||
);
|
||||
},
|
||||
|
||||
findInvalidRules(config, linter = linterConfig.getName()) {
|
||||
return this.getAllRuleIds(linter).then(allRuleIds => {
|
||||
const allRuleIdsSet = new Set(allRuleIds);
|
||||
const rules = linter === 'stylelint' ? config.rules : config;
|
||||
return Object.keys(rules).filter(rule => !allRuleIdsSet.has(rule));
|
||||
});
|
||||
},
|
||||
|
||||
stringify(config = this.getCurrent()) {
|
||||
if (linterConfig.getDefault() === 'stylelint') {
|
||||
if (linterConfig.getName() === 'stylelint') {
|
||||
config.syntax = undefined;
|
||||
}
|
||||
return JSON.stringify(config, null, 2)
|
||||
|
@ -91,7 +118,7 @@ var linterConfig = {
|
|||
|
||||
save(config) {
|
||||
config = this.fallbackToDefaults(config);
|
||||
const linter = linterConfig.getDefault();
|
||||
const linter = linterConfig.getName();
|
||||
this[linter] = config;
|
||||
BG.chromeSync.setLZValue(this.storageName[linter], config);
|
||||
return config;
|
||||
|
@ -155,7 +182,7 @@ function initLint() {
|
|||
prefs.subscribe(['editor.linter'], updateLinter);
|
||||
}
|
||||
|
||||
function updateLinter({immediately, linter = linterConfig.getDefault()} = {}) {
|
||||
function updateLinter({immediately, linter = linterConfig.getName()} = {}) {
|
||||
if (!immediately) {
|
||||
debounce(updateLinter, 0, {immediately: true, linter});
|
||||
return;
|
||||
|
@ -358,17 +385,16 @@ function gotoLintIssue(event) {
|
|||
}
|
||||
|
||||
function showLintHelp() {
|
||||
const linter = linterConfig.getDefault();
|
||||
const linter = linterConfig.getName();
|
||||
const baseUrl = linter === 'stylelint'
|
||||
? 'https://stylelint.io/user-guide/rules/'
|
||||
// some CSSLint rules do not have a url
|
||||
: 'https://github.com/CSSLint/csslint/issues/535';
|
||||
let headerLink, template;
|
||||
if (linter === 'csslint') {
|
||||
const CSSLintRules = CSSLint.getRules();
|
||||
headerLink = makeLink('https://github.com/CSSLint/csslint/wiki/Rules-by-ID', 'CSSLint');
|
||||
template = ruleID => {
|
||||
const rule = CSSLintRules.find(rule => rule.id === ruleID);
|
||||
const rule = linterConfig.allRuleIds.csslint.find(rule => rule.id === ruleID);
|
||||
return rule &&
|
||||
$element({tag: 'li', appendChild: [
|
||||
$element({tag: 'b', appendChild: makeLink(rule.url || baseUrl, rule.name)}),
|
||||
|
@ -398,91 +424,79 @@ function showLintHelp() {
|
|||
);
|
||||
}
|
||||
|
||||
function showLinterErrorMessage(title, contents) {
|
||||
function showLinterErrorMessage(title, contents, popup) {
|
||||
messageBox({
|
||||
title,
|
||||
contents,
|
||||
className: 'danger center lint-config',
|
||||
buttons: [t('confirmOK')],
|
||||
});
|
||||
}
|
||||
|
||||
function setupLinterSettingsEvents(popup) {
|
||||
$('.save', popup).addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const linter = linterConfig.setLinter(event.target.dataset.linter);
|
||||
const json = tryJSONparse(popup.codebox.getValue());
|
||||
if (json) {
|
||||
showLinterErrorMessage(linter, t('linterJSONError'));
|
||||
popup.codebox.focus();
|
||||
return;
|
||||
}
|
||||
linterConfig.findInvalidRules(json, linter).then(invalid => {
|
||||
if (invalid.length) {
|
||||
showLinterErrorMessage(linter, [
|
||||
t('linterInvalidConfigError'),
|
||||
$element({
|
||||
tag: 'ul',
|
||||
appendChild: invalid.map(name =>
|
||||
$element({tag: 'li', textContent: name})),
|
||||
}),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
linterConfig.save(json);
|
||||
linterConfig.showSavedMessage();
|
||||
popup.codebox.markClean();
|
||||
popup.codebox.focus();
|
||||
});
|
||||
});
|
||||
$('.reset', popup).addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const linter = linterConfig.setLinter(event.target.dataset.linter);
|
||||
popup.codebox.setValue(linterConfig.stringify(linterConfig.defaults[linter] || {}));
|
||||
popup.codebox.focus();
|
||||
});
|
||||
$('.cancel', popup).addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
$('.dismiss').dispatchEvent(new Event('click'));
|
||||
});
|
||||
}).then(() => popup && popup.codebox.focus());
|
||||
}
|
||||
|
||||
function setupLinterPopup(config) {
|
||||
const linter = linterConfig.getDefault();
|
||||
const linter = linterConfig.getName();
|
||||
const linterTitle = linter === 'stylelint' ? 'Stylelint' : 'CSSLint';
|
||||
const defaultConfig = linterConfig.stringify(linterConfig.defaults[linter] || {});
|
||||
const title = t('linterConfigPopupTitle', linterTitle);
|
||||
const popup = showCodeMirrorPopup(title, null, {
|
||||
lint: false,
|
||||
extraKeys: {'Ctrl-Enter': save},
|
||||
hintOptions: {hint},
|
||||
});
|
||||
$('.contents', popup).appendChild(makeFooter());
|
||||
|
||||
function makeButton(className, text, options = {}) {
|
||||
return $element(Object.assign(options, {
|
||||
tag: 'button',
|
||||
const cm = popup.codebox;
|
||||
cm.focus();
|
||||
cm.setValue(config);
|
||||
cm.clearHistory();
|
||||
cm.markClean();
|
||||
cm.on('changes', updateButtonState);
|
||||
updateButtonState();
|
||||
|
||||
hotkeyRerouter.setState(false);
|
||||
window.addEventListener('closeHelp', function _() {
|
||||
window.removeEventListener('closeHelp', _);
|
||||
hotkeyRerouter.setState(true);
|
||||
});
|
||||
|
||||
loadScript([
|
||||
'/vendor/codemirror/mode/javascript/javascript.js',
|
||||
'/vendor/codemirror/addon/lint/json-lint.js',
|
||||
'/vendor/jsonlint/jsonlint.js'
|
||||
]).then(() => {
|
||||
cm.setOption('mode', 'application/json');
|
||||
cm.setOption('lint', 'json');
|
||||
});
|
||||
|
||||
function makeFooter() {
|
||||
const makeButton = (className, onclick, text, options = {}) =>
|
||||
$element(Object.assign(options, {
|
||||
className,
|
||||
onclick,
|
||||
tag: 'button',
|
||||
type: 'button',
|
||||
textContent: t(text),
|
||||
dataset: {linter}
|
||||
}));
|
||||
}
|
||||
function makeLink(url, textContent) {
|
||||
return $element({tag: 'a', target: '_blank', href: url, textContent});
|
||||
}
|
||||
|
||||
const title = t('linterConfigPopupTitle', linterTitle);
|
||||
const contents = $element({
|
||||
return $element({
|
||||
appendChild: [
|
||||
$element({
|
||||
tag: 'p',
|
||||
appendChild: [
|
||||
t('linterRulesLink') + ' ',
|
||||
makeLink(
|
||||
linter === 'stylelint'
|
||||
$element({
|
||||
tag: 'a',
|
||||
target: '_blank',
|
||||
href: linter === 'stylelint'
|
||||
? 'https://stylelint.io/user-guide/rules/'
|
||||
: 'https://github.com/CSSLint/csslint/wiki/Rules-by-ID',
|
||||
linterTitle
|
||||
),
|
||||
textContent: linterTitle
|
||||
}),
|
||||
linter === 'csslint' ? ' ' + t('linterCSSLintSettings') : ''
|
||||
]
|
||||
}),
|
||||
makeButton('save', 'styleSaveLabel', {disabled: true}),
|
||||
makeButton('cancel', 'confirmCancel'),
|
||||
makeButton('reset', 'genericResetLabel', {title: t('linterResetMessage')}),
|
||||
makeButton('save', save, 'styleSaveLabel', {title: 'Ctrl-Enter'}),
|
||||
makeButton('cancel', cancel, 'confirmClose'),
|
||||
makeButton('reset', reset, 'genericResetLabel', {title: t('linterResetMessage')}),
|
||||
$element({
|
||||
tag: 'span',
|
||||
className: 'saved-message',
|
||||
|
@ -490,58 +504,122 @@ function setupLinterPopup(config) {
|
|||
})
|
||||
]
|
||||
});
|
||||
const popup = showCodeMirrorPopup(title, contents, {lint: false});
|
||||
contents.parentNode.appendChild(contents);
|
||||
popup.codebox.focus();
|
||||
popup.codebox.setValue(config);
|
||||
popup.codebox.clearHistory();
|
||||
popup.codebox.markClean();
|
||||
popup.codebox.on('change', cm => {
|
||||
$('.save', popup).disabled = cm.isClean();
|
||||
});
|
||||
setupLinterSettingsEvents(popup);
|
||||
loadScript([
|
||||
'/vendor/codemirror/mode/javascript/javascript.js',
|
||||
'/vendor/codemirror/addon/lint/json-lint.js',
|
||||
'/vendor/jsonlint/jsonlint.js'
|
||||
]).then(() => {
|
||||
popup.codebox.setOption('mode', 'application/json');
|
||||
popup.codebox.setOption('lint', 'json');
|
||||
});
|
||||
}
|
||||
|
||||
function loadLinterAssets(name = linterConfig.getDefault()) {
|
||||
if (!name) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return loadLibrary().then(loadAddon);
|
||||
|
||||
function loadLibrary() {
|
||||
if (name === 'csslint' && !window.CSSLint) {
|
||||
window.CSSLint = new Worker('/vendor-overwrites/csslint/csslint-worker.js');
|
||||
return loadScript([
|
||||
'/edit/lint-defaults-csslint.js'
|
||||
]);
|
||||
}
|
||||
if (name === 'stylelint' && !window.stylelint) {
|
||||
return loadScript([
|
||||
'/vendor-overwrites/stylelint/stylelint-bundle.min.js',
|
||||
'/edit/lint-defaults-stylelint.js'
|
||||
]).then(() => (window.stylelint = require('stylelint')));
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function loadAddon() {
|
||||
if (CodeMirror.lint) {
|
||||
function save(event) {
|
||||
if (event instanceof Event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const json = tryJSONparse(cm.getValue());
|
||||
if (!json) {
|
||||
showLinterErrorMessage(linter, t('linterJSONError'), popup);
|
||||
cm.focus();
|
||||
}
|
||||
linterConfig.findInvalidRules(json, linter).then(invalid => {
|
||||
if (invalid.length) {
|
||||
showLinterErrorMessage(linter, [
|
||||
t('linterInvalidConfigError'),
|
||||
$element({tag: 'ul', appendChild: invalid.map(name =>
|
||||
$element({tag: 'li', textContent: name})),
|
||||
}),
|
||||
], popup);
|
||||
return;
|
||||
}
|
||||
return loadScript([
|
||||
linterConfig.setLinter(linter);
|
||||
linterConfig.save(json);
|
||||
linterConfig.showSavedMessage();
|
||||
cm.markClean();
|
||||
cm.focus();
|
||||
updateButtonState();
|
||||
});
|
||||
}
|
||||
|
||||
function reset(event) {
|
||||
event.preventDefault();
|
||||
if (linterConfig.getName() !== linter) {
|
||||
linterConfig.setLinter(linter);
|
||||
}
|
||||
cm.setValue(defaultConfig);
|
||||
cm.focus();
|
||||
updateButtonState();
|
||||
}
|
||||
|
||||
function cancel(event) {
|
||||
event.preventDefault();
|
||||
$('.dismiss').dispatchEvent(new Event('click'));
|
||||
}
|
||||
|
||||
function updateButtonState() {
|
||||
$('.save', popup).disabled = cm.isClean();
|
||||
$('.reset', popup).disabled = cm.getValue() === defaultConfig;
|
||||
$('.cancel', popup).textContent = t(cm.isClean() ? 'confirmClose' : 'confirmCancel');
|
||||
}
|
||||
|
||||
function hint(cm) {
|
||||
return Promise.all([
|
||||
linterConfig.getAllRuleIds(linter),
|
||||
linter !== 'stylelint' || hint.allOptions ||
|
||||
linterConfig.invokeWorker({action: 'getAllRuleOptions', linter})
|
||||
.then(options => (hint.allOptions = options)),
|
||||
])
|
||||
.then(([ruleIds, options]) => {
|
||||
const cursor = cm.getCursor();
|
||||
const {start, end, string, type, state: {lexical}} = cm.getTokenAt(cursor);
|
||||
const {line, ch} = cursor;
|
||||
|
||||
const quoted = string.startsWith('"');
|
||||
const leftPart = string.slice(quoted ? 1 : 0, ch - start).trim();
|
||||
const depth = getLexicalDepth(lexical);
|
||||
|
||||
const search = cm.getSearchCursor(/"([-\w]+)"/, {line, ch: start - 1});
|
||||
let [, prevWord] = search.find(true) || [];
|
||||
let words = [];
|
||||
|
||||
if (depth === 1 && linter === 'stylelint') {
|
||||
words = quoted ? ['rules'] : [];
|
||||
} else if ((depth === 1 || depth === 2) && type && type.includes('property')) {
|
||||
words = ruleIds;
|
||||
} else if (depth === 2 || depth === 3 && lexical.type === ']') {
|
||||
words = !quoted ? ['true', 'false', 'null'] :
|
||||
ruleIds.includes(prevWord) && (options[prevWord] || [])[0] || [];
|
||||
} else if (depth === 4 && prevWord === 'severity') {
|
||||
words = ['error', 'warning'];
|
||||
} else if (depth === 4) {
|
||||
words = ['ignore', 'ignoreAtRules', 'except', 'severity'];
|
||||
} else if (depth === 5 && lexical.type === ']' && quoted) {
|
||||
while (prevWord && !ruleIds.includes(prevWord)) {
|
||||
prevWord = (search.find(true) || [])[1];
|
||||
}
|
||||
words = (options[prevWord] || []).slice(-1)[0] || ruleIds;
|
||||
}
|
||||
return {
|
||||
list: words.filter(word => word.startsWith(leftPart)),
|
||||
from: {line, ch: start + (quoted ? 1 : 0)},
|
||||
to: {line, ch: string.endsWith('"') ? end - 1 : end},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function getLexicalDepth(lexicalState) {
|
||||
let depth = 0;
|
||||
while ((lexicalState = lexicalState.prev)) {
|
||||
depth++;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
function loadLinterAssets(name = linterConfig.getName()) {
|
||||
const worker = linterConfig.worker[name];
|
||||
return !name || !worker || worker.instance ? Promise.resolve() :
|
||||
loadScript((worker.instance ? [] : [
|
||||
(worker.instance = new Worker(worker.path)),
|
||||
`/edit/lint-defaults-${name}.js`,
|
||||
]).concat(CodeMirror.lint ? [] : [
|
||||
'/vendor/codemirror/addon/lint/lint.css',
|
||||
'/msgbox/msgbox.css',
|
||||
'/vendor/codemirror/addon/lint/lint.js',
|
||||
'/edit/lint-codemirror-helper.js',
|
||||
'/msgbox/msgbox.js'
|
||||
]);
|
||||
}
|
||||
]));
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ function createSourceEditor(style) {
|
|||
update();
|
||||
|
||||
function update() {
|
||||
linterEl.value = linterConfig.getDefault();
|
||||
linterEl.value = linterConfig.getName();
|
||||
|
||||
const cssLintOption = linterEl.querySelector('[value="csslint"]');
|
||||
if (cm.getOption('mode') !== 'css') {
|
||||
|
|
|
@ -15,6 +15,7 @@ function messageBox({
|
|||
if (onshow) {
|
||||
onshow(messageBox.element);
|
||||
}
|
||||
messageBox.element.focus();
|
||||
return new Promise(_resolve => {
|
||||
messageBox.resolve = _resolve;
|
||||
});
|
||||
|
|
|
@ -10950,14 +10950,15 @@ if (!CSSLint.suppressUsoVarError) {
|
|||
});
|
||||
}
|
||||
|
||||
self.onmessage = ({data: {action, code, config}}) => {
|
||||
self.onmessage = ({data: {action = 'run', code, config}}) => {
|
||||
switch (action) {
|
||||
|
||||
case 'getRules':
|
||||
self.postMessage(CSSLint.getRules());
|
||||
case 'getAllRuleIds':
|
||||
// the functions are non-tranferable and we need only an id
|
||||
self.postMessage(CSSLint.getRules().map(rule => rule.id));
|
||||
return;
|
||||
|
||||
case 'verify':
|
||||
case 'run':
|
||||
Object.defineProperty(config, 'errors', {get: () => 0, set: () => 0});
|
||||
config['uso-vars'] = 1;
|
||||
self.postMessage(CSSLint.verify(code, config).messages.map(m => {
|
||||
|
|
|
@ -1673,3 +1673,59 @@ N,R,y-N,"inline"])):(K.lastIndex=G+1,K.test(E),y=0===K.lastIndex?E.length-1:K.la
|
|||
Object.keys(f).forEach(function(a){d[a]=f[a]});return d}if(a&&f)return k(a)(f);if("function"!==typeof a)throw new TypeError("need wrapper function");Object.keys(a).forEach(function(c){d[c]=a[c]});return d}l.exports=k},{}],611:[function(a,l,g){var k=a("fs"),h=a("path"),f=a("mkdirp");l.exports=function(a,c,b){var d=h.dirname(a);k.exists(d,function(g){g?k.writeFile(a,c,b):f(d,function(d){if(d)return b(d);k.writeFile(a,c,b)})})};l.exports.sync=function(a,c){var b=h.dirname(a);k.existsSync(b)||f.sync(b);
|
||||
k.writeFileSync(a,c)};l.exports.stream=function(a){var c=h.dirname(a);k.existsSync(c)||f.sync(c);return k.createWriteStream(a)}},{fs:1,mkdirp:173,path:14}],stylelint:[function(a,l,g){g=a("./utils/checkAgainstRule");var k=a("./createPlugin"),h=a("./createStylelint"),f=a("./formatters"),d=a("./postcssPlugin"),c=a("./utils/report"),b=a("./utils/ruleMessages"),p=a("./rules"),m=a("./standalone");a=a("./utils/validateOptions");d.utils={report:c,ruleMessages:b,validateOptions:a,checkAgainstRule:g};d.lint=
|
||||
m;d.rules=p;d.formatters=f;d.createPlugin=k;d.createLinter=h;l.exports=d},{"./createPlugin":334,"./createStylelint":335,"./formatters":338,"./postcssPlugin":346,"./rules":429,"./standalone":520,"./utils/checkAgainstRule":529,"./utils/report":592,"./utils/ruleMessages":593,"./utils/validateOptions":595}]},{},[]);
|
||||
|
||||
(() => {
|
||||
const stylelint = require('stylelint');
|
||||
|
||||
self.onmessage = ({data: {action = 'run', code, config}}) => {
|
||||
switch (action) {
|
||||
case 'getAllRuleIds':
|
||||
// the functions are non-tranferable
|
||||
self.postMessage(Object.keys(stylelint.rules));
|
||||
return;
|
||||
case 'getAllRuleOptions':
|
||||
self.postMessage(getAllRuleOptions());
|
||||
return;
|
||||
case 'run':
|
||||
stylelint.lint({code, config}).then(results =>
|
||||
self.postMessage(results));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
function getAllRuleOptions() {
|
||||
const options = {};
|
||||
const rxPossible = /\bpossible:("(?:[^"]*?)"|\[(?:[^\]]*?)\]|\{(?:[^}]*?)\})/g;
|
||||
const rxString = /"([-\w\s]{3,}?)"/g;
|
||||
for (const id of Object.keys(stylelint.rules)) {
|
||||
const ruleCode = String(stylelint.rules[id]);
|
||||
const sets = [];
|
||||
let m, mStr;
|
||||
while ((m = rxPossible.exec(ruleCode))) {
|
||||
const possible = m[1];
|
||||
const set = [];
|
||||
while ((mStr = rxString.exec(possible))) {
|
||||
const s = mStr[1];
|
||||
if (s.includes(' ')) {
|
||||
set.push(...s.split(/\s+/));
|
||||
} else {
|
||||
set.push(s);
|
||||
}
|
||||
}
|
||||
if (possible.includes('ignoreAtRules')) {
|
||||
set.push('ignoreAtRules');
|
||||
}
|
||||
if (possible.includes('ignoreShorthands')) {
|
||||
set.push('ignoreShorthands');
|
||||
}
|
||||
if (set.length) {
|
||||
sets.push(set);
|
||||
}
|
||||
}
|
||||
if (sets.length) {
|
||||
options[id] = sets;
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
})();
|
||||
|
|
Loading…
Reference in New Issue
Block a user