Add stylelint settings popup & cleanup

This commit is contained in:
Rob Garrison 2017-08-17 14:08:48 -05:00
parent 07dce1006d
commit 06cd78b643
7 changed files with 149 additions and 38 deletions

View File

@ -506,6 +506,23 @@
"message": "Remove section", "message": "Remove section",
"description": "Label for the button to remove a 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": { "shortcuts": {
"message": "Shortcuts", "message": "Shortcuts",
"description": "Go to shortcut configuration" "description": "Go to shortcut configuration"

View File

@ -198,6 +198,9 @@
<option value="csslint" selected>CSSLint</option> <option value="csslint" selected>CSSLint</option>
<option value="stylelint">stylelint</option> <option value="stylelint">stylelint</option>
</select> </select>
<svg id="stylelint-settings" class="svg-icon settings">
<use xlink:href="#svg-icon-settings"/>
</svg>
</section> </section>
<section id="lint"><h2 i18n-text="issues">: <span id="issue-count"></span><svg id="lint-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2><div></div></section> <section id="lint"><h2 i18n-text="issues">: <span id="issue-count"></span><svg id="lint-help" class="svg-icon info"><use xlink:href="#svg-icon-help"/></svg></h2><div></div></section>
</div> </div>
@ -219,6 +222,9 @@
<symbol id="svg-icon-close" height="16" width="12" viewBox="0 0 12 16"> <symbol id="svg-icon-close" height="16" width="12" viewBox="0 0 12 16">
<path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48z"></path> <path fill-rule="evenodd" d="M7.48 8l3.75 3.75-1.48 1.48L6 9.48l-3.75 3.75-1.48-1.48L4.52 8 .77 4.25l1.48-1.48L6 6.52l3.75-3.75 1.48 1.48z"></path>
</symbol> </symbol>
<symbol id="svg-icon-settings" height="12" width="12" viewBox="0 0 16 16">
<path d="M16 9.45V6.52l-1.8-.3c-.14-.5-.34-.95-.56-1.35L14.7 3.4l-2.07-2.1-1.5 1.03c-.43-.24-.9-.43-1.37-.56L9.46 0H6.54l-.3 1.8c-.5.13-.94.33-1.36.55L3.4 1.32 1.3 3.4l1.07 1.45c-.24.45-.44.92-.57 1.42L0 6.53v2.95l1.8.3c.14.52.33.95.57 1.37l-1.07 1.5 2.06 2.08 1.5-1.07c.44.24.9.45 1.4.57l.3 1.77H9.5l.32-1.8c.47-.13.95-.32 1.34-.56l1.5 1.06 2.07-2.05-1.03-1.5c.24-.45.44-.9.56-1.36L16 9.44v-.02zm-8 1.6C6.3 11.05 4.93 9.7 4.93 8S6.33 4.9 8 4.9s3.06 1.4 3.06 3.1S9.7 11.04 8 11.04z"/>
</symbol>
</svg> </svg>
</body> </body>

View File

@ -77,16 +77,19 @@ input[type="checkbox"] {
h2 .svg-icon, label .svg-icon { h2 .svg-icon, label .svg-icon {
margin-top: -1px; margin-top: -1px;
} }
.svg-icon.info { .svg-icon.info,
.svg-icon.settings {
width: 14px; width: 14px;
height: 16px; height: 16px;
} }
.svg-icon:hover, .svg-icon:hover,
.svg-icon.info { .svg-icon.info,
.svg-icon.settings {
fill: #666; fill: #666;
} }
.svg-icon, .svg-icon,
.svg-icon.info:hover { .svg-icon.info:hover,
.svg-icon.settings:hover {
fill: #000; fill: #000;
} }
#enabled { #enabled {
@ -365,11 +368,28 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
max-height: calc(100vh - 8rem); max-height: calc(100vh - 8rem);
overflow-y: auto; overflow-y: auto;
} }
#help-popup .settings {
min-width: 500px;
min-height: 200px;
max-width: 48vw;
}
#help-popup .dismiss { #help-popup .dismiss {
position: absolute; position: absolute;
right: 4px; right: 4px;
top: .5em; 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 { .keymap-list {
font-size: 85%; font-size: 85%;
@ -417,7 +437,6 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
border-spacing: 0; border-spacing: 0;
margin-bottom: 1rem; margin-bottom: 1rem;
line-height: 1.0; line-height: 1.0;
width: 245px;
} }
#lint table:last-child { #lint table:last-child {
margin-bottom: 0; margin-bottom: 0;

View File

@ -1,5 +1,5 @@
/* eslint brace-style: 0, operator-linebreak: 0 */ /* 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'; 'use strict';
let styleId = null; let styleId = null;
@ -168,7 +168,7 @@ function initCodeMirror() {
matchBrackets: true, matchBrackets: true,
highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true}, highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true},
hintOptions: {}, hintOptions: {},
lint: setLinter('csslint'), lint: setLinter(prefs.get('editor.linter')),
lintReportDelay: prefs.get('editor.lintReportDelay'), lintReportDelay: prefs.get('editor.lintReportDelay'),
styleActiveLine: true, styleActiveLine: true,
theme: 'default', theme: 'default',
@ -351,15 +351,7 @@ function acmeEventListener(event) {
break; break;
case 'linter': case 'linter':
if (value !== null && editors.length) { if (value !== null && editors.length) {
if (prefs.get(el.id) !== value) { updateLinter(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();
} }
break; break;
} }
@ -1251,7 +1243,7 @@ function initHooks() {
document.getElementById('sections-help').addEventListener('click', showSectionHelp, false); document.getElementById('sections-help').addEventListener('click', showSectionHelp, false);
document.getElementById('keyMap-help').addEventListener('click', showKeyMapHelp, false); document.getElementById('keyMap-help').addEventListener('click', showKeyMapHelp, false);
document.getElementById('cancel-button').addEventListener('click', goBackToManage); document.getElementById('cancel-button').addEventListener('click', goBackToManage);
initLintHooks(); initLint();
if (!FIREFOX) { if (!FIREFOX) {
$$([ $$([

View File

@ -1,15 +1,24 @@
/* global CodeMirror CSSLint editors makeSectionVisible showHelp */ /* global CodeMirror CSSLint editors makeSectionVisible showHelp stylelintDefaultConfig BG */
'use strict'; 'use strict';
function initLintHooks() { function initLint() {
document.getElementById('lint-help').addEventListener('click', showLintHelp); document.getElementById('lint-help').addEventListener('click', showLintHelp);
document.getElementById('lint').addEventListener('click', gotoLintIssue); document.getElementById('lint').addEventListener('click', gotoLintIssue);
window.addEventListener('resize', resizeLintReport); 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) // touch devices don't have onHover events so the element we'll be toggled via clicking (touching)
if ('ontouchstart' in document.body) { if ('ontouchstart' in document.body) {
document.querySelector('#lint h2').addEventListener('click', toggleLintReport); 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) { 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) { function updateLintReport(cm, delay) {
if (delay === 0) { if (delay === 0) {
// immediately show pending csslint/stylelint messages in onbeforeunload and save // immediately show pending csslint/stylelint messages in onbeforeunload and save
@ -178,3 +199,51 @@ function showLintHelp() {
} }
return showHelp(t('issues'), header + list + '</ul>'); return showHelp(t('issues'), header + list + '</ul>');
} }
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 = '<a target="_blank" href="https://stylelint.io/demo/">Stylelint</a>';
const text = JSON.stringify({rules: rules}, null, 2);
const content = `<textarea class="contents settings">${text}</textarea>
<p>${t('setStylelintLink', link)}</p>
<button class="save" type="button">Save</button>&nbsp;
<button class="reset" type="button">Reset</button>
<span class="error">
${t('setStylelintError')} (<a target="_blank" href="https://jsonlint.com/">JSONLint</a>)
</span>`;
showHelp(t('setStylelintRules'), content);
setupStylelintSettingsEvents();
});
}

View File

@ -66,26 +66,34 @@ CodeMirror.registerHelper("lint", "stylelint", function(text) {
let found = []; let found = [];
const stylelint = require('stylelint').lint; const stylelint = require('stylelint').lint;
if (stylelint) { if (stylelint) {
return stylelint({ return BG.chromeLocal.getValue('editorStylelintRules').then((rules = stylelintDefaultConfig.rules) => {
code: text, // stylelintDefaultConfig stored in stylelint-config.js & loaded by edit.html
// stylelintConfig stored in stylelint-config.js & loaded by edit.html if (Object.keys(rules).length === 0) {
config: stylelintConfig rules = stylelintDefaultConfig.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 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; return found;

View File

@ -1,4 +1,4 @@
const stylelintConfig = { const stylelintDefaultConfig = {
// 'sugarss' is a indent-based syntax like Sass or Stylus // 'sugarss' is a indent-based syntax like Sass or Stylus
// ref: https://github.com/postcss/postcss#syntaxes // ref: https://github.com/postcss/postcss#syntaxes
syntax: 'sugarss', syntax: 'sugarss',