refactor: link-utils to use adapters
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { App, TFile, MetadataCache } from 'obsidian';
|
import { TFile } from 'obsidian';
|
||||||
|
import { IVaultAdapter, IMetadataCacheAdapter } from '../adapters/interfaces';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parsed wikilink structure
|
* Parsed wikilink structure
|
||||||
@@ -114,14 +115,15 @@ export class LinkUtils {
|
|||||||
* Resolve a wikilink to its target file
|
* Resolve a wikilink to its target file
|
||||||
* Uses Obsidian's MetadataCache for accurate resolution
|
* Uses Obsidian's MetadataCache for accurate resolution
|
||||||
*
|
*
|
||||||
* @param app Obsidian App instance
|
* @param vault Vault adapter for file operations
|
||||||
|
* @param metadata Metadata cache adapter for link resolution
|
||||||
* @param sourcePath Path of the file containing the link
|
* @param sourcePath Path of the file containing the link
|
||||||
* @param linkText Link text (without brackets)
|
* @param linkText Link text (without brackets)
|
||||||
* @returns Resolved file or null if not found
|
* @returns Resolved file or null if not found
|
||||||
*/
|
*/
|
||||||
static resolveLink(app: App, sourcePath: string, linkText: string): TFile | null {
|
static resolveLink(vault: IVaultAdapter, metadata: IMetadataCacheAdapter, sourcePath: string, linkText: string): TFile | null {
|
||||||
// Get the source file
|
// Get the source file
|
||||||
const sourceFile = app.vault.getAbstractFileByPath(sourcePath);
|
const sourceFile = vault.getAbstractFileByPath(sourcePath);
|
||||||
if (!(sourceFile instanceof TFile)) {
|
if (!(sourceFile instanceof TFile)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -132,7 +134,7 @@ export class LinkUtils {
|
|||||||
// - Relative paths
|
// - Relative paths
|
||||||
// - Aliases
|
// - Aliases
|
||||||
// - Headings and blocks
|
// - Headings and blocks
|
||||||
const resolvedFile = app.metadataCache.getFirstLinkpathDest(linkText, sourcePath);
|
const resolvedFile = metadata.getFirstLinkpathDest(linkText, sourcePath);
|
||||||
|
|
||||||
return resolvedFile;
|
return resolvedFile;
|
||||||
}
|
}
|
||||||
@@ -141,13 +143,13 @@ export class LinkUtils {
|
|||||||
* Find potential matches for an unresolved link
|
* Find potential matches for an unresolved link
|
||||||
* Uses fuzzy matching on file names
|
* Uses fuzzy matching on file names
|
||||||
*
|
*
|
||||||
* @param app Obsidian App instance
|
* @param vault Vault adapter for file operations
|
||||||
* @param linkText Link text to find matches for
|
* @param linkText Link text to find matches for
|
||||||
* @param maxSuggestions Maximum number of suggestions to return
|
* @param maxSuggestions Maximum number of suggestions to return
|
||||||
* @returns Array of suggested file paths
|
* @returns Array of suggested file paths
|
||||||
*/
|
*/
|
||||||
static findSuggestions(app: App, linkText: string, maxSuggestions: number = 5): string[] {
|
static findSuggestions(vault: IVaultAdapter, linkText: string, maxSuggestions: number = 5): string[] {
|
||||||
const allFiles = app.vault.getMarkdownFiles();
|
const allFiles = vault.getMarkdownFiles();
|
||||||
const suggestions: Array<{ path: string; score: number }> = [];
|
const suggestions: Array<{ path: string; score: number }> = [];
|
||||||
|
|
||||||
// Remove heading/block references for matching
|
// Remove heading/block references for matching
|
||||||
@@ -197,18 +199,20 @@ export class LinkUtils {
|
|||||||
* Get all backlinks to a file
|
* Get all backlinks to a file
|
||||||
* Uses Obsidian's MetadataCache for accurate backlink detection
|
* Uses Obsidian's MetadataCache for accurate backlink detection
|
||||||
*
|
*
|
||||||
* @param app Obsidian App instance
|
* @param vault Vault adapter for file operations
|
||||||
|
* @param metadata Metadata cache adapter for link resolution
|
||||||
* @param targetPath Path of the file to find backlinks for
|
* @param targetPath Path of the file to find backlinks for
|
||||||
* @param includeUnlinked Whether to include unlinked mentions
|
* @param includeUnlinked Whether to include unlinked mentions
|
||||||
* @returns Array of backlinks
|
* @returns Array of backlinks
|
||||||
*/
|
*/
|
||||||
static async getBacklinks(
|
static async getBacklinks(
|
||||||
app: App,
|
vault: IVaultAdapter,
|
||||||
|
metadata: IMetadataCacheAdapter,
|
||||||
targetPath: string,
|
targetPath: string,
|
||||||
includeUnlinked: boolean = false
|
includeUnlinked: boolean = false
|
||||||
): Promise<Backlink[]> {
|
): Promise<Backlink[]> {
|
||||||
const backlinks: Backlink[] = [];
|
const backlinks: Backlink[] = [];
|
||||||
const targetFile = app.vault.getAbstractFileByPath(targetPath);
|
const targetFile = vault.getAbstractFileByPath(targetPath);
|
||||||
|
|
||||||
if (!(targetFile instanceof TFile)) {
|
if (!(targetFile instanceof TFile)) {
|
||||||
return backlinks;
|
return backlinks;
|
||||||
@@ -219,7 +223,7 @@ export class LinkUtils {
|
|||||||
|
|
||||||
// Get all backlinks from MetadataCache using resolvedLinks
|
// Get all backlinks from MetadataCache using resolvedLinks
|
||||||
// resolvedLinks is a map of: sourcePath -> { targetPath: linkCount }
|
// resolvedLinks is a map of: sourcePath -> { targetPath: linkCount }
|
||||||
const resolvedLinks = app.metadataCache.resolvedLinks;
|
const resolvedLinks = metadata.resolvedLinks;
|
||||||
|
|
||||||
// Find all files that link to our target
|
// Find all files that link to our target
|
||||||
for (const [sourcePath, links] of Object.entries(resolvedLinks)) {
|
for (const [sourcePath, links] of Object.entries(resolvedLinks)) {
|
||||||
@@ -228,13 +232,13 @@ export class LinkUtils {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceFile = app.vault.getAbstractFileByPath(sourcePath);
|
const sourceFile = vault.getAbstractFileByPath(sourcePath);
|
||||||
if (!(sourceFile instanceof TFile)) {
|
if (!(sourceFile instanceof TFile)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the source file to find link occurrences
|
// Read the source file to find link occurrences
|
||||||
const content = await app.vault.read(sourceFile);
|
const content = await vault.read(sourceFile);
|
||||||
const lines = content.split('\n');
|
const lines = content.split('\n');
|
||||||
const occurrences: BacklinkOccurrence[] = [];
|
const occurrences: BacklinkOccurrence[] = [];
|
||||||
|
|
||||||
@@ -243,7 +247,7 @@ export class LinkUtils {
|
|||||||
|
|
||||||
for (const link of wikilinks) {
|
for (const link of wikilinks) {
|
||||||
// Resolve this link to see if it points to our target
|
// Resolve this link to see if it points to our target
|
||||||
const resolvedFile = this.resolveLink(app, sourcePath, link.target);
|
const resolvedFile = this.resolveLink(vault, metadata, sourcePath, link.target);
|
||||||
|
|
||||||
if (resolvedFile && resolvedFile.path === targetPath) {
|
if (resolvedFile && resolvedFile.path === targetPath) {
|
||||||
const snippet = this.extractSnippet(lines, link.line - 1, 100);
|
const snippet = this.extractSnippet(lines, link.line - 1, 100);
|
||||||
@@ -265,7 +269,7 @@ export class LinkUtils {
|
|||||||
|
|
||||||
// Process unlinked mentions if requested
|
// Process unlinked mentions if requested
|
||||||
if (includeUnlinked) {
|
if (includeUnlinked) {
|
||||||
const allFiles = app.vault.getMarkdownFiles();
|
const allFiles = vault.getMarkdownFiles();
|
||||||
|
|
||||||
// Build a set of files that already have linked backlinks
|
// Build a set of files that already have linked backlinks
|
||||||
const linkedSourcePaths = new Set(backlinks.map(b => b.sourcePath));
|
const linkedSourcePaths = new Set(backlinks.map(b => b.sourcePath));
|
||||||
@@ -281,7 +285,7 @@ export class LinkUtils {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await app.vault.read(file);
|
const content = await vault.read(file);
|
||||||
const lines = content.split('\n');
|
const lines = content.split('\n');
|
||||||
const occurrences: BacklinkOccurrence[] = [];
|
const occurrences: BacklinkOccurrence[] = [];
|
||||||
|
|
||||||
@@ -345,30 +349,32 @@ export class LinkUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate all wikilinks in a file
|
* Validate all wikilinks in a file
|
||||||
* @param app Obsidian App instance
|
* @param vault Vault adapter for file operations
|
||||||
|
* @param metadata Metadata cache adapter for link resolution
|
||||||
* @param filePath Path of the file to validate
|
* @param filePath Path of the file to validate
|
||||||
* @returns Object with resolved and unresolved links
|
* @returns Object with resolved and unresolved links
|
||||||
*/
|
*/
|
||||||
static async validateWikilinks(
|
static async validateWikilinks(
|
||||||
app: App,
|
vault: IVaultAdapter,
|
||||||
|
metadata: IMetadataCacheAdapter,
|
||||||
filePath: string
|
filePath: string
|
||||||
): Promise<{
|
): Promise<{
|
||||||
resolvedLinks: ResolvedLink[];
|
resolvedLinks: ResolvedLink[];
|
||||||
unresolvedLinks: UnresolvedLink[];
|
unresolvedLinks: UnresolvedLink[];
|
||||||
}> {
|
}> {
|
||||||
const file = app.vault.getAbstractFileByPath(filePath);
|
const file = vault.getAbstractFileByPath(filePath);
|
||||||
if (!(file instanceof TFile)) {
|
if (!(file instanceof TFile)) {
|
||||||
return { resolvedLinks: [], unresolvedLinks: [] };
|
return { resolvedLinks: [], unresolvedLinks: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await app.vault.read(file);
|
const content = await vault.read(file);
|
||||||
const wikilinks = this.parseWikilinks(content);
|
const wikilinks = this.parseWikilinks(content);
|
||||||
|
|
||||||
const resolvedLinks: ResolvedLink[] = [];
|
const resolvedLinks: ResolvedLink[] = [];
|
||||||
const unresolvedLinks: UnresolvedLink[] = [];
|
const unresolvedLinks: UnresolvedLink[] = [];
|
||||||
|
|
||||||
for (const link of wikilinks) {
|
for (const link of wikilinks) {
|
||||||
const resolvedFile = this.resolveLink(app, filePath, link.target);
|
const resolvedFile = this.resolveLink(vault, metadata, filePath, link.target);
|
||||||
|
|
||||||
if (resolvedFile) {
|
if (resolvedFile) {
|
||||||
resolvedLinks.push({
|
resolvedLinks.push({
|
||||||
@@ -377,7 +383,7 @@ export class LinkUtils {
|
|||||||
alias: link.alias
|
alias: link.alias
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
const suggestions = this.findSuggestions(app, link.target);
|
const suggestions = this.findSuggestions(vault, link.target);
|
||||||
unresolvedLinks.push({
|
unresolvedLinks.push({
|
||||||
text: link.raw,
|
text: link.raw,
|
||||||
line: link.line,
|
line: link.line,
|
||||||
|
|||||||
Reference in New Issue
Block a user