From d1afdd25dba4dc163f256f18ec41ec370efbf87d Mon Sep 17 00:00:00 2001 From: Kodai Nakamura Date: Sat, 20 Dec 2025 19:40:09 +0900 Subject: [PATCH] feat: refresh Trie on frontmatter changes --- src/frontmatter-utils.ts | 36 ++++++++++++++++++++++++ src/main.ts | 61 +++++++++++++++++++++++++++++++--------- 2 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 src/frontmatter-utils.ts diff --git a/src/frontmatter-utils.ts b/src/frontmatter-utils.ts new file mode 100644 index 0000000..73c82ff --- /dev/null +++ b/src/frontmatter-utils.ts @@ -0,0 +1,36 @@ +/** + * Check if the file has the "off" frontmatter property + */ +export const isLinkingOff = ( + frontmatter: Record | undefined, +): boolean => { + return ( + frontmatter?.["automatic-linker-disabled"] === true || + frontmatter?.["automatic-linker-off"] === true + ); +}; + +/** + * Check if the file has the "scoped" frontmatter property + */ +export const isNamespaceScoped = ( + frontmatter: Record | undefined, +): boolean => { + return ( + frontmatter?.["automatic-linker-restrict-namespace"] === true || + frontmatter?.["automatic-linker-limited-namespace"] === true || + frontmatter?.["automatic-linker-scoped"] === true + ); +}; + +/** + * Check if the file has the "exclude" frontmatter property + */ +export const isLinkingExcluded = ( + frontmatter: Record | undefined, +): boolean => { + return ( + frontmatter?.["automatic-linker-prevent-linking"] === true || + frontmatter?.["automatic-linker-exclude"] === true + ); +}; diff --git a/src/main.ts b/src/main.ts index 6f4f4b3..8f99bfd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,11 @@ import { TFile, } from "obsidian"; import { excludeLinks } from "./exclude-links"; +import { + isLinkingOff, + isLinkingExcluded, + isNamespaceScoped, +} from "./frontmatter-utils"; import { PathAndAliases } from "./path-and-aliases.types"; import { removeMinimalIndent } from "./remove-minimal-indent"; import { @@ -45,6 +50,8 @@ export default class AutomaticLinkerPlugin extends Plugin { // Preserved callback for the original save command private originalSaveCallback: (checking: boolean) => boolean | void; private urlTitleMap: Map = new Map(); + // Cache of frontmatter values that affect the Trie + private frontmatterCache: Map = new Map(); constructor(app: App, pluginManifest: PluginManifest) { super(app, pluginManifest); @@ -193,10 +200,7 @@ export default class AutomaticLinkerPlugin extends Plugin { const metadata = this.app.metadataCache.getFileCache(activeFile)?.frontmatter; - const off = - metadata?.["automatic-linker-disabled"] === true || - metadata?.["automatic-linker-off"] === true; - if (off) { + if (isLinkingOff(metadata)) { return; } @@ -326,16 +330,9 @@ export default class AutomaticLinkerPlugin extends Plugin { const path = file.path.replace(/\.md$/, ""); const metadata = this.app.metadataCache.getFileCache(file)?.frontmatter; - const scoped = - metadata?.["automatic-linker-restrict-namespace"] === - true || - metadata?.["automatic-linker-limited-namespace"] === true || - metadata?.["automatic-linker-scoped"] === true; - + const scoped = isNamespaceScoped(metadata); // if this property exists, prevent this file from being linked from other files - const exclude = - metadata?.["automatic-linker-prevent-linking"] === true || - metadata?.["automatic-linker-exclude"] === true; + const exclude = isLinkingExcluded(metadata); const aliases = (() => { if (this.settings.considerAliases) { @@ -378,6 +375,9 @@ export default class AutomaticLinkerPlugin extends Plugin { this.candidateMap = candidateMap; this.trie = trie; + // Clear frontmatter cache when Trie is rebuilt + this.frontmatterCache.clear(); + if (this.settings.showNotice) { new Notice( `Automatic Linker: Loaded all markdown files. (${allFiles.length} files)`, @@ -390,6 +390,36 @@ export default class AutomaticLinkerPlugin extends Plugin { } } + private refreshFileDataAndTrieOnFrontmatterChange(file: TFile) { + const metadata = + this.app.metadataCache.getFileCache(file)?.frontmatter; + + // Extract frontmatter fields that affect the Trie + const relevantFields = { + aliases: metadata?.aliases + ? JSON.stringify(metadata.aliases) + : undefined, + scoped: isNamespaceScoped(metadata), + exclude: isLinkingExcluded(metadata), + }; + + // Create a hash of the relevant fields + const currentHash = JSON.stringify(relevantFields); + const cachedHash = this.frontmatterCache.get(file.path); + + // If the hash has changed, refresh the Trie + if (currentHash !== cachedHash) { + this.frontmatterCache.set(file.path, currentHash); + this.refreshFileDataAndTrie(); + + if (this.settings.debug) { + console.log( + `Automatic Linker: Refreshing Trie due to frontmatter change in ${file.path}`, + ); + } + } + } + async onload() { await this.loadSettings(); this.addSettingTab( @@ -415,6 +445,11 @@ export default class AutomaticLinkerPlugin extends Plugin { this.refreshFileDataAndTrie(), ), ); + this.registerEvent( + this.app.metadataCache.on("changed", (file) => + this.refreshFileDataAndTrieOnFrontmatterChange(file), + ), + ); }); // Command: Manually trigger link replacement for the current file.