diff --git a/edit.html b/edit.html
index f36ca5bc..87612e11 100644
--- a/edit.html
+++ b/edit.html
@@ -9,6 +9,7 @@
+
@@ -33,14 +34,15 @@
-
-
-
+
+
+
+
diff --git a/edit/edit.js b/edit/edit.js
index b136f1be..1b15e1dd 100644
--- a/edit/edit.js
+++ b/edit/edit.js
@@ -1,5 +1,5 @@
/* eslint brace-style: 0, operator-linebreak: 0 */
-/* global CodeMirror exports parserlib CSSLint */
+/* global CodeMirror exports css_beautify parserlib CSSLint initLintHooks setLinter updateLintReport renderLintReport */
'use strict';
let styleId = null;
@@ -168,7 +168,7 @@ function initCodeMirror() {
matchBrackets: true,
highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true},
hintOptions: {},
- lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get('editor.lintDelay')},
+ lint: setLinter('csslint'),
lintReportDelay: prefs.get('editor.lintReportDelay'),
styleActiveLine: true,
theme: 'default',
@@ -350,11 +350,15 @@ function acmeEventListener(event) {
}
break;
case 'linter':
- if (value !== null && (editors.lastActive || editors[0])) {
+ if (value !== null && editors.length) {
if (prefs.get(el.id) !== value) {
prefs.set(el.id, value || 'csslint');
}
- CodeMirror.signal(editors.lastActive || editors[0], "change");
+ editors.forEach(cm => {
+ console.log('set linter to', value);
+ cm.setOption('lint', setLinter(value || 'csslint'));
+ });
+ // CodeMirror.signal(editors.lastActive || editors[0], "change");
// save();
}
break;
@@ -1058,143 +1062,6 @@ function getEditorInSight(nearbyElement) {
}
}
-function updateLintReport(cm, delay) {
- if (delay === 0) {
- // immediately show pending csslint/stylelint messages in onbeforeunload and save
- update(cm);
- return;
- }
- if (delay > 0) {
- setTimeout(cm => { cm.performLint(); update(cm); }, delay, cm);
- return;
- }
- // eslint-disable-next-line no-var
- var state = cm.state.lint;
- if (!state) {
- return;
- }
- // user is editing right now: postpone updating the report for the new issues (default: 500ms lint + 4500ms)
- // or update it as soon as possible (default: 500ms lint + 100ms) in case an existing issue was just fixed
- clearTimeout(state.reportTimeout);
- state.reportTimeout = setTimeout(update, state.options.delay + 100, cm);
- state.postponeNewIssues = delay === undefined || delay === null;
-
- function update(cm) {
- const scope = cm ? [cm] : editors;
- let changed = false;
- let fixedOldIssues = false;
- scope.forEach(cm => {
- const linter = prefs.get('editor.linter');
- const scopedState = cm.state.lint || {};
- const oldMarkers = scopedState.markedLast || {};
- const newMarkers = {};
- const html = !scopedState.marked || scopedState.marked.length === 0 ? '' : '
' +
- scopedState.marked.map(mark => {
- const info = mark.__annotation;
- const isActiveLine = info.from.line === cm.getCursor().line;
- const pos = isActiveLine ? 'cursor' : (info.from.line + ',' + info.from.ch);
- // stylelint rule added in parentheses at the end
- const rule = linter === 'stylelint' ?
- info.message.substring(info.message.lastIndexOf('('), info.message.length) :
- / at line \d.+$/;
- // csslint
- let message = escapeHtml(info.message.replace(rule, ''));
- if (isActiveLine || oldMarkers[pos] === message) {
- delete oldMarkers[pos];
- }
- newMarkers[pos] = message;
- return `
-
- ${info.severity}
- |
- ${info.from.line + 1} |
- : |
- ${info.from.ch + 1} |
- ${message} |
-
`
- }).join('') + '';
- scopedState.markedLast = newMarkers;
- fixedOldIssues |= scopedState.reportDisplayed && Object.keys(oldMarkers).length > 0;
- if (scopedState.html !== html) {
- scopedState.html = html;
- changed = true;
- }
- });
- if (changed) {
- clearTimeout(state ? state.renderTimeout : undefined);
- if (!state || !state.postponeNewIssues || fixedOldIssues) {
- renderLintReport(true);
- } else {
- state.renderTimeout = setTimeout(() => {
- renderLintReport(true);
- }, CodeMirror.defaults.lintReportDelay);
- }
- }
- }
- function escapeHtml(html) {
- const chars = {'&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/'};
- return html.replace(/[&<>"'/]/g, char => chars[char]);
- }
-}
-
-function renderLintReport(someBlockChanged) {
- const container = document.getElementById('lint');
- const content = container.children[1];
- const label = t('sectionCode');
- const newContent = content.cloneNode(false);
- let issueCount = 0;
- editors.forEach((cm, index) => {
- if (cm.state.lint && cm.state.lint.html) {
- const html = '' + label + ' ' + (index + 1) + '' + cm.state.lint.html;
- const newBlock = newContent.appendChild(tHTML(html, 'table'));
-
- newBlock.cm = cm;
- issueCount += newBlock.rows.length;
-
- const block = content.children[newContent.children.length - 1];
- const blockChanged = !block || cm !== block.cm || html !== block.innerHTML;
- someBlockChanged |= blockChanged;
- cm.state.lint.reportDisplayed = blockChanged;
- }
- });
- if (someBlockChanged || newContent.children.length !== content.children.length) {
- document.getElementById('issue-count').textContent = issueCount;
- container.replaceChild(newContent, content);
- container.style.display = newContent.children.length ? 'block' : 'none';
- resizeLintReport(null, newContent);
- }
-}
-
-function resizeLintReport(event, content) {
- content = content || document.getElementById('lint').children[1];
- if (content.children.length) {
- const bounds = content.getBoundingClientRect();
- const newMaxHeight = bounds.bottom <= innerHeight ? '' : (innerHeight - bounds.top) + 'px';
- if (newMaxHeight !== content.style.maxHeight) {
- content.style.maxHeight = newMaxHeight;
- }
- }
-}
-
-function gotoLintIssue(event) {
- const issue = event.target.closest('tr');
- if (!issue) {
- return;
- }
- const block = issue.closest('table');
- makeSectionVisible(block.cm);
- block.cm.focus();
- block.cm.setSelection({
- line: parseInt(issue.querySelector('td[role="line"]').textContent) - 1,
- ch: parseInt(issue.querySelector('td[role="col"]').textContent) - 1
- });
-}
-
-function toggleLintReport() {
- document.getElementById('lint').classList.toggle('collapsed');
-}
-
function beautify(event) {
const script = document.head.appendChild(document.createElement('script'));
script.src = 'vendor-overwrites/beautify/beautify-css-mod.js';
@@ -1384,14 +1251,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);
- document.getElementById('lint-help').addEventListener('click', showLintHelp);
- document.getElementById('lint').addEventListener('click', gotoLintIssue);
- window.addEventListener('resize', resizeLintReport);
-
- // 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);
- }
+ initLintHooks();
if (!FIREFOX) {
$$([
@@ -1878,29 +1738,6 @@ function showKeyMapHelp() {
}
}
-function showLintHelp() {
- let list = '';
- let content = '';
- if (prefs.get('editor.linter') === 'csslint') {
- content = t('issuesHelp', 'CSSLint') + list +
- CSSLint.getRules().map(rule =>
- '- ' + rule.name + '
' + rule.desc + ' '
- ).join('');
- } else {
- let rules = [];
- const url = 'https://stylelint.io/user-guide/rules/';
- content = t('issuesHelp', `stylelint`) + list;
- $$('#lint td[role="severity"]').forEach(el => {
- const rule = el.title.replace('Rule: (', '').replace(/[()]/g, '').trim();
- if (!rules.includes(rule)) {
- content += `- ${rule}
`;
- rules.push(rule);
- }
- });
- }
- return showHelp(t('issues'), content + '
');
-}
-
function showRegExpTester(event, section = getSectionForChild(this)) {
const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain=';
const OWN_ICON = chrome.runtime.getManifest().icons['16'];
@@ -2123,6 +1960,7 @@ function onRuntimeMessage(request) {
break;
case 'styleDeleted':
if (styleId && styleId === request.id) {
+ // eslint-disable-next-line no-empty-function
window.onbeforeunload = () => {};
window.close();
break;
diff --git a/edit/lint.js b/edit/lint.js
new file mode 100644
index 00000000..a7d01ee0
--- /dev/null
+++ b/edit/lint.js
@@ -0,0 +1,180 @@
+/* global CodeMirror CSSLint editors makeSectionVisible showHelp */
+'use strict';
+
+function initLintHooks() {
+ document.getElementById('lint-help').addEventListener('click', showLintHelp);
+ document.getElementById('lint').addEventListener('click', gotoLintIssue);
+ window.addEventListener('resize', resizeLintReport);
+
+ // 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);
+ }
+}
+
+function setLinter(name) {
+ return {
+ getAnnotations: CodeMirror.lint[name],
+ delay: prefs.get('editor.lintDelay')
+ };
+}
+
+function updateLintReport(cm, delay) {
+ if (delay === 0) {
+ // immediately show pending csslint/stylelint messages in onbeforeunload and save
+ update(cm);
+ return;
+ }
+ if (delay > 0) {
+ setTimeout(cm => { cm.performLint(); update(cm); }, delay, cm);
+ return;
+ }
+ // eslint-disable-next-line no-var
+ var state = cm.state.lint;
+ if (!state) {
+ return;
+ }
+ // user is editing right now: postpone updating the report for the new issues (default: 500ms lint + 4500ms)
+ // or update it as soon as possible (default: 500ms lint + 100ms) in case an existing issue was just fixed
+ clearTimeout(state.reportTimeout);
+ state.reportTimeout = setTimeout(update, state.options.delay + 100, cm);
+ state.postponeNewIssues = delay === undefined || delay === null;
+
+ function update(cm) {
+ const scope = cm ? [cm] : editors;
+ let changed = false;
+ let fixedOldIssues = false;
+ scope.forEach(cm => {
+ const linter = prefs.get('editor.linter');
+ const scopedState = cm.state.lint || {};
+ const oldMarkers = scopedState.markedLast || {};
+ const newMarkers = {};
+ const html = !scopedState.marked || scopedState.marked.length === 0 ? '' : '' +
+ scopedState.marked.map(mark => {
+ const info = mark.__annotation;
+ const isActiveLine = info.from.line === cm.getCursor().line;
+ const pos = isActiveLine ? 'cursor' : (info.from.line + ',' + info.from.ch);
+ // stylelint rule added in parentheses at the end
+ const rule = linter === 'stylelint' ?
+ info.message.substring(info.message.lastIndexOf('('), info.message.length) :
+ / at line \d.+$/;
+ // csslint
+ const message = escapeHtml(info.message.replace(rule, ''));
+ if (isActiveLine || oldMarkers[pos] === message) {
+ delete oldMarkers[pos];
+ }
+ newMarkers[pos] = message;
+ return `
+
+ ${info.severity}
+ |
+ ${info.from.line + 1} |
+ : |
+ ${info.from.ch + 1} |
+ ${message} |
+
`;
+ }).join('') + '';
+ scopedState.markedLast = newMarkers;
+ fixedOldIssues |= scopedState.reportDisplayed && Object.keys(oldMarkers).length > 0;
+ if (scopedState.html !== html) {
+ scopedState.html = html;
+ changed = true;
+ }
+ });
+ if (changed) {
+ clearTimeout(state ? state.renderTimeout : undefined);
+ if (!state || !state.postponeNewIssues || fixedOldIssues) {
+ renderLintReport(true);
+ } else {
+ state.renderTimeout = setTimeout(() => {
+ renderLintReport(true);
+ }, CodeMirror.defaults.lintReportDelay);
+ }
+ }
+ }
+ function escapeHtml(html) {
+ const chars = {'&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/'};
+ return html.replace(/[&<>"'/]/g, char => chars[char]);
+ }
+}
+
+function renderLintReport(someBlockChanged) {
+ const container = document.getElementById('lint');
+ const content = container.children[1];
+ const label = t('sectionCode');
+ const newContent = content.cloneNode(false);
+ let issueCount = 0;
+ editors.forEach((cm, index) => {
+ if (cm.state.lint && cm.state.lint.html) {
+ const html = '' + label + ' ' + (index + 1) + '' + cm.state.lint.html;
+ const newBlock = newContent.appendChild(tHTML(html, 'table'));
+
+ newBlock.cm = cm;
+ issueCount += newBlock.rows.length;
+
+ const block = content.children[newContent.children.length - 1];
+ const blockChanged = !block || cm !== block.cm || html !== block.innerHTML;
+ someBlockChanged |= blockChanged;
+ cm.state.lint.reportDisplayed = blockChanged;
+ }
+ });
+ if (someBlockChanged || newContent.children.length !== content.children.length) {
+ document.getElementById('issue-count').textContent = issueCount;
+ container.replaceChild(newContent, content);
+ container.style.display = newContent.children.length ? 'block' : 'none';
+ resizeLintReport(null, newContent);
+ }
+}
+
+function resizeLintReport(event, content) {
+ content = content || document.getElementById('lint').children[1];
+ if (content.children.length) {
+ const bounds = content.getBoundingClientRect();
+ const newMaxHeight = bounds.bottom <= innerHeight ? '' : (innerHeight - bounds.top) + 'px';
+ if (newMaxHeight !== content.style.maxHeight) {
+ content.style.maxHeight = newMaxHeight;
+ }
+ }
+}
+
+function gotoLintIssue(event) {
+ const issue = event.target.closest('tr');
+ if (!issue) {
+ return;
+ }
+ const block = issue.closest('table');
+ makeSectionVisible(block.cm);
+ block.cm.focus();
+ block.cm.setSelection({
+ line: parseInt(issue.querySelector('td[role="line"]').textContent) - 1,
+ ch: parseInt(issue.querySelector('td[role="col"]').textContent) - 1
+ });
+}
+
+function toggleLintReport() {
+ document.getElementById('lint').classList.toggle('collapsed');
+}
+
+function showLintHelp() {
+ let list = '';
+ let header = '';
+ if (prefs.get('editor.linter') === 'csslint') {
+ header = t('issuesHelp', 'CSSLint');
+ list += CSSLint.getRules().map(rule =>
+ '- ' + rule.name + '
' + rule.desc + ' '
+ ).join('');
+ } else {
+ const rules = [];
+ const url = 'https://stylelint.io/user-guide/rules/';
+ header = t('issuesHelp', `stylelint`);
+ $$('#lint td[role="severity"]').forEach(el => {
+ const rule = el.title.replace('Rule: (', '').replace(/[()]/g, '').trim();
+ if (!rules.includes(rule)) {
+ list += `- ${rule}
`;
+ rules.push(rule);
+ }
+ });
+ }
+ return showHelp(t('issues'), header + list + '
');
+}
diff --git a/vendor-overwrites/codemirror/addon/lint/linter.js b/vendor-overwrites/codemirror/addon/lint/linter.js
index fc156eea..36751c52 100644
--- a/vendor-overwrites/codemirror/addon/lint/linter.js
+++ b/vendor-overwrites/codemirror/addon/lint/linter.js
@@ -15,10 +15,9 @@
})(function(CodeMirror) {
"use strict";
-CodeMirror.registerHelper("lint", "css", function(text) {
+CodeMirror.registerHelper("lint", "csslint", function(text) {
let found = [];
- const linter = prefs.get('editor.linter');
- if (linter === 'csslint' && window.CSSLint) {
+ if (window.CSSLint) {
/* STYLISH: hack start (part 1) */
var rules = CSSLint.getRules();
@@ -59,31 +58,35 @@ CodeMirror.registerHelper("lint", "css", function(text) {
severity : message.type
});
}
- } else if (linter === 'stylelint') {
- 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 found;
- });
- }
+ }
+ return found;
+});
+
+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 found;
+ });
}
return found;
});