diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 96eeb115..abb4e467 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -506,6 +506,23 @@ "message": "Remove section", "description": "Label for the button to remove a section" }, + "setStylelintLink": { + "message": "Get a full list of rules from $link$", + "description": "Stylelint rules link", + "placeholders": { + "link": { + "content": "$1" + } + } + }, + "setStylelintRules": { + "message": "Set stylelint rules", + "description": "Stylelint popup header" + }, + "setStylelintError": { + "message": "Invalid JSON format", + "description": "Stylelint invalid JSON message" + }, "shortcuts": { "message": "Shortcuts", "description": "Go to shortcut configuration" diff --git a/edit.html b/edit.html index 87612e11..fa2d29eb 100644 --- a/edit.html +++ b/edit.html @@ -198,6 +198,9 @@ + + +

:

@@ -219,6 +222,9 @@ + + + diff --git a/edit/edit.css b/edit/edit.css index 49ff5fcd..e4b9a2cc 100644 --- a/edit/edit.css +++ b/edit/edit.css @@ -77,16 +77,19 @@ input[type="checkbox"] { h2 .svg-icon, label .svg-icon { margin-top: -1px; } -.svg-icon.info { +.svg-icon.info, +.svg-icon.settings { width: 14px; height: 16px; } .svg-icon:hover, -.svg-icon.info { +.svg-icon.info, +.svg-icon.settings { fill: #666; } .svg-icon, -.svg-icon.info:hover { +.svg-icon.info:hover, +.svg-icon.settings:hover { fill: #000; } #enabled { @@ -365,11 +368,28 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar max-height: calc(100vh - 8rem); overflow-y: auto; } +#help-popup .settings { + min-width: 500px; + min-height: 200px; + max-width: 48vw; +} #help-popup .dismiss { position: absolute; right: 4px; top: .5em; } +#help-popup .error { + display: none; + margin-left: 10px; + color: #900; + font-weight: bold; +} +#help-popup .error a { + color: #f00; +} +#help-popup .error.show { + display: inline-block; +} .keymap-list { font-size: 85%; @@ -417,7 +437,6 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar border-spacing: 0; margin-bottom: 1rem; line-height: 1.0; - width: 245px; } #lint table:last-child { margin-bottom: 0; diff --git a/edit/edit.js b/edit/edit.js index 1b15e1dd..983813f5 100644 --- a/edit/edit.js +++ b/edit/edit.js @@ -1,5 +1,5 @@ /* eslint brace-style: 0, operator-linebreak: 0 */ -/* global CodeMirror exports css_beautify parserlib CSSLint initLintHooks setLinter updateLintReport renderLintReport */ +/* global CodeMirror exports css_beautify parserlib CSSLint initLint setLinter updateLintReport renderLintReport updateLinter */ 'use strict'; let styleId = null; @@ -168,7 +168,7 @@ function initCodeMirror() { matchBrackets: true, highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true}, hintOptions: {}, - lint: setLinter('csslint'), + lint: setLinter(prefs.get('editor.linter')), lintReportDelay: prefs.get('editor.lintReportDelay'), styleActiveLine: true, theme: 'default', @@ -351,15 +351,7 @@ function acmeEventListener(event) { break; case 'linter': if (value !== null && editors.length) { - if (prefs.get(el.id) !== value) { - prefs.set(el.id, value || 'csslint'); - } - editors.forEach(cm => { - console.log('set linter to', value); - cm.setOption('lint', setLinter(value || 'csslint')); - }); - // CodeMirror.signal(editors.lastActive || editors[0], "change"); - // save(); + updateLinter(value); } break; } @@ -1251,7 +1243,7 @@ function initHooks() { document.getElementById('sections-help').addEventListener('click', showSectionHelp, false); document.getElementById('keyMap-help').addEventListener('click', showKeyMapHelp, false); document.getElementById('cancel-button').addEventListener('click', goBackToManage); - initLintHooks(); + initLint(); if (!FIREFOX) { $$([ diff --git a/edit/lint.js b/edit/lint.js index a7d01ee0..a1c575bb 100644 --- a/edit/lint.js +++ b/edit/lint.js @@ -1,15 +1,24 @@ -/* global CodeMirror CSSLint editors makeSectionVisible showHelp */ +/* global CodeMirror CSSLint editors makeSectionVisible showHelp stylelintDefaultConfig BG */ 'use strict'; -function initLintHooks() { +function initLint() { document.getElementById('lint-help').addEventListener('click', showLintHelp); document.getElementById('lint').addEventListener('click', gotoLintIssue); window.addEventListener('resize', resizeLintReport); + document.getElementById('stylelint-settings').addEventListener('click', openStylelintSettings); // touch devices don't have onHover events so the element we'll be toggled via clicking (touching) if ('ontouchstart' in document.body) { document.querySelector('#lint h2').addEventListener('click', toggleLintReport); } + BG.chromeLocal.getValue('editorStylelintRules').then(rules => setStylelintRules(rules)); +} + +function setStylelintRules(rules = {}) { + if (Object.keys(rules).length === 0) { + rules = deepCopy(stylelintDefaultConfig.rules); + } + BG.chromeLocal.setValue('editorStylelintRules', rules); } function setLinter(name) { @@ -19,6 +28,18 @@ function setLinter(name) { }; } +function updateLinter(name = 'csslint') { + if (prefs.get('editor.linter') !== name) { + prefs.set('editor.linter', name); + } + editors.forEach(cm => { + cm.setOption('lint', setLinter(name)); + updateLintReport(cm, 200); + }); + $('#stylelint-settings').style.display = name === 'stylelint' ? + 'inline-block' : 'none'; +} + function updateLintReport(cm, delay) { if (delay === 0) { // immediately show pending csslint/stylelint messages in onbeforeunload and save @@ -178,3 +199,51 @@ function showLintHelp() { } return showHelp(t('issues'), header + list + ''); } + +function setupStylelintSettingsEvents() { + let timer; + $('#help-popup .save').addEventListener('click', () => { + try { + setStylelintRules(JSON.parse($('#help-popup textarea').value).rules); + // it is possible to have stylelint rules popup open & switch to csslint + if (prefs.get('editor.linter') === 'stylelint') { + updateLinter('stylelint'); + } + } catch (err) { + $('#help-popup .error').classList.add('show'); + clearTimeout(timer); + timer = setTimeout(() => { + // popup may be closed at this point + const error = $('#help-popup .error'); + if (error) { + error.classList.remove('show'); + } + }, 3000); + } + return false; + }); + $('#help-popup .reset').addEventListener('click', () => { + setStylelintRules(); + $('#help-popup .settings').value = JSON.stringify({rules: stylelintDefaultConfig.rules}, null, 2); + if (prefs.get('editor.linter') === 'stylelint') { + updateLinter('stylelint'); + } + return false; + }); +} + +function openStylelintSettings() { + BG.chromeLocal.getValue('editorStylelintRules').then((rules = stylelintDefaultConfig.rules) => { + const link = 'Stylelint'; + const text = JSON.stringify({rules: rules}, null, 2); + const content = ` +

${t('setStylelintLink', link)}

+   + + + ${t('setStylelintError')} (JSONLint) + `; + showHelp(t('setStylelintRules'), content); + setupStylelintSettingsEvents(); + }); +} diff --git a/vendor-overwrites/codemirror/addon/lint/linter.js b/vendor-overwrites/codemirror/addon/lint/linter.js index 36751c52..a48e01fe 100644 --- a/vendor-overwrites/codemirror/addon/lint/linter.js +++ b/vendor-overwrites/codemirror/addon/lint/linter.js @@ -66,26 +66,34 @@ CodeMirror.registerHelper("lint", "stylelint", function(text) { let found = []; const stylelint = require('stylelint').lint; if (stylelint) { - return stylelint({ - code: text, - // stylelintConfig stored in stylelint-config.js & loaded by edit.html - config: stylelintConfig - }).then(output => { - const warnings = output.results.length ? output.results[0].warnings : [], - len = warnings.length; - let i, warning; - if (len) { - for (i = 0; i < len; i++) { - warning = warnings[i]; - found.push({ - from: CodeMirror.Pos(warning.line - 1, warning.column - 1), - to: CodeMirror.Pos(warning.line - 1, warning.column), - message: warning.text, - severity : warning.severity - }); - } + return BG.chromeLocal.getValue('editorStylelintRules').then((rules = stylelintDefaultConfig.rules) => { + // stylelintDefaultConfig stored in stylelint-config.js & loaded by edit.html + if (Object.keys(rules).length === 0) { + rules = stylelintDefaultConfig.rules; } - return found; + return stylelint({ + code: text, + config: { + syntax: stylelintDefaultConfig.syntax, + rules: rules + } + }).then(output => { + const warnings = output.results.length ? output.results[0].warnings : [], + len = warnings.length; + let i, warning; + if (len) { + for (i = 0; i < len; i++) { + warning = warnings[i]; + found.push({ + from: CodeMirror.Pos(warning.line - 1, warning.column - 1), + to: CodeMirror.Pos(warning.line - 1, warning.column), + message: warning.text, + severity : warning.severity + }); + } + } + return found; + }); }); } return found; diff --git a/vendor-overwrites/codemirror/addon/lint/stylelint-config.js b/vendor-overwrites/codemirror/addon/lint/stylelint-config.js index b1b8c517..2b8a2a6a 100644 --- a/vendor-overwrites/codemirror/addon/lint/stylelint-config.js +++ b/vendor-overwrites/codemirror/addon/lint/stylelint-config.js @@ -1,4 +1,4 @@ -const stylelintConfig = { +const stylelintDefaultConfig = { // 'sugarss' is a indent-based syntax like Sass or Stylus // ref: https://github.com/postcss/postcss#syntaxes syntax: 'sugarss',