feat: refresh Trie on frontmatter changes

This commit is contained in:
Kodai Nakamura 2025-12-20 19:40:09 +09:00
parent d9e405224b
commit d1afdd25db
2 changed files with 84 additions and 13 deletions

36
src/frontmatter-utils.ts Normal file
View file

@ -0,0 +1,36 @@
/**
* Check if the file has the "off" frontmatter property
*/
export const isLinkingOff = (
frontmatter: Record<string, unknown> | 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<string, unknown> | 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<string, unknown> | undefined,
): boolean => {
return (
frontmatter?.["automatic-linker-prevent-linking"] === true ||
frontmatter?.["automatic-linker-exclude"] === true
);
};

View file

@ -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<string, string> = new Map();
// Cache of frontmatter values that affect the Trie
private frontmatterCache: Map<string, string> = 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.