diff --git a/src/main.ts b/src/main.ts index 634c8b1..0732631 100644 --- a/src/main.ts +++ b/src/main.ts @@ -9,6 +9,7 @@ import { PluginManifest, FileSystemAdapter, TAbstractFile, + App, // PluginSettingTab, // App, } from 'obsidian'; @@ -20,7 +21,7 @@ import { DEFAULT_SETTINGS } from 'defaults'; import { PluginsAnnotationsSettingTab } from 'settings_tab' import * as path from 'path'; import { readAnnotationsFromMdFile, writeAnnotationsToMdFile } from 'manageAnnotations'; -import { backupSettings, delay, sortAnnotations } from 'utils'; +import { backupSettings, debounceFactoryWithWaitMechanism, delay, sortAnnotations } from 'utils'; import { annotationControl } from 'annotation_control'; export default class PluginsAnnotations extends Plugin { @@ -51,6 +52,21 @@ export default class PluginsAnnotations extends Plugin { private savePromise: Promise | null = null; private resolveSavePromise: (() => void) | null = null; + // public debouncedSaveAnnotations: (callback: () => void = (): void => {}): void; + + constructor(app:App, manifest:PluginManifest) { + super(app, manifest); + + // const { waitFnc, debouncedFct } = debounceFactoryWithWaitMechanism(async (...args: unknown[]): Promise => { + // // Extract the callback if it's provided, otherwise default to a no-op function + // const callback = typeof args[0] === 'function' ? args[0] as () => void : () => {}; + + // await this.saveSettings(); + // callback(); // Call the callback after saving settings + // }, 100); + + } + async onload() { // console.clear(); @@ -77,6 +93,27 @@ export default class PluginsAnnotations extends Plugin { } } }); + + + + + const asyncFunc = async (msg: string) => { + console.log("Processing:", msg); + return `Processed: ${msg}`; + }; + console.log("HEY"); + + const { debouncedFct, waitFnc } = debounceFactoryWithWaitMechanism(asyncFunc, 1000); + + // Make multiple debounced calls, only the last one should execute + debouncedFct("Message 1"); + debouncedFct("Message 2"); + // Wait for the last debounced call to finish + waitFnc().then(() => { + console.log("All debounced calls have been completed."); + }).catch(error => { + console.error("An unexpected error occurred:", error); + }); } /* Load settings for different versions */ @@ -229,6 +266,8 @@ export default class PluginsAnnotations extends Plugin { let isRestoreOperation; if(data === undefined) { + // we load directly from file, but first wait until + // the previous debounced writing operation is completed this.waitForSaveToComplete(); data = await this.loadData(); isRestoreOperation = false; @@ -544,6 +583,9 @@ export default class PluginsAnnotations extends Plugin { descriptionDiv.appendChild(annotation_container); } } + + const controlDiv = settingItemInfo.querySelector('.setting-item-control'); + console.log(controlDiv); } } @@ -603,14 +645,14 @@ export default class PluginsAnnotations extends Plugin { onunload() { // console.log('Unloading Plugins Annotations'); - // Remove all comments - this.removeCommentsFromTab(); + // // Remove all comments + // this.removeCommentsFromTab(); - // Just in case, disconnect observers if they still exist - this.disconnectObservers(); + // // Just in case, disconnect observers if they still exist + // this.disconnectObservers(); - // Remove icons - this.removeIcon(); + // // Remove icons + // this.removeIcon(); } getUninstalledPlugins(): PluginAnnotationDict { diff --git a/src/utils.ts b/src/utils.ts index 6fa950d..88f0b5f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -112,6 +112,87 @@ export async function createFolderIfNotExists(vault: Vault, folderPath: string) } } +// Utility to debounce rebuilds +export function debounceFactory unknown>(func: F, wait: number) { + let timeout: ReturnType; + + return (...args: Parameters): void => { + clearTimeout(timeout); + timeout = setTimeout(() => func(...args), wait); + }; +} + +export function debounceFactoryWithWaitMechanism ReturnType | Promise>>(func: F, wait: number) { + let timeout: ReturnType | null = null; + let promise: Promise> | null = null; + let resolvePromise: (() => void) | null = null; + + class CancelledExecution extends Error { + constructor(message = 'Debounced function call was cancelled') { + super(message); + this.name = 'CancelledExecution'; + } + } + + return { + // Function to wait for the completion of the current debounced call (if any) + waitFnc: async (): Promise => { + while (promise) { + try { + await promise; + } catch (error) { + if (!(error instanceof CancelledExecution)) { + throw error; // Re-throw if it's not a cancellation + } + // We do nothing if the execution was cancelled + } + } + }, + + // The debounced function itself + debouncedFct: (...args: Parameters): Promise> => { + // Clear the previous timeout to cancel any pending execution + if (timeout) { + clearTimeout(timeout); + } + + // Store the previous resolvePromise to resolve it after the new promise is created + const previousResolvePromise = resolvePromise; + + // Create a new promise for the current execution + + promise = new Promise>((resolve, reject) => { + // Now we set the new resolvePromise function + resolvePromise = () => { + reject(new CancelledExecution()); // Reject with a specific cancellation error + }; + + // After the new promise is created, resolve the previous one + if (previousResolvePromise) { + previousResolvePromise(); // Resolve the previous promise to prevent leaks + } + + // Schedule the function to run after the debounce delay + timeout = setTimeout(async () => { + try { + // Await the result of the function and cast it to the expected return type + const result = (await func(...args)) as ReturnType; + resolve(result); // Resolve with the result of the function (sync or async) + } catch (error) { + reject(error); // Reject the promise if the function throws an error + } + + // Clear the stored promise and resolve function after execution + promise = null; + resolvePromise = null; + }, wait); + }); + // Return the promise, allowing the caller to await the debounced execution + return promise; + } + }; +} + /* File suggestions */ export class FileSuggestion extends AbstractInputSuggest { private files:TFile[] = []; @@ -234,4 +315,5 @@ export async function backupSettings(backupName: string, toBeBackedUp: unknown, export function delay(ms: number) { return new Promise( resolve => setTimeout(resolve, ms) ); -} \ No newline at end of file +} +