From 218ddbfea7d46cca5dba8b7c03ed36e4769ddc12 Mon Sep 17 00:00:00 2001 From: Caleb Campbell Date: Thu, 17 Jul 2025 08:06:23 +1000 Subject: [PATCH] Added autocomplete suggestions to the bibles path setting field. Cleaned up a few things. --- package-lock.json | 2 +- src/local-bible-ref-setting-tab.ts | 30 ++++++++++++++++------------ src/passage-reference.ts | 4 ++-- src/passage-suggest.ts | 32 ++++++++++++++++++++++++------ src/path-suggest.ts | 27 +++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 22 deletions(-) create mode 100644 src/path-suggest.ts diff --git a/package-lock.json b/package-lock.json index 82634ec..f684282 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "local-bible-ref", - "version": "1.0.1", + "version": "1.0.2", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/src/local-bible-ref-setting-tab.ts b/src/local-bible-ref-setting-tab.ts index f333e7f..919b935 100644 --- a/src/local-bible-ref-setting-tab.ts +++ b/src/local-bible-ref-setting-tab.ts @@ -1,9 +1,10 @@ import LocalBibleRefPlugin from 'main'; import { App, Notice, PluginSettingTab, Setting } from 'obsidian'; import { PassageFormat } from './passage-reference'; +import { PathSuggest } from './path-suggest'; export default class LocalBibleRefSettingTab extends PluginSettingTab { - plugin: LocalBibleRefPlugin; + private plugin: LocalBibleRefPlugin; constructor(app: App, plugin: LocalBibleRefPlugin) { super(app, plugin); @@ -18,19 +19,22 @@ export default class LocalBibleRefSettingTab extends PluginSettingTab { new Setting(containerEl) .setName('Bibles Path') .setDesc('The path to the folder containing your bibles.') - .addText(text => text - .setPlaceholder('e.g. Data/Bibles') - .setValue(this.plugin.settings.biblesPath) - .onChange(async (value) => { - this.plugin.settings.biblesPath = value; - await this.plugin.saveSettings(); + .addText(text => { + text.setPlaceholder('e.g. Data/Bibles') + .setValue(this.plugin.settings.biblesPath) + .onChange(async (value) => { + this.plugin.settings.biblesPath = value; + await this.plugin.saveSettings(); - clearTimeout(biblesPathTimeout); - biblesPathTimeout = setTimeout(async () => { - const exists = await this.app.vault.adapter.exists(value); - if (!exists) new Notice(`Bibles folder doesn't exist at path: ${value}.`); - }, 1000); - })); + clearTimeout(biblesPathTimeout); + biblesPathTimeout = setTimeout(async () => { + const exists = await this.app.vault.adapter.exists(value); + if (!exists) new Notice(`Bibles folder doesn't exist at path: ${value}.`); + }, 1000); + }); + + new PathSuggest(this.app, text.inputEl); + }); let defaultVersionTimeout: NodeJS.Timeout; new Setting(containerEl) diff --git a/src/passage-reference.ts b/src/passage-reference.ts index 104cf34..dab31a6 100644 --- a/src/passage-reference.ts +++ b/src/passage-reference.ts @@ -68,7 +68,7 @@ export class PassageReference implements ChapterReference, PassageOptions { if (this.startVerse === 1 && this.endVerse === -1) { if (this.startChapter === this.endChapter) return ( - this.book.name + `${this.startChapter} - ${this.version}` + this.book.name + ` ${this.startChapter} - ${this.version}` ); return ( `${this.book.name} ${this.startChapter}-` + @@ -81,7 +81,7 @@ export class PassageReference implements ChapterReference, PassageOptions { if (this.startVerse === this.endVerse) return ( this.book.name + - `${this.startChapter}:${this.startVerse} - ${this.version}` + ` ${this.startChapter}:${this.startVerse} - ${this.version}` ); return ( `${this.book.name} ${this.startChapter}:` + diff --git a/src/passage-suggest.ts b/src/passage-suggest.ts index a023992..efc9cc0 100644 --- a/src/passage-suggest.ts +++ b/src/passage-suggest.ts @@ -5,6 +5,8 @@ import { EditorSuggest, EditorSuggestContext, EditorSuggestTriggerInfo, + Notice, + TFile, TFolder, } from "obsidian"; import { PassageFormat, PassageReference } from "./passage-reference"; @@ -12,6 +14,7 @@ import { LocalBibleRefSettings } from "./settings"; export class PassageSuggest extends EditorSuggest { private settings: LocalBibleRefSettings; + private noSettingsNotice: Notice; constructor(app: App, settings: LocalBibleRefSettings) { super(app); @@ -21,15 +24,27 @@ export class PassageSuggest extends EditorSuggest { onTrigger( cursor: EditorPosition, editor: Editor, - _: any + _: TFile | null ): EditorSuggestTriggerInfo | null { - // min ref length is 6 ('--gen1') - if (cursor.ch < 6) return null; - // line must start with '--' const line = editor.getLine(cursor.line); if (!line.startsWith("--")) return null; + // if no settings, alert user + if (!this.settings.biblesPath) { + if (!this.noSettingsNotice?.noticeEl.isShown()) { + const noticeText = "Local Bible Ref settings are not " + + "configured. Please set the bibles path before " + + "attempting to reference passages."; + this.noSettingsNotice = new Notice(noticeText); + } + + return null; + } + + // min ref length is 6 ('--gen1') + if (cursor.ch < 6) return null; + // must be a passage ref const isPassage = PassageReference.regExp.test(line); if (!isPassage) return null; @@ -48,9 +63,14 @@ export class PassageSuggest extends EditorSuggest { async getSuggestions( context: EditorSuggestContext ): Promise { + let version = this.settings.defaultVersionShorthand; + if (!version) { + const folder = this.app.vault.getFolderByPath(this.settings.biblesPath); + version = folder?.children?.filter(c => c instanceof TFolder)?.first()?.name ?? ''; + } const passageRef = PassageReference.parse( context.query, - this.settings.defaultVersionShorthand, + version, this.settings.defaultPassageFormat, ); if (!passageRef) return []; @@ -90,7 +110,7 @@ export class PassageSuggest extends EditorSuggest { } renderSuggestion(item: PassageSuggestion, el: HTMLElement): void { - el.innerText = item.excerpt; + el.setText(item.excerpt); } selectSuggestion( diff --git a/src/path-suggest.ts b/src/path-suggest.ts new file mode 100644 index 0000000..f7e498e --- /dev/null +++ b/src/path-suggest.ts @@ -0,0 +1,27 @@ +import { AbstractInputSuggest, App } from "obsidian"; + +export class PathSuggest extends AbstractInputSuggest { + private textInputEl: HTMLInputElement | HTMLDivElement; + + constructor(app: App, textInputEl: HTMLInputElement | HTMLDivElement) { + super(app, textInputEl); + this.textInputEl = textInputEl; + } + + async getSuggestions(query: string): Promise { + let searchPath = ''; + if (await this.app.vault.adapter.exists(query)) searchPath = query; + let folders = (await this.app.vault.adapter.list(searchPath)).folders; + folders = folders.filter(folder => !folder.startsWith('.') && folder.startsWith(query)); + return folders; + } + + renderSuggestion(item: string, el: HTMLElement): void { + el.setText(item); + } + + selectSuggestion(item: string, _: MouseEvent | KeyboardEvent): void { + this.setValue(item); + this.textInputEl.dispatchEvent(new Event('input')); + } +} \ No newline at end of file