keep scroll position and selections in tab's session

This commit is contained in:
tophf 2020-11-13 19:09:01 +03:00
parent 1d226aac8b
commit 3905722cdf
4 changed files with 55 additions and 19 deletions

View File

@ -56,13 +56,23 @@ lazyInit();
.then(initTheme), .then(initTheme),
onDOMready(), onDOMready(),
]); ]);
const scrollInfo = style.id && tryJSONparse(sessionStore['editorScrollInfo' + style.id]);
/** @namespace EditorBase */ /** @namespace EditorBase */
Object.assign(editor, { Object.assign(editor, {
style, style,
dirty, dirty,
scrollInfo,
updateName, updateName,
updateToc, updateToc,
toggleStyle, toggleStyle,
applyScrollInfo(cm, si = ((scrollInfo || {}).cms || [])[0]) {
if (si && si.sel) {
cm.operation(() => {
cm.setSelections(...si.sel, {scroll: false});
cm.scrollIntoView(cm.getCursor(), si.parentHeight / 2);
});
}
}
}); });
prefs.subscribe('editor.linter', updateLinter); prefs.subscribe('editor.linter', updateLinter);
prefs.subscribe('editor.keyMap', showHotkeyInTooltip); prefs.subscribe('editor.keyMap', showHotkeyInTooltip);
@ -410,6 +420,15 @@ function onRuntimeMessage(request) {
function beforeUnload(e) { function beforeUnload(e) {
sessionStore.windowPos = JSON.stringify(canSaveWindowPos() && prefs.get('windowPosition')); sessionStore.windowPos = JSON.stringify(canSaveWindowPos() && prefs.get('windowPosition'));
sessionStore['editorScrollInfo' + editor.style.id] = JSON.stringify({
scrollY: window.scrollY,
cms: editor.getEditors().map(cm => /** @namespace EditorScrollInfo */({
focus: cm.hasFocus(),
height: cm.display.wrapper.style.height.replace('100vh', ''),
parentHeight: cm.display.wrapper.parentElement.offsetHeight,
sel: cm.isClean() && [cm.doc.sel.ranges, cm.doc.sel.primIndex],
})),
});
const activeElement = document.activeElement; const activeElement = document.activeElement;
if (activeElement) { if (activeElement) {
// blurring triggers 'change' or 'input' event if needed // blurring triggers 'change' or 'input' event if needed

View File

@ -17,20 +17,26 @@
/* exported createSection */ /* exported createSection */
/** @returns {EditorSection} */ /**
function createSection(originalSection, genId) { * @param {StyleSection} originalSection
* @param {function():number} genId
* @param {EditorScrollInfo} [si]
* @returns {EditorSection}
*/
function createSection(originalSection, genId, si) {
const {dirty} = editor; const {dirty} = editor;
const sectionId = genId(); const sectionId = genId();
const el = template.section.cloneNode(true); const el = template.section.cloneNode(true);
const elLabel = $('.code-label', el); const elLabel = $('.code-label', el);
const cm = cmFactory.create(wrapper => { const cm = cmFactory.create(wrapper => {
// making it tall during initial load so IntersectionObserver sees only one adjacent CM // making it tall during initial load so IntersectionObserver sees only one adjacent CM
wrapper.style.height = '100vh'; wrapper.style.height = si ? si.height : '100vh';
elLabel.after(wrapper); elLabel.after(wrapper);
}, { }, {
value: originalSection.code, value: originalSection.code,
}); });
el.CodeMirror = cm; // used by getAssociatedEditor el.CodeMirror = cm; // used by getAssociatedEditor
editor.applyScrollInfo(cm, si);
const changeListeners = new Set(); const changeListeners = new Set();

View File

@ -486,7 +486,7 @@ function SectionsEditor() {
livePreview.update(getModel()); livePreview.update(getModel());
} }
function initSections(originalSections, { function initSections(src, {
focusOn = 0, focusOn = 0,
replace = false, replace = false,
pristine = false, pristine = false,
@ -497,26 +497,35 @@ function SectionsEditor() {
container.textContent = ''; container.textContent = '';
} }
let done; let done;
const total = originalSections.length; let index = 0;
originalSections = originalSections.slice(); 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;
container.style.height = si.scrollY2 + 'px';
scrollTo(0, si.scrollY);
} else {
si = null;
}
return new Promise(resolve => { return new Promise(resolve => {
done = resolve; done = resolve;
chunk(true); chunk(!si);
}); });
function chunk(forceRefresh) { function chunk(forceRefresh) {
const t0 = performance.now(); const t0 = performance.now();
while (originalSections.length && performance.now() - t0 < 100) { while (index < total && performance.now() - t0 < 100) {
insertSectionAfter(originalSections.shift(), undefined, forceRefresh); 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 (pristine) dirty.clear();
if (focusOn !== false && sections[focusOn]) { if (index === focusOn && !si) sections[index].cm.focus();
sections[focusOn].cm.focus(); index++;
focusOn = false;
} }
} setGlobalProgress(index, total);
setGlobalProgress(total - originalSections.length, total); if (index === total) {
if (!originalSections.length) {
setGlobalProgress(); setGlobalProgress();
requestAnimationFrame(fitToAvailableSpace); if (!si) requestAnimationFrame(fitToAvailableSpace);
container.style.removeProperty('height');
done(); done();
} else { } else {
setTimeout(chunk); setTimeout(chunk);
@ -565,18 +574,19 @@ function SectionsEditor() {
* @param {StyleSection} [init] * @param {StyleSection} [init]
* @param {EditorSection} [base] * @param {EditorSection} [base]
* @param {boolean} [forceRefresh] * @param {boolean} [forceRefresh]
* @param {EditorScrollInfo} [si]
*/ */
function insertSectionAfter(init, base, forceRefresh) { function insertSectionAfter(init, base, forceRefresh, si) {
if (!init) { if (!init) {
init = {code: '', urlPrefixes: ['http://example.com']}; init = {code: '', urlPrefixes: ['http://example.com']};
} }
const section = createSection(init, genId); const section = createSection(init, genId, si);
const {cm} = section; const {cm} = section;
sections.splice(base ? sections.indexOf(base) + 1 : sections.length, 0, section); sections.splice(base ? sections.indexOf(base) + 1 : sections.length, 0, section);
container.insertBefore(section.el, base ? base.el.nextSibling : null); container.insertBefore(section.el, base ? base.el.nextSibling : null);
refreshOnView(cm, forceRefresh); refreshOnView(cm, forceRefresh);
registerEvents(section); registerEvents(section);
if (!base || init.code) { if ((!si || !si.height) && (!base || init.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);
} }

View File

@ -75,6 +75,7 @@ function SourceEditor() {
'editor.appliesToLineWidget': (k, val) => sectionWidget.toggle(val), 'editor.appliesToLineWidget': (k, val) => sectionWidget.toggle(val),
'editor.toc.expanded': (k, val) => sectionFinder.onOff(editor.updateToc, val), 'editor.toc.expanded': (k, val) => sectionFinder.onOff(editor.updateToc, val),
}, {now: true}); }, {now: true});
editor.applyScrollInfo(cm);
cm.clearHistory(); cm.clearHistory();
cm.markClean(); cm.markClean();
savedGeneration = cm.changeGeneration(); savedGeneration = cm.changeGeneration();