Mercurial > obsidian-remember-file-state
changeset 43:7e981d54a055
Support state persistence between sessions
There is now an option to save/load file states on disk.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 20 Sep 2023 22:40:25 -0700 |
parents | d52beb77d109 |
children | fae202d7b3de |
files | src/main.ts src/settings.ts |
diffstat | 2 files changed, 84 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/src/main.ts Wed Sep 20 17:19:46 2023 -0700 +++ b/src/main.ts Wed Sep 20 22:40:25 2023 -0700 @@ -7,6 +7,7 @@ Plugin, TAbstractFile, TFile, + Tasks, View, WorkspaceLeaf } from 'obsidian'; @@ -69,6 +70,9 @@ rememberedFiles: {} }; +// Where to save the states database. +const STATE_DB_PATH: string = '.obsidian/plugins/obsidian-remember-file-state/states.json'; + // Simple warning message. class WarningModal extends Modal { title: string = ""; @@ -109,12 +113,14 @@ this.data = Object.assign({}, DEFAULT_DATA); + await this.readStateDatabase(STATE_DB_PATH); + this.registerEvent(this.app.workspace.on('file-open', this.onFileOpen)); + this.registerEvent(this.app.workspace.on('quit', this.onAppQuit)); this.registerEvent(this.app.vault.on('rename', this.onFileRename)); this.registerEvent(this.app.vault.on('delete', this.onFileDelete)); - this.app.workspace.getLeavesOfType("markdown").forEach( - (leaf: WorkspaceLeaf) => { this.registerOnUnloadFile(leaf.view as MarkdownView); }); + this.app.workspace.onLayoutReady(() => { this.onLayoutReady(); }); const _this = this; var uninstall = around(this.app.workspace, { @@ -184,6 +190,31 @@ await this.saveData(this.settings); } + private readonly onLayoutReady = function() { + this.app.workspace.getLeavesOfType("markdown").forEach( + (leaf: WorkspaceLeaf) => { + var view = leaf.view as MarkdownView; + + // On startup, assign unique IDs to views and register the + // unload callback to remember their state. + this.registerOnUnloadFile(view); + + // Also remember which file is opened in which view. + const viewId = this.getUniqueViewId(view as ViewWithID); + if (viewId != undefined) { + this._lastOpenFiles[viewId] = view.file.path; + } + + // Restore state for each opened pane on startup. + const existingFile = this.data.rememberedFiles[view.file.path]; + if (existingFile) { + const savedStateData = existingFile.stateData; + console.debug("RememberFileState: restoring saved state for:", view.file.path, savedStateData); + this.restoreState(savedStateData, view); + } + }); + } + private readonly registerOnUnloadFile = function(view: MarkdownView) { var filePath = view.file.path; var viewId = this.getUniqueViewId(view as unknown as ViewWithID, true); @@ -196,7 +227,7 @@ var uninstall = around(view, { onUnloadFile: function(next) { return async function (unloaded: TFile) { - _this.rememberFileState(unloaded, this); + _this.rememberFileState(this, unloaded); return await next.call(this, unloaded); }; } @@ -282,8 +313,12 @@ } } - private readonly rememberFileState = async (file: TFile, view: MarkdownView): Promise<void> => { + private readonly rememberFileState = async (view: MarkdownView, file?: TFile): Promise<void> => { const stateData = this.getState(view); + + if (file === undefined) { + file = view.file; + } var existingFile = this.data.rememberedFiles[file.path]; if (existingFile) { existingFile.lastSavedTime = Date.now(); @@ -341,7 +376,6 @@ curView.file.path == file.path && this.getUniqueViewId(curView) >= 0 // Skip views that have never been activated. ) { - console.debug(`FFFFOOOOOUNNNNDD!!!!! ${file.path}`, curView, activeView); otherView = curView; return false; // Stop iterating leaves. } @@ -405,5 +439,37 @@ ): Promise<void> => { delete this.data.rememberedFiles[file.path]; }; + + private readonly onAppQuit = async (tasks: Tasks): Promise<void> => { + const _this = this; + tasks.addPromise( + _this.rememberAllOpenedFileStates() + .then(_this.writeStateDatabase(STATE_DB_PATH))); + } + + private readonly rememberAllOpenedFileStates = async(): Promise<void> => { + this.app.workspace.getLeavesOfType("markdown").forEach( + (leaf: WorkspaceLeaf) => { + const view = leaf.view as MarkdownView; + this.rememberFileState(view); + } + ); + } + + private readonly writeStateDatabase = async(path: string): Promise<void> => { + const fs = this.app.vault.adapter; + const jsonDb = JSON.stringify(this.data); + await fs.write(path, jsonDb); + } + + private readonly readStateDatabase = async(path: string): Promise<void> => { + const fs = this.app.vault.adapter; + if (await fs.exists(path)) { + const jsonDb = await fs.read(path); + this.data = JSON.parse(jsonDb); + const numLoaded = Object.keys(this.data.rememberedFiles).length; + console.debug(`RememberFileState: read ${numLoaded} record from state database.`); + } + } }
--- a/src/settings.ts Wed Sep 20 17:19:46 2023 -0700 +++ b/src/settings.ts Wed Sep 20 22:40:25 2023 -0700 @@ -8,12 +8,14 @@ export interface RememberFileStatePluginSettings { rememberMaxFiles: number; + persistStates: boolean; } export const DEFAULT_SETTINGS: RememberFileStatePluginSettings = { // Matches the number of files Obsidian remembers the undo/redo // history for by default (at least as of 0.13.17). - rememberMaxFiles: 20 + rememberMaxFiles: 20, + persistStates: true } export class RememberFileStatePluginSettingTab extends PluginSettingTab { @@ -41,5 +43,15 @@ await this.plugin.saveSettings(); } })); + + new Setting(containerEl) + .setName('Save states') + .setDesc('Whether to save the state of all open files to disk') + .addToggle(toggle => toggle + .setValue(this.plugin.settings.persistStates) + .onChange(async (value: boolean) => { + this.plugin.settings.persistStates = value; + await this.plugin.saveSettings(); + })); } }