mirror of
https://github.com/kdnk/obsidian-automatic-linker.git
synced 2026-07-22 05:37:46 +00:00
perf: use PCancelable for cancelable tasks
This commit is contained in:
parent
8ae51e813f
commit
717580efcf
2 changed files with 76 additions and 26 deletions
101
src/main.ts
101
src/main.ts
|
|
@ -5,29 +5,68 @@ import {
|
|||
AutomaticLinkerSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
} from "./settings";
|
||||
import PCancelable from "p-cancelable";
|
||||
|
||||
export default class AutomaticLinkerPlugin extends Plugin {
|
||||
settings: AutomaticLinkerSettings;
|
||||
|
||||
private allFileNames: string[] = [];
|
||||
// Holds the currently running modifyLinks task
|
||||
private currentModifyLinks: PCancelable<void> | null = null;
|
||||
|
||||
constructor(app: App, pluginManifest: PluginManifest) {
|
||||
super(app, pluginManifest);
|
||||
}
|
||||
|
||||
// Cancelable function to modify links in the active file
|
||||
async modifyLinks() {
|
||||
const activeFile = this.app.workspace.getActiveFile();
|
||||
if (!activeFile) return;
|
||||
const fileContent = await this.app.vault.read(activeFile);
|
||||
// If a previous task is running, cancel it first.
|
||||
if (this.currentModifyLinks) {
|
||||
this.currentModifyLinks.cancel();
|
||||
}
|
||||
|
||||
const updatedContent = await replaceLinks({
|
||||
fileContent,
|
||||
allFileNames: this.allFileNames,
|
||||
getFrontMatterInfo,
|
||||
specialDirs: this.settings.specialDirs,
|
||||
});
|
||||
const cancelableTask = new PCancelable<void>(
|
||||
async (resolve, reject, onCancel) => {
|
||||
const activeFile = this.app.workspace.getActiveFile();
|
||||
if (!activeFile) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
await this.app.vault.modify(activeFile, updatedContent);
|
||||
// Flag to track cancellation within this task.
|
||||
let canceled = false;
|
||||
onCancel(() => {
|
||||
canceled = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Read the active file's content
|
||||
const fileContent = await this.app.vault.read(activeFile);
|
||||
// If the task was canceled after reading, abort processing.
|
||||
if (canceled) {
|
||||
return reject(new PCancelable.CancelError());
|
||||
}
|
||||
// Replace links using the provided utility function.
|
||||
const updatedContent = await replaceLinks({
|
||||
fileContent,
|
||||
allFileNames: this.allFileNames,
|
||||
getFrontMatterInfo,
|
||||
specialDirs: this.settings.specialDirs,
|
||||
});
|
||||
// Check again if the task has been canceled.
|
||||
if (canceled) {
|
||||
return reject(new PCancelable.CancelError());
|
||||
}
|
||||
// Modify the active file with the updated content.
|
||||
await this.app.vault.modify(activeFile, updatedContent);
|
||||
resolve();
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.currentModifyLinks = cancelableTask;
|
||||
return cancelableTask;
|
||||
}
|
||||
|
||||
async onload() {
|
||||
|
|
@ -42,41 +81,46 @@ export default class AutomaticLinkerPlugin extends Plugin {
|
|||
const allFileNames = allMarkdownFiles.map((file) =>
|
||||
file.path.replace(/\.md$/, ""),
|
||||
);
|
||||
// Sort by descending length so that longer paths match first.
|
||||
// Sort file names in descending order by length so longer paths match first.
|
||||
allFileNames.sort((a, b) => b.length - a.length);
|
||||
this.allFileNames = allFileNames;
|
||||
};
|
||||
|
||||
// Load markdown file names when the layout is ready.
|
||||
this.app.workspace.onLayoutReady(() => {
|
||||
loadMarkdownFiles();
|
||||
console.log("Automatic Linker: Loaded all markdown files.");
|
||||
console.log("this.allFileNames.length", this.allFileNames.length);
|
||||
});
|
||||
this.registerEvent(
|
||||
this.app.vault.on("delete", () => {
|
||||
loadMarkdownFiles();
|
||||
}),
|
||||
this.app.vault.on("delete", () => loadMarkdownFiles()),
|
||||
);
|
||||
this.registerEvent(
|
||||
this.app.vault.on("create", () => {
|
||||
loadMarkdownFiles();
|
||||
}),
|
||||
this.app.vault.on("create", () => loadMarkdownFiles()),
|
||||
);
|
||||
this.registerEvent(
|
||||
this.app.vault.on("rename", () => {
|
||||
loadMarkdownFiles();
|
||||
}),
|
||||
this.app.vault.on("rename", () => loadMarkdownFiles()),
|
||||
);
|
||||
|
||||
// Add a command to trigger modifyLinks manually.
|
||||
this.addCommand({
|
||||
id: "automatic-linker:link-current-file",
|
||||
name: "Link current file",
|
||||
editorCallback: async () => {
|
||||
await this.modifyLinks();
|
||||
try {
|
||||
await this.modifyLinks();
|
||||
} catch (error) {
|
||||
// If the task was canceled, log that it was canceled.
|
||||
if (error instanceof PCancelable.CancelError) {
|
||||
console.log("modifyLinks was canceled");
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Override the save command callback if available.
|
||||
// Optionally, override the default save command to run modifyLinks with debounce.
|
||||
const saveCommandDefinition = (this.app as any)?.commands?.commands?.[
|
||||
"editor:save-file"
|
||||
];
|
||||
|
|
@ -85,11 +129,17 @@ export default class AutomaticLinkerPlugin extends Plugin {
|
|||
// Create a debounced version of modifyLinks with a 300ms delay.
|
||||
const debouncedModifyLinks = debounce(async () => {
|
||||
if (this.settings.formatOnSave) {
|
||||
await this.modifyLinks();
|
||||
try {
|
||||
await this.modifyLinks();
|
||||
} catch (error) {
|
||||
if (!(error instanceof PCancelable.CancelError)) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
saveCommandDefinition.callback = async () => {
|
||||
// Call the debounced modifyLinks function.
|
||||
// Call the debounced modifyLinks function first.
|
||||
debouncedModifyLinks();
|
||||
// Then, call the original save function.
|
||||
save();
|
||||
|
|
@ -114,7 +164,6 @@ export default class AutomaticLinkerPlugin extends Plugin {
|
|||
function debounce<T extends (...args: any[]) => any>(func: T, wait: number): T {
|
||||
let timeout: ReturnType<typeof setTimeout>;
|
||||
return function (this: any, ...args: any[]) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const context = this;
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(context, args), wait);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
|
|
|
|||
Loading…
Reference in a new issue