From b41d3275d99956f9ca1597e11cecb7b0e75ee15b Mon Sep 17 00:00:00 2001 From: Daniel Bauer Date: Thu, 12 Dec 2024 23:26:51 +0100 Subject: [PATCH] ufuzzy suggestions --- package-lock.json | 6 +++ package.json | 1 + src/DataviewSuggester.ts | 83 +++++++++++++++++++++++++++------------- src/main.ts | 19 ++++++++- 4 files changed, 81 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6cb3b4..64d7c77 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@leeoniya/ufuzzy": "^1.0.17", "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-node-resolve": "^15.3.0", "rollup": "^2.79.2", @@ -1093,6 +1094,11 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@leeoniya/ufuzzy": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/@leeoniya/ufuzzy/-/ufuzzy-1.0.17.tgz", + "integrity": "sha512-bGNsu11VP70ZgN+bP9SOQ9rsX6Tu/RzLJQ5F5TzgM9IaZncZweu7lDwcMfPAQ9RW3TVMfnbMCD/NnowkjyT+dQ==" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "license": "MIT", diff --git a/package.json b/package.json index 4fe4377..6a19536 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "typescript": "4.7.4" }, "dependencies": { + "@leeoniya/ufuzzy": "^1.0.17", "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-node-resolve": "^15.3.0", "rollup": "^2.79.2", diff --git a/src/DataviewSuggester.ts b/src/DataviewSuggester.ts index 20181d5..85086ce 100644 --- a/src/DataviewSuggester.ts +++ b/src/DataviewSuggester.ts @@ -9,8 +9,12 @@ import { TFile, } from "obsidian"; import { getTriggerText } from "./trigger"; +import uFuzzy from "@leeoniya/ufuzzy"; export class DataviewSuggester extends EditorSuggest { + suggestionsList: string[] = []; + searcher: uFuzzy = new uFuzzy({}); + constructor(plugin: Plugin) { super(plugin.app); } @@ -36,34 +40,22 @@ export class DataviewSuggester extends EditorSuggest { getSuggestions( context: EditorSuggestContext, ): string[] | Promise { - const suggestions: string[] = []; - // TODO use official dv api? - // @ts-ignore - for (const page of this.app.plugins.plugins.dataview.index.pages) { - const fields = page[1].fields; - for (let [key, val] of fields) { - let arrayVal; - if (!Array.isArray(val)) { - arrayVal = [val]; - } else { - arrayVal = val; - } + let idxs = this.searcher.filter(this.suggestionsList, context.query); + if (idxs != null && idxs.length > 0) { + let info = this.searcher.info( + idxs, + this.suggestionsList, + context.query, + ); + let order = this.searcher.sort( + info, + this.suggestionsList, + context.query, + ); - for (const value of arrayVal) { - // TODO whitespace in match - // Fuzzy matching? - const suggestion = key + ":: " + value; - if ( - suggestion.includes(context.query) && - !suggestions.includes(suggestion) - ) { - suggestions.push(key + ":: " + value); - } - } - } + return order.map((i) => this.suggestionsList[info.idx[i]]); } - console.log(suggestions); - return suggestions; + return []; } renderSuggestion(value: string, el: HTMLElement): void { @@ -71,7 +63,6 @@ export class DataviewSuggester extends EditorSuggest { } selectSuggestion(value: string, evt: MouseEvent | KeyboardEvent): void { - console.log("selected", value); const { editor, start, end } = this.context!; editor.replaceRange(value, start, end); @@ -81,4 +72,42 @@ export class DataviewSuggester extends EditorSuggest { }; editor.setCursor(newCursorPos); } + + public buildNewIndex() { + console.log("Rebuilding dataview suggestion index"); + const newSuggestions: string[] = []; + + // Iterate all pages of the Dataview index, and ingest all fields into suggestions + // TODO: can we use official dataview api? + // @ts-ignore + for (const page of this.app.plugins.plugins.dataview.index.pages) { + const fields = page[1].fields; + for (let [key, val] of fields) { + // fields can be a single value or a dict, so we need to handle both + let arrayVal; + if (!Array.isArray(val)) { + arrayVal = [val]; + } else { + arrayVal = val; + } + + // Add composite value "key:: value" to suggestions list + for (const value of arrayVal) { + const compositeValue = key + ":: " + value; + if (newSuggestions.indexOf(compositeValue) === -1) { + newSuggestions.push(compositeValue); + } + } + } + } + + // replace old index + this.suggestionsList = newSuggestions; + } + + // possible types: update, rename, delete. rename has oldPath + public onMetadataChange(type: string, file: TFile, oldPath?: string) { + // TODO handle efficiently with delta updates + this.buildNewIndex(); + } } diff --git a/src/main.ts b/src/main.ts index 5270ab2..329baec 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { App, Plugin, PluginSettingTab, Setting } from "obsidian"; +import { App, Plugin, PluginSettingTab, Setting, TFile } from "obsidian"; import { DataviewSuggester } from "./DataviewSuggester"; interface DataviewAutocompleteSettings { @@ -19,6 +19,23 @@ export default class DataviewAutocompletePlugin extends Plugin { // register suggester this.suggester = new DataviewSuggester(this); this.registerEditorSuggest(this.suggester); + + this.registerEvent( + // @ts-ignore + this.app.metadataCache.on("dataview:index-ready", () => { + this.suggester.buildNewIndex(); + }), + ); + + this.registerEvent( + this.app.metadataCache.on( + // @ts-ignore + "dataview:metadata-change", + (type: string, file: TFile, oldPath?: string) => { + this.suggester.onMetadataChange(type, file, oldPath); + }, + ), + ); } onunload() {}