Performance Layer

Tag Cache & Autocomplete

Source Files: tagCache.ts, tagCompletionProvider.ts Complexity: Low

The Real-time Tag Cache & Autocomplete System indexes and caches tags across your entire vault. By implementing a custom completion provider, it offers real-time tag autocomplete suggestions as you write.

1. Overview

Maintaining a consistent set of tags across your vault is essential for organization. If you use similar but slightly different tags (like dev, development, and devel), your notes will become fragmented. This feature solves this problem:

  • Fast Tag Caching: Maintains an in-memory cache of all unique tags used across your vault, eliminating the need to read files from disk on every keystroke.
  • Filesystem Sync: Automatically updates the cache in real-time as notes are created, modified, or deleted.
  • Intelligent Autocomplete: Registers a custom completion provider that triggers when typing space or comma in frontmatter tag blocks, suggesting existing tags to prevent duplicate tag names.

2. Under the Hood & Code Walkthrough

2.1 Tag Extraction Algorithm

The cache parses YAML tags from the frontmatter block of Markdown documents using a precise regular expression match:

export function extractTagsFromContent(content: string): string[] {
    const match = content.match(/^---\n([\s\S]*?)\n---/);
    if (!match) { return []; }

    const yaml = match[1];
    const tagsLine = yaml.split('\n').find(line => line.trim().startsWith('tags:'));
    if (!tagsLine) { return []; }

    const bracketMatch = tagsLine.match(/\[(.*?)\]/);
    if (bracketMatch) {
        return bracketMatch[1]
            .split(',')
            .map(t => t.trim())
            .filter(Boolean);
    }
    return [];
}

2.2 Autocomplete Context Verification

The completion provider intercepts keystroke events and checks if the cursor is currently inside a YAML frontmatter tags: [...] block before suggesting completions, ensuring suggestions don't leak into the main note body:

export class TagCompletionProvider implements vscode.CompletionItemProvider {
    constructor(private tagCache: TagCache) {}

    provideCompletionItems(
        document: vscode.TextDocument,
        position: vscode.Position,
        _token: vscode.CancellationToken,
        _context: vscode.CompletionContext
    ): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
        const lineText = document.lineAt(position).text;
        const lineUntilCursor = lineText.slice(0, position.character);

        // Verify cursor is inside: tags: [ ... | ... ]
        if (!lineUntilCursor.match(/tags:\s*\[[^\]]*$/)) {
            return []; // Skip if cursor is outside brackets
        }

        const tags = this.tagCache.getUniqueTags();
        return tags.map(tag => {
            const item = new vscode.CompletionItem(tag, vscode.CompletionItemKind.Keyword);
            item.documentation = new vscode.MarkdownString(`Existing vault tag: \`${tag}\``);
            return item;
        });
    }
}