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;
editor.ready = true;
setTimeout(() => editor.getEditors().forEach(linter.enableForEditor));
// enabling after init to prevent flash of validation failure on an empty name
$('#name').required = !editor.isUsercss;
$('#save-button').onclick = editor.save;

View File

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

View File

@ -2,6 +2,7 @@
'use strict';
/* exported editorWorker */
/** @type {EditorWorker} */
const editorWorker = workerUtil.createWorker({
url: '/edit/editor-worker.js',
});
@ -14,20 +15,57 @@ const linter = (() => {
const cms = new Set();
return {
register,
run,
enableForEditor,
disableForEditor,
onLintingUpdated,
onUnhook,
disableForEditor(cm) {
cm.setOption('lint', false);
cms.delete(cm);
for (const cb of unhookListeners) {
cb(cm);
}
},
/**
* @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) {
unhookListeners.push(cb);
async function enableOnProblems(cm, code) {
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) {
lintingUpdatedListeners.push(cb);
async function getAnnotations(...args) {
const results = await Promise.all(linters.map(fn => fn(...args)));
return [].concat(...results.filter(Boolean));
}
function onUpdateLinting(...args) {
@ -35,32 +73,4 @@ const linter = (() => {
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());
}
function initSections(src, {
async function initSections(src, {
focusOn = 0,
replace = false,
pristine = false,
@ -497,10 +497,6 @@ function SectionsEditor() {
sections.length = 0;
container.textContent = '';
}
let done;
let index = 0;
let y = 0;
const total = src.length;
let si = editor.scrollInfo;
if (si && si.cms && si.cms.length === src.length) {
si.scrollY2 = si.scrollY + window.innerHeight;
@ -510,29 +506,27 @@ function SectionsEditor() {
} else {
si = null;
}
return new Promise(resolve => {
done = resolve;
chunk(!si);
});
function chunk(forceRefresh) {
const t0 = performance.now();
while (index < total && performance.now() - t0 < 100) {
if (si) forceRefresh = y < si.scrollY2 && (y += si.cms[index].parentHeight) > si.scrollY;
insertSectionAfter(src[index], undefined, forceRefresh, si && si.cms[index]);
if (pristine) dirty.clear();
if (index === focusOn && !si) sections[index].cm.focus();
index++;
}
setGlobalProgress(index, total);
if (index === total) {
setGlobalProgress();
if (!si) requestAnimationFrame(fitToAvailableSpace);
container.style.removeProperty('height');
done();
} else {
setTimeout(chunk);
let forceRefresh = true;
let y = 0;
let tPrev;
for (let i = 0; i < src.length; i++) {
const t = performance.now();
if (!tPrev) {
tPrev = t;
} else if (t - tPrev > 100) {
tPrev = 0;
forceRefresh = false;
await new Promise(setTimeout);
}
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 */
@ -584,23 +578,23 @@ function SectionsEditor() {
}
const section = createSection(init, genId, si);
const {cm} = section;
const {code} = init;
const index = base ? sections.indexOf(base) + 1 : sections.length;
sections.splice(index, 0, section);
container.insertBefore(section.el, base ? base.el.nextSibling : null);
refreshOnView(cm, base || forceRefresh);
refreshOnView(cm, {code, force: base || forceRefresh});
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
fitToContent(section);
}
if (base) {
cm.focus();
editor.scrollToEditor(cm);
linter.enableForEditor(cm);
}
updateSectionOrder();
section.onChange(updateLivePreview);
updateLivePreview();
section.onChange(updateLivePreview);
}
/** @param {EditorSection} section */
@ -654,10 +648,12 @@ function SectionsEditor() {
}
}
function refreshOnView(cm, force) {
return force || !xo ?
cm.refresh() :
function refreshOnView(cm, {code, force} = {}) {
if (force || !xo) {
refreshOnViewNow(cm, code);
} else {
xo.observe(cm.display.wrapper);
}
}
/** @param {IntersectionObserverEntry[]} entries */
@ -668,14 +664,19 @@ function SectionsEditor() {
xo.unobserve(e.target);
const cm = e.target.CodeMirror;
if (r.bottom > 0 && r.top < window.innerHeight) {
cm.refresh();
refreshOnViewNow(cm);
} else {
setTimeout(() => cm.refresh());
setTimeout(refreshOnViewNow, 0, cm);
}
}
}
}
async function refreshOnViewNow(cm, code) {
cm.refresh();
linter.enableForEditor(cm, code);
}
function toggleContextMenuDelete(event) {
if (chrome.contextMenus && event.button === 2 && prefs.get('editor.contextDelete')) {
chrome.contextMenus.update('editor.contextDelete', {

View File

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