re-enable linter after import + async'ify

regressed in 420733b9
This commit is contained in:
tophf 2020-11-29 01:07:05 +03:00
parent 207afccd65
commit e6f94378bf
5 changed files with 86 additions and 74 deletions

View File

@ -103,7 +103,6 @@ lazyInit();
await editor.ready; await editor.ready;
editor.ready = true; editor.ready = true;
setTimeout(() => editor.getEditors().forEach(linter.enableForEditor));
// enabling after init to prevent flash of validation failure on an empty name // enabling after init to prevent flash of validation failure on an empty name
$('#name').required = !editor.isUsercss; $('#name').required = !editor.isUsercss;
$('#save-button').onclick = editor.save; $('#save-button').onclick = editor.save;

View File

@ -4,6 +4,7 @@
importScripts('/js/worker-util.js'); importScripts('/js/worker-util.js');
const {loadScript} = workerUtil; const {loadScript} = workerUtil;
/** @namespace EditorWorker */
workerUtil.createAPI({ workerUtil.createAPI({
csslint: (code, config) => { csslint: (code, config) => {
loadScript('/vendor-overwrites/csslint/parserlib.js', '/vendor-overwrites/csslint/csslint.js'); loadScript('/vendor-overwrites/csslint/parserlib.js', '/vendor-overwrites/csslint/csslint.js');

View File

@ -2,6 +2,7 @@
'use strict'; 'use strict';
/* exported editorWorker */ /* exported editorWorker */
/** @type {EditorWorker} */
const editorWorker = workerUtil.createWorker({ const editorWorker = workerUtil.createWorker({
url: '/edit/editor-worker.js', url: '/edit/editor-worker.js',
}); });
@ -14,20 +15,57 @@ const linter = (() => {
const cms = new Set(); const cms = new Set();
return { return {
register, disableForEditor(cm) {
run, cm.setOption('lint', false);
enableForEditor, cms.delete(cm);
disableForEditor, for (const cb of unhookListeners) {
onLintingUpdated, cb(cm);
onUnhook, }
},
/**
* @param {Object} cm
* @param {string} [code] - to be used to avoid slowdowns when creating a lot of cms.
* Enables lint option only if there are problems, thus avoiding a _very_ costly layout
* update when lint gutter is added to a lot of editors simultaneously.
*/
enableForEditor(cm, code) {
if (code) return enableOnProblems(cm, code);
cm.setOption('lint', {getAnnotations, onUpdateLinting});
cms.add(cm);
},
onLintingUpdated(cb) {
lintingUpdatedListeners.push(cb);
},
onUnhook(cb) {
unhookListeners.push(cb);
},
register(linterFn) {
linters.push(linterFn);
},
run() {
for (const cm of cms) {
cm.performLint();
}
},
}; };
function onUnhook(cb) { async function enableOnProblems(cm, code) {
unhookListeners.push(cb); const results = await getAnnotations(code, {}, cm);
if (results.length) {
cms.add(cm);
cm.setOption('lint', {
getAnnotations() {
cm.options.lint.getAnnotations = getAnnotations;
return results;
},
onUpdateLinting,
});
}
} }
function onLintingUpdated(cb) { async function getAnnotations(...args) {
lintingUpdatedListeners.push(cb); const results = await Promise.all(linters.map(fn => fn(...args)));
return [].concat(...results.filter(Boolean));
} }
function onUpdateLinting(...args) { function onUpdateLinting(...args) {
@ -35,32 +73,4 @@ const linter = (() => {
cb(...args); cb(...args);
} }
} }
function enableForEditor(cm) {
cm.setOption('lint', {onUpdateLinting, getAnnotations});
cms.add(cm);
}
function disableForEditor(cm) {
cm.setOption('lint', false);
cms.delete(cm);
for (const cb of unhookListeners) {
cb(cm);
}
}
function register(linterFn) {
linters.push(linterFn);
}
function run() {
for (const cm of cms) {
cm.performLint();
}
}
function getAnnotations(...args) {
return Promise.all(linters.map(fn => fn(...args)))
.then(results => [].concat(...results.filter(Boolean)));
}
})(); })();

View File

@ -487,7 +487,7 @@ function SectionsEditor() {
livePreview.update(getModel()); livePreview.update(getModel());
} }
function initSections(src, { async function initSections(src, {
focusOn = 0, focusOn = 0,
replace = false, replace = false,
pristine = false, pristine = false,
@ -497,10 +497,6 @@ function SectionsEditor() {
sections.length = 0; sections.length = 0;
container.textContent = ''; container.textContent = '';
} }
let done;
let index = 0;
let y = 0;
const total = src.length;
let si = editor.scrollInfo; let si = editor.scrollInfo;
if (si && si.cms && si.cms.length === src.length) { if (si && si.cms && si.cms.length === src.length) {
si.scrollY2 = si.scrollY + window.innerHeight; si.scrollY2 = si.scrollY + window.innerHeight;
@ -510,29 +506,27 @@ function SectionsEditor() {
} else { } else {
si = null; si = null;
} }
return new Promise(resolve => { let forceRefresh = true;
done = resolve; let y = 0;
chunk(!si); let tPrev;
}); for (let i = 0; i < src.length; i++) {
function chunk(forceRefresh) { const t = performance.now();
const t0 = performance.now(); if (!tPrev) {
while (index < total && performance.now() - t0 < 100) { tPrev = t;
if (si) forceRefresh = y < si.scrollY2 && (y += si.cms[index].parentHeight) > si.scrollY; } else if (t - tPrev > 100) {
insertSectionAfter(src[index], undefined, forceRefresh, si && si.cms[index]); tPrev = 0;
if (pristine) dirty.clear(); forceRefresh = false;
if (index === focusOn && !si) sections[index].cm.focus(); await new Promise(setTimeout);
index++;
}
setGlobalProgress(index, total);
if (index === total) {
setGlobalProgress();
if (!si) requestAnimationFrame(fitToAvailableSpace);
container.style.removeProperty('height');
done();
} else {
setTimeout(chunk);
} }
if (si) forceRefresh = y < si.scrollY2 && (y += si.cms[i].parentHeight) > si.scrollY;
insertSectionAfter(src[i], null, forceRefresh, si && si.cms[i]);
setGlobalProgress(i, src.length);
if (pristine) dirty.clear();
if (i === focusOn && !si) sections[i].cm.focus();
} }
if (!si) requestAnimationFrame(fitToAvailableSpace);
container.style.removeProperty('height');
setGlobalProgress();
} }
/** @param {EditorSection} section */ /** @param {EditorSection} section */
@ -584,23 +578,23 @@ function SectionsEditor() {
} }
const section = createSection(init, genId, si); const section = createSection(init, genId, si);
const {cm} = section; const {cm} = section;
const {code} = init;
const index = base ? sections.indexOf(base) + 1 : sections.length; const index = base ? sections.indexOf(base) + 1 : sections.length;
sections.splice(index, 0, section); sections.splice(index, 0, section);
container.insertBefore(section.el, base ? base.el.nextSibling : null); container.insertBefore(section.el, base ? base.el.nextSibling : null);
refreshOnView(cm, base || forceRefresh); refreshOnView(cm, {code, force: base || forceRefresh});
registerEvents(section); registerEvents(section);
if ((!si || !si.height) && (!base || init.code)) { if ((!si || !si.height) && (!base || code)) {
// Fit a) during startup or b) when the clone button is clicked on a section with some code // Fit a) during startup or b) when the clone button is clicked on a section with some code
fitToContent(section); fitToContent(section);
} }
if (base) { if (base) {
cm.focus(); cm.focus();
editor.scrollToEditor(cm); editor.scrollToEditor(cm);
linter.enableForEditor(cm);
} }
updateSectionOrder(); updateSectionOrder();
section.onChange(updateLivePreview);
updateLivePreview(); updateLivePreview();
section.onChange(updateLivePreview);
} }
/** @param {EditorSection} section */ /** @param {EditorSection} section */
@ -654,10 +648,12 @@ function SectionsEditor() {
} }
} }
function refreshOnView(cm, force) { function refreshOnView(cm, {code, force} = {}) {
return force || !xo ? if (force || !xo) {
cm.refresh() : refreshOnViewNow(cm, code);
} else {
xo.observe(cm.display.wrapper); xo.observe(cm.display.wrapper);
}
} }
/** @param {IntersectionObserverEntry[]} entries */ /** @param {IntersectionObserverEntry[]} entries */
@ -668,14 +664,19 @@ function SectionsEditor() {
xo.unobserve(e.target); xo.unobserve(e.target);
const cm = e.target.CodeMirror; const cm = e.target.CodeMirror;
if (r.bottom > 0 && r.top < window.innerHeight) { if (r.bottom > 0 && r.top < window.innerHeight) {
cm.refresh(); refreshOnViewNow(cm);
} else { } else {
setTimeout(() => cm.refresh()); setTimeout(refreshOnViewNow, 0, cm);
} }
} }
} }
} }
async function refreshOnViewNow(cm, code) {
cm.refresh();
linter.enableForEditor(cm, code);
}
function toggleContextMenuDelete(event) { function toggleContextMenuDelete(event) {
if (chrome.contextMenus && event.button === 2 && prefs.get('editor.contextDelete')) { if (chrome.contextMenus && event.button === 2 && prefs.get('editor.contextDelete')) {
chrome.contextMenus.update('editor.contextDelete', { chrome.contextMenus.update('editor.contextDelete', {

View File

@ -91,6 +91,7 @@ function SourceEditor() {
linter.run(); linter.run();
updateLinterSwitch(); updateLinterSwitch();
}); });
setTimeout(linter.enableForEditor, 0, cm);
if (!$.isTextInput(document.activeElement)) { if (!$.isTextInput(document.activeElement)) {
cm.focus(); cm.focus();
} }