From 99512da9da75bf7a14f5b2ea2df5ce6f0fb6eadf Mon Sep 17 00:00:00 2001 From: tophf Date: Mon, 27 Nov 2017 17:02:23 +0300 Subject: [PATCH] use CSSLint in a web werkker --- edit/edit.css | 7 ++- edit/lint-codemirror-helper.js | 46 +++++------------ edit/lint.js | 52 +++++++++---------- vendor-overwrites/csslint/csslint-worker.js | 57 ++++++++++++--------- 4 files changed, 75 insertions(+), 87 deletions(-) diff --git a/edit/edit.css b/edit/edit.css index d2073151..746f7b6a 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -40,6 +40,8 @@ body { box-shadow: 0 0 3rem -1.2rem black; box-sizing: border-box; z-index: 10; + display: flex; + flex-direction: column; } #header h1 { margin-top: 0; @@ -468,9 +470,9 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar } /************ lint ************/ -#lint > div { +#lint { overflow-y: auto; -} + overflow-x: hidden;} #lint table { font-size: 100%; border-spacing: 0; @@ -505,6 +507,7 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar } #lint td[role="message"] { text-align: left; + white-space: nowrap; } #message-box.center.lint-config #message-box-contents { text-align: left; diff --git a/edit/lint-codemirror-helper.js b/edit/lint-codemirror-helper.js index 7dd12503..be0a61ee 100644 --- a/edit/lint-codemirror-helper.js +++ b/edit/lint-codemirror-helper.js @@ -1,38 +1,20 @@ /* global CodeMirror CSSLint parserlib stylelint linterConfig */ 'use strict'; -CodeMirror.registerHelper('lint', 'csslint', code => { - if (!CSSLint.suppressUsoVarError) { - CSSLint.suppressUsoVarError = true; - parserlib.css.Tokens[parserlib.css.Tokens.COMMENT].hide = false; - const isUsoVar = ({value}) => value.startsWith('/*[[') && value.endsWith(']]*/'); - CSSLint.addRule({ - id: 'uso-vars', - init(parser, reporter) { - parser.addListener('error', function ({message, line, col}) { - if (!isUsoVar(this._tokenStream._token)) { - const {_lt, _ltIndex: i} = this._tokenStream; - if (i < 2 || !_lt.slice(0, i - 1).reverse().some(isUsoVar)) { - reporter.error(message, line, col); - } - } - }); - }, - }); - } - const rules = deepCopy(linterConfig.getCurrent('csslint')); - Object.defineProperty(rules, 'errors', {get: () => 0, set: () => 0}); - rules['uso-vars'] = 1; - return CSSLint.verify(code, rules).messages - .map(({line, col, message, rule, type}) => line && { - message, - from: {line: line - 1, ch: col - 1}, - to: {line: line - 1, ch: col}, - rule: rule.id, - severity: type - }) - .filter(Boolean); -}); +CodeMirror.registerHelper('lint', 'csslint', code => new Promise(resolve => { + CSSLint.onmessage = ({data}) => { + resolve( + data.map(({line, col, message, rule, type}) => line && { + message, + from: {line: line - 1, ch: col - 1}, + to: {line: line - 1, ch: col}, + rule: rule.id, + severity: type + }).filter(Boolean)); + }; + const config = deepCopy(linterConfig.getCurrent('csslint')); + CSSLint.postMessage({action: 'verify', code, config}); +})); CodeMirror.registerHelper('lint', 'stylelint', code => stylelint.lint({ diff --git a/edit/lint.js b/edit/lint.js index 8b4f6f3c..f3039c00 100644 --- a/edit/lint.js +++ b/edit/lint.js @@ -38,6 +38,9 @@ var linterConfig = { return CodeMirror.lint && CodeMirror.lint[linter] ? { getAnnotations: CodeMirror.lint[linter], delay: prefs.get('editor.lintDelay'), + onUpdateLinting(annotationsNotSorted, annotations, cm) { + updateLintReport(cm, 0); + }, } : false; }, @@ -64,12 +67,18 @@ var linterConfig = { findInvalidRules(config, linter = linterConfig.getDefault()) { const rules = linter === 'stylelint' ? config.rules : config; - const allRules = new Set( - linter === 'stylelint' - ? Object.keys(stylelint.rules) - : CSSLint.getRules().map(rule => rule.id) - ); - return Object.keys(rules).filter(rule => !allRules.has(rule)); + 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'}); + } + }).then(allRules => { + allRules = new Set(allRules); + return Object.keys(rules).filter(rule => !allRules.has(rule)); + }); }, stringify(config = this.getCurrent()) { @@ -140,7 +149,6 @@ function initLint() { $('#lint-help').addEventListener('click', showLintHelp); $('#lint').addEventListener('click', gotoLintIssue); $('#linter-settings').addEventListener('click', linterConfig.openOnClick); - window.addEventListener('resize', resizeLintReport); updateLinter(); linterConfig.watchStorage(); @@ -332,22 +340,6 @@ function renderLintReport(someBlockChanged) { $('#issue-count').textContent = issueCount; container.replaceChild(newContent, content); container.classList.toggle('hidden', !newContent.children.length); - resizeLintReport(); - } -} - -function resizeLintReport() { - // subtracted value to prevent scrollbar - const magicBuffer = 20; - const content = $('#lint table'); - if (content) { - const bounds = content.getBoundingClientRect(); - const newMaxHeight = bounds.bottom <= window.innerHeight ? '' : - // subtract out a bit of padding or the vertical scrollbar extends beyond the viewport - (window.innerHeight - bounds.top - magicBuffer) + 'px'; - if (newMaxHeight !== content.style.maxHeight) { - content.parentNode.style.maxHeight = newMaxHeight; - } } } @@ -421,7 +413,11 @@ function setupLinterSettingsEvents(popup) { const linter = linterConfig.setLinter(event.target.dataset.linter); const json = tryJSONparse(popup.codebox.getValue()); if (json) { - const invalid = linterConfig.findInvalidRules(json, linter); + showLinterErrorMessage(linter, t('linterJSONError')); + popup.codebox.focus(); + return; + } + linterConfig.findInvalidRules(json, linter).then(invalid => { if (invalid.length) { showLinterErrorMessage(linter, [ t('linterInvalidConfigError'), @@ -436,10 +432,8 @@ function setupLinterSettingsEvents(popup) { linterConfig.save(json); linterConfig.showSavedMessage(); popup.codebox.markClean(); - } else { - showLinterErrorMessage(linter, t('linterJSONError')); - } - popup.codebox.focus(); + popup.codebox.focus(); + }); }); $('.reset', popup).addEventListener('click', event => { event.preventDefault(); @@ -524,8 +518,8 @@ function loadLinterAssets(name = linterConfig.getDefault()) { function loadLibrary() { if (name === 'csslint' && !window.CSSLint) { + window.CSSLint = new Worker('/vendor-overwrites/csslint/csslint-worker.js'); return loadScript([ - '/vendor-overwrites/csslint/csslint-worker.js', '/edit/lint-defaults-csslint.js' ]); } diff --git a/vendor-overwrites/csslint/csslint-worker.js b/vendor-overwrites/csslint/csslint-worker.js index 02428485..679a7667 100644 --- a/vendor-overwrites/csslint/csslint-worker.js +++ b/vendor-overwrites/csslint/csslint-worker.js @@ -10931,31 +10931,40 @@ CSSLint.addFormatter({ } }); -/* - * Web worker for CSSLint - */ +if (!CSSLint.suppressUsoVarError) { + CSSLint.suppressUsoVarError = true; + parserlib.css.Tokens[parserlib.css.Tokens.COMMENT].hide = false; + const isUsoVar = ({value}) => value.startsWith('/*[[') && value.endsWith(']]*/'); + CSSLint.addRule({ + id: 'uso-vars', + init(parser, reporter) { + parser.addListener('error', function ({message, line, col}) { + if (!isUsoVar(this._tokenStream._token)) { + const {_lt, _ltIndex: i} = this._tokenStream; + if (i < 2 || !_lt.slice(0, i - 1).reverse().some(isUsoVar)) { + reporter.error(message, line, col); + } + } + }); + }, + }); +} -/* global self, JSON */ +self.onmessage = ({data: {action, code, config}}) => { + switch (action) { -// message indicates to start linting -self.onmessage = function(event) { - "use strict"; - var data = event.data, - message, - text, - ruleset, - results; + case 'getRules': + self.postMessage(CSSLint.getRules()); + return; - try { - message = JSON.parse(data); - text = message.text; - ruleset = message.ruleset; - } catch (ex) { - text = data; - } - - results = CSSLint.verify(text, ruleset); - - // Not all browsers support structured clone, so JSON stringify results - self.postMessage(JSON.stringify(results)); + case 'verify': + Object.defineProperty(config, 'errors', {get: () => 0, set: () => 0}); + config['uso-vars'] = 1; + self.postMessage(CSSLint.verify(code, config).messages.map(m => { + // the functions are non-tranferable and we need only an id + m.rule = {id: m.rule.id}; + return m; + })); + return; + } };