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,
|
AutomaticLinkerSettings,
|
||||||
DEFAULT_SETTINGS,
|
DEFAULT_SETTINGS,
|
||||||
} from "./settings";
|
} from "./settings";
|
||||||
|
import PCancelable from "p-cancelable";
|
||||||
|
|
||||||
export default class AutomaticLinkerPlugin extends Plugin {
|
export default class AutomaticLinkerPlugin extends Plugin {
|
||||||
settings: AutomaticLinkerSettings;
|
settings: AutomaticLinkerSettings;
|
||||||
|
|
||||||
private allFileNames: string[] = [];
|
private allFileNames: string[] = [];
|
||||||
|
// Holds the currently running modifyLinks task
|
||||||
|
private currentModifyLinks: PCancelable<void> | null = null;
|
||||||
|
|
||||||
constructor(app: App, pluginManifest: PluginManifest) {
|
constructor(app: App, pluginManifest: PluginManifest) {
|
||||||
super(app, pluginManifest);
|
super(app, pluginManifest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cancelable function to modify links in the active file
|
||||||
async modifyLinks() {
|
async modifyLinks() {
|
||||||
const activeFile = this.app.workspace.getActiveFile();
|
// If a previous task is running, cancel it first.
|
||||||
if (!activeFile) return;
|
if (this.currentModifyLinks) {
|
||||||
const fileContent = await this.app.vault.read(activeFile);
|
this.currentModifyLinks.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
const updatedContent = await replaceLinks({
|
const cancelableTask = new PCancelable<void>(
|
||||||
fileContent,
|
async (resolve, reject, onCancel) => {
|
||||||
allFileNames: this.allFileNames,
|
const activeFile = this.app.workspace.getActiveFile();
|
||||||
getFrontMatterInfo,
|
if (!activeFile) {
|
||||||
specialDirs: this.settings.specialDirs,
|
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() {
|
async onload() {
|
||||||
|
|
@ -42,41 +81,46 @@ export default class AutomaticLinkerPlugin extends Plugin {
|
||||||
const allFileNames = allMarkdownFiles.map((file) =>
|
const allFileNames = allMarkdownFiles.map((file) =>
|
||||||
file.path.replace(/\.md$/, ""),
|
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);
|
allFileNames.sort((a, b) => b.length - a.length);
|
||||||
this.allFileNames = allFileNames;
|
this.allFileNames = allFileNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Load markdown file names when the layout is ready.
|
||||||
this.app.workspace.onLayoutReady(() => {
|
this.app.workspace.onLayoutReady(() => {
|
||||||
loadMarkdownFiles();
|
loadMarkdownFiles();
|
||||||
console.log("Automatic Linker: Loaded all markdown files.");
|
console.log("Automatic Linker: Loaded all markdown files.");
|
||||||
console.log("this.allFileNames.length", this.allFileNames.length);
|
console.log("this.allFileNames.length", this.allFileNames.length);
|
||||||
});
|
});
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.vault.on("delete", () => {
|
this.app.vault.on("delete", () => loadMarkdownFiles()),
|
||||||
loadMarkdownFiles();
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.vault.on("create", () => {
|
this.app.vault.on("create", () => loadMarkdownFiles()),
|
||||||
loadMarkdownFiles();
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
this.registerEvent(
|
this.registerEvent(
|
||||||
this.app.vault.on("rename", () => {
|
this.app.vault.on("rename", () => loadMarkdownFiles()),
|
||||||
loadMarkdownFiles();
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Add a command to trigger modifyLinks manually.
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "automatic-linker:link-current-file",
|
id: "automatic-linker:link-current-file",
|
||||||
name: "Link current file",
|
name: "Link current file",
|
||||||
editorCallback: async () => {
|
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?.[
|
const saveCommandDefinition = (this.app as any)?.commands?.commands?.[
|
||||||
"editor:save-file"
|
"editor:save-file"
|
||||||
];
|
];
|
||||||
|
|
@ -85,11 +129,17 @@ export default class AutomaticLinkerPlugin extends Plugin {
|
||||||
// Create a debounced version of modifyLinks with a 300ms delay.
|
// Create a debounced version of modifyLinks with a 300ms delay.
|
||||||
const debouncedModifyLinks = debounce(async () => {
|
const debouncedModifyLinks = debounce(async () => {
|
||||||
if (this.settings.formatOnSave) {
|
if (this.settings.formatOnSave) {
|
||||||
await this.modifyLinks();
|
try {
|
||||||
|
await this.modifyLinks();
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof PCancelable.CancelError)) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
saveCommandDefinition.callback = async () => {
|
saveCommandDefinition.callback = async () => {
|
||||||
// Call the debounced modifyLinks function.
|
// Call the debounced modifyLinks function first.
|
||||||
debouncedModifyLinks();
|
debouncedModifyLinks();
|
||||||
// Then, call the original save function.
|
// Then, call the original save function.
|
||||||
save();
|
save();
|
||||||
|
|
@ -114,7 +164,6 @@ export default class AutomaticLinkerPlugin extends Plugin {
|
||||||
function debounce<T extends (...args: any[]) => any>(func: T, wait: number): T {
|
function debounce<T extends (...args: any[]) => any>(func: T, wait: number): T {
|
||||||
let timeout: ReturnType<typeof setTimeout>;
|
let timeout: ReturnType<typeof setTimeout>;
|
||||||
return function (this: any, ...args: any[]) {
|
return function (this: any, ...args: any[]) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
||||||
const context = this;
|
const context = this;
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
timeout = setTimeout(() => func.apply(context, args), wait);
|
timeout = setTimeout(() => func.apply(context, args), wait);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"DOM",
|
"DOM",
|
||||||
"ES5",
|
"ES5",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue