From 0ffb5f83a13e4be15d62e064ad80188a7c1e4fc0 Mon Sep 17 00:00:00 2001 From: Caleb Campbell Date: Fri, 28 Jun 2024 11:41:14 +1000 Subject: [PATCH] Plugin built. --- main.ts | 132 +------ manifest.json | 12 +- package-lock.json | 658 +++++++++++++++++++++++++++++++++ package.json | 4 +- src/LocalBibleRefSettingTab.ts | 53 +++ src/PassageSuggester.ts | 356 ++++++++++++++++++ src/config/settings.ts | 4 + src/data/books.ts | 73 ++++ styles.css | 12 +- 9 files changed, 1167 insertions(+), 137 deletions(-) create mode 100644 package-lock.json create mode 100644 src/LocalBibleRefSettingTab.ts create mode 100644 src/PassageSuggester.ts create mode 100644 src/config/settings.ts create mode 100644 src/data/books.ts diff --git a/main.ts b/main.ts index 2d07212..e09eb18 100644 --- a/main.ts +++ b/main.ts @@ -1,134 +1,24 @@ -import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; +import { Plugin } from 'obsidian'; +import LocalBibleRefSettingTab from 'src/LocalBibleRefSettingTab'; +import { PassageSuggester } from 'src/PassageSuggester'; +import { LocalBibleRefSettings } from 'src/config/settings'; -// Remember to rename these classes and interfaces! - -interface MyPluginSettings { - mySetting: string; -} - -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' -} - -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; +export default class LocalBibleRefPlugin extends Plugin { + settings: LocalBibleRefSettings; async onload() { await this.loadSettings(); - - // This creates an icon in the left ribbon. - const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - }); - // Perform additional things with the ribbon - ribbonIconEl.addClass('my-plugin-ribbon-class'); - - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status Bar Text'); - - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-sample-modal-simple', - name: 'Open sample modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'sample-editor-command', - name: 'Sample editor command', - editorCallback: (editor: Editor, view: MarkdownView) => { - console.log(editor.getSelection()); - editor.replaceSelection('Sample Editor Command'); - } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-sample-modal-complex', - name: 'Open sample modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } - - // This command will only show up in Command Palette when the check function returns true - return true; - } - } - }); - - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); - - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - console.log('click', evt); - }); - - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); + this.addSettingTab(new LocalBibleRefSettingTab(this.app, this)); + this.registerEditorSuggest(new PassageSuggester(this.app, this.settings)); } - onunload() { - - } + onunload() {} async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + this.settings = await this.loadData(); } async saveSettings() { await this.saveData(this.settings); } -} - -class SampleModal extends Modal { - constructor(app: App) { - super(app); - } - - onOpen() { - const {contentEl} = this; - contentEl.setText('Woah!'); - } - - onClose() { - const {contentEl} = this; - contentEl.empty(); - } -} - -class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; - - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; - } - - display(): void { - const {containerEl} = this; - - containerEl.empty(); - - new Setting(containerEl) - .setName('Setting #1') - .setDesc('It\'s a secret') - .addText(text => text - .setPlaceholder('Enter your secret') - .setValue(this.plugin.settings.mySetting) - .onChange(async (value) => { - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); - } -} +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index dfa940e..2473711 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "sample-plugin", - "name": "Sample Plugin", + "id": "local-bible-ref", + "name": "Local Bible Ref", "version": "1.0.0", "minAppVersion": "0.15.0", - "description": "Demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", - "authorUrl": "https://obsidian.md", - "fundingUrl": "https://obsidian.md/pricing", + "description": "Quickly and easily reference Bible verses stored locally in your Obsidian vault.", + "author": "calebcampbell", + "authorUrl": "https://mastodon.social/@calebcampbell", + "fundingUrl": "", "isDesktopOnly": false } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fcc55f4 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,658 @@ +{ + "name": "local-bible-ref", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@esbuild/android-arm": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.3.tgz", + "integrity": "sha512-1Mlz934GvbgdDmt26rTLmf03cAgLg5HyOgJN+ZGCeP3Q9ynYTNMn2/LQxIl7Uy+o4K6Rfi2OuLsr12JQQR8gNg==", + "dev": true, + "optional": true + }, + "@esbuild/android-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.3.tgz", + "integrity": "sha512-XvJsYo3dO3Pi4kpalkyMvfQsjxPWHYjoX4MDiB/FUM4YMfWcXa5l4VCwFWVYI1+92yxqjuqrhNg0CZg3gSouyQ==", + "dev": true, + "optional": true + }, + "@esbuild/android-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.3.tgz", + "integrity": "sha512-nuV2CmLS07Gqh5/GrZLuqkU9Bm6H6vcCspM+zjp9TdQlxJtIe+qqEXQChmfc7nWdyr/yz3h45Utk1tUn8Cz5+A==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.3.tgz", + "integrity": "sha512-01Hxaaat6m0Xp9AXGM8mjFtqqwDjzlMP0eQq9zll9U85ttVALGCGDuEvra5Feu/NbP5AEP1MaopPwzsTcUq1cw==", + "dev": true, + "optional": true + }, + "@esbuild/darwin-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.3.tgz", + "integrity": "sha512-Eo2gq0Q/er2muf8Z83X21UFoB7EU6/m3GNKvrhACJkjVThd0uA+8RfKpfNhuMCl1bKRfBzKOk6xaYKQZ4lZqvA==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.3.tgz", + "integrity": "sha512-CN62ESxaquP61n1ZjQP/jZte8CE09M6kNn3baos2SeUfdVBkWN5n6vGp2iKyb/bm/x4JQzEvJgRHLGd5F5b81w==", + "dev": true, + "optional": true + }, + "@esbuild/freebsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.3.tgz", + "integrity": "sha512-feq+K8TxIznZE+zhdVurF3WNJ/Sa35dQNYbaqM/wsCbWdzXr5lyq+AaTUSER2cUR+SXPnd/EY75EPRjf4s1SLg==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.3.tgz", + "integrity": "sha512-CLP3EgyNuPcg2cshbwkqYy5bbAgK+VhyfMU7oIYyn+x4Y67xb5C5ylxsNUjRmr8BX+MW3YhVNm6Lq6FKtRTWHQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.3.tgz", + "integrity": "sha512-JHeZXD4auLYBnrKn6JYJ0o5nWJI9PhChA/Nt0G4MvLaMrvXuWnY93R3a7PiXeJQphpL1nYsaMcoV2QtuvRnF/g==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ia32": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.3.tgz", + "integrity": "sha512-FyXlD2ZjZqTFh0sOQxFDiWG1uQUEOLbEh9gKN/7pFxck5Vw0qjWSDqbn6C10GAa1rXJpwsntHcmLqydY9ST9ZA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-loong64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.3.tgz", + "integrity": "sha512-OrDGMvDBI2g7s04J8dh8/I7eSO+/E7nMDT2Z5IruBfUO/RiigF1OF6xoH33Dn4W/OwAWSUf1s2nXamb28ZklTA==", + "dev": true, + "optional": true + }, + "@esbuild/linux-mips64el": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.3.tgz", + "integrity": "sha512-DcnUpXnVCJvmv0TzuLwKBC2nsQHle8EIiAJiJ+PipEVC16wHXaPEKP0EqN8WnBe0TPvMITOUlP2aiL5YMld+CQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-ppc64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.3.tgz", + "integrity": "sha512-BDYf/l1WVhWE+FHAW3FzZPtVlk9QsrwsxGzABmN4g8bTjmhazsId3h127pliDRRu5674k1Y2RWejbpN46N9ZhQ==", + "dev": true, + "optional": true + }, + "@esbuild/linux-riscv64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.3.tgz", + "integrity": "sha512-WViAxWYMRIi+prTJTyV1wnqd2mS2cPqJlN85oscVhXdb/ZTFJdrpaqm/uDsZPGKHtbg5TuRX/ymKdOSk41YZow==", + "dev": true, + "optional": true + }, + "@esbuild/linux-s390x": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.3.tgz", + "integrity": "sha512-Iw8lkNHUC4oGP1O/KhumcVy77u2s6+KUjieUqzEU3XuWJqZ+AY7uVMrrCbAiwWTkpQHkr00BuXH5RpC6Sb/7Ug==", + "dev": true, + "optional": true + }, + "@esbuild/linux-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.3.tgz", + "integrity": "sha512-0AGkWQMzeoeAtXQRNB3s4J1/T2XbigM2/Mn2yU1tQSmQRmHIZdkGbVq2A3aDdNslPyhb9/lH0S5GMTZ4xsjBqg==", + "dev": true, + "optional": true + }, + "@esbuild/netbsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.3.tgz", + "integrity": "sha512-4+rR/WHOxIVh53UIQIICryjdoKdHsFZFD4zLSonJ9RRw7bhKzVyXbnRPsWSfwybYqw9sB7ots/SYyufL1mBpEg==", + "dev": true, + "optional": true + }, + "@esbuild/openbsd-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.3.tgz", + "integrity": "sha512-cVpWnkx9IYg99EjGxa5Gc0XmqumtAwK3aoz7O4Dii2vko+qXbkHoujWA68cqXjhh6TsLaQelfDO4MVnyr+ODeA==", + "dev": true, + "optional": true + }, + "@esbuild/sunos-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.3.tgz", + "integrity": "sha512-RxmhKLbTCDAY2xOfrww6ieIZkZF+KBqG7S2Ako2SljKXRFi+0863PspK74QQ7JpmWwncChY25JTJSbVBYGQk2Q==", + "dev": true, + "optional": true + }, + "@esbuild/win32-arm64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.3.tgz", + "integrity": "sha512-0r36VeEJ4efwmofxVJRXDjVRP2jTmv877zc+i+Pc7MNsIr38NfsjkQj23AfF7l0WbB+RQ7VUb+LDiqC/KY/M/A==", + "dev": true, + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.3.tgz", + "integrity": "sha512-wgO6rc7uGStH22nur4aLFcq7Wh86bE9cOFmfTr/yxN3BXvDEdCSXyKkO+U5JIt53eTOgC47v9k/C1bITWL/Teg==", + "dev": true, + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.3.tgz", + "integrity": "sha512-FdVl64OIuiKjgXBjwZaJLKp0eaEckifbhn10dXWhysMJkWblg3OEEGKSIyhiD5RSgAya8WzP3DNkngtIg3Nt7g==", + "dev": true, + "optional": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/codemirror": { + "version": "5.60.8", + "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.8.tgz", + "integrity": "sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==", + "dev": true, + "requires": { + "@types/tern": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "@types/node": { + "version": "16.18.98", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.98.tgz", + "integrity": "sha512-fpiC20NvLpTLAzo3oVBKIqBGR6Fx/8oAK/SSf7G+fydnXMY1x4x9RZ6sBXhqKlCU21g2QapUsbLlhv3+a7wS+Q==", + "dev": true + }, + "@types/tern": { + "version": "0.23.9", + "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", + "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz", + "integrity": "sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/type-utils": "5.29.0", + "@typescript-eslint/utils": "5.29.0", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.29.0.tgz", + "integrity": "sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", + "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz", + "integrity": "sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==", + "dev": true, + "requires": { + "@typescript-eslint/utils": "5.29.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", + "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", + "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", + "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.29.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "requires": { + "fill-range": "^7.1.1" + } + }, + "builtin-modules": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", + "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", + "dev": true + }, + "debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "esbuild": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.3.tgz", + "integrity": "sha512-9n3AsBRe6sIyOc6kmoXg2ypCLgf3eZSraWFRpnkto+svt8cZNuKTkb1bhQcitBcvIqjNiK7K0J3KPmwGSfkA8g==", + "dev": true, + "requires": { + "@esbuild/android-arm": "0.17.3", + "@esbuild/android-arm64": "0.17.3", + "@esbuild/android-x64": "0.17.3", + "@esbuild/darwin-arm64": "0.17.3", + "@esbuild/darwin-x64": "0.17.3", + "@esbuild/freebsd-arm64": "0.17.3", + "@esbuild/freebsd-x64": "0.17.3", + "@esbuild/linux-arm": "0.17.3", + "@esbuild/linux-arm64": "0.17.3", + "@esbuild/linux-ia32": "0.17.3", + "@esbuild/linux-loong64": "0.17.3", + "@esbuild/linux-mips64el": "0.17.3", + "@esbuild/linux-ppc64": "0.17.3", + "@esbuild/linux-riscv64": "0.17.3", + "@esbuild/linux-s390x": "0.17.3", + "@esbuild/linux-x64": "0.17.3", + "@esbuild/netbsd-x64": "0.17.3", + "@esbuild/openbsd-x64": "0.17.3", + "@esbuild/sunos-x64": "0.17.3", + "@esbuild/win32-arm64": "0.17.3", + "@esbuild/win32-ia32": "0.17.3", + "@esbuild/win32-x64": "0.17.3" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dev": true, + "requires": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + } + }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "obsidian": { + "version": "1.5.7-1", + "resolved": "https://registry.npmjs.org/obsidian/-/obsidian-1.5.7-1.tgz", + "integrity": "sha512-T5ZRuQ1FnfXqEoakTTHVDYvzUEEoT8zSPnQCW31PVgYwG4D4tZCQfKHN2hTz1ifnCe8upvwa6mBTAP2WUA5Vng==", + "dev": true, + "requires": { + "@types/codemirror": "5.60.8", + "moment": "2.29.4" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 6a00766..75eb673 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "obsidian-sample-plugin", + "name": "local-bible-ref", "version": "1.0.0", - "description": "This is a sample plugin for Obsidian (https://obsidian.md)", + "description": "Quickly and easily reference Bible verses stored locally in your Obsidian vault.", "main": "main.js", "scripts": { "dev": "node esbuild.config.mjs", diff --git a/src/LocalBibleRefSettingTab.ts b/src/LocalBibleRefSettingTab.ts new file mode 100644 index 0000000..9ccb03a --- /dev/null +++ b/src/LocalBibleRefSettingTab.ts @@ -0,0 +1,53 @@ +import LocalBibleRefPlugin from "main"; +import { App, Notice, PluginSettingTab, Setting } from "obsidian"; + +export default class LocalBibleRefSettingTab extends PluginSettingTab { + plugin: LocalBibleRefPlugin; + + constructor(app: App, plugin: LocalBibleRefPlugin) { + super(app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + containerEl.empty(); + + this.containerEl.createEl('h1', { text: 'Local Bible Ref' }); + + let biblesPathTimeout: NodeJS.Timeout; + new Setting(containerEl) + .setName("Bibles Path") + .setDesc("The path to the folder containing your bibles.") + .addText(text => text + .setPlaceholder("e.g. Data/Bibles") + .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}.`); + }, 500); + })); + + let defaultVersionTimeout: NodeJS.Timeout; + new Setting(containerEl) + .setName("Default Version Shorthand") + .setDesc("The version to use by default - shorthand.") + .addText(text => text + .setPlaceholder("e.g. ESV") + .onChange(async (value) => { + this.plugin.settings.defaultVersionShorthand = value; + await this.plugin.saveSettings(); + + clearTimeout(defaultVersionTimeout); + defaultVersionTimeout = setTimeout(async () => { + const path = `${this.plugin.settings.biblesPath}/${value}`; + const exists = await this.app.vault.adapter.exists(path); + if (!exists) new Notice(`Version folder doesn't exist at path: ${path}.`); + }, 500); + })); + } +} \ No newline at end of file diff --git a/src/PassageSuggester.ts b/src/PassageSuggester.ts new file mode 100644 index 0000000..8d12d3d --- /dev/null +++ b/src/PassageSuggester.ts @@ -0,0 +1,356 @@ +import { + App, + Editor, + EditorPosition, + EditorSuggest, + EditorSuggestContext, + Notice, + TFile +} from "obsidian"; +import { BOOKS, Book } from "./data/books"; +import { LocalBibleRefSettings } from "./config/settings"; + +interface ChapterReference { + startChapter: number; + startVerse: number; + endChapter: number; + endVerse: number; +} + +interface PassageReference extends ChapterReference { + book: Book; +} + +interface PassageSuggestion { + passageRef: string; + suggestText: string; + fullText: string; +} + +export class PassageSuggester extends EditorSuggest { + private settings: LocalBibleRefSettings; + private passageRegex: RegExp; + + constructor(app: App, settings: LocalBibleRefSettings) { + super(app); + this.settings = settings; + + // builds the book matching regex + let regexString = BOOKS.reduce((string, book) => { + return `${string}${book.name}|${book.aliases.join("|")}|`; + }, "^\\-\\- ?("); + regexString = regexString.slice(0, -1); + regexString += ") ?(\\d{1,3}(?::\\d{1,3})?(?: ?\\- ?\\d{1,3}(?::\\d{1,3})?)?)$"; + this.passageRegex = new RegExp(regexString, "i"); + } + + onTrigger(cursor: EditorPosition, editor: Editor, _: TFile | 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; + + // must be a passage ref + const isPassage = this.passageRegex.test(line); + if (!isPassage) return null; + + // trigger info + return { end: cursor, query: line, start: { ch: 0, line: cursor.line } }; + } + + async getSuggestions(context: EditorSuggestContext): Promise { + const passageRef = this.parsePassageRef(context.query); + if (!passageRef) return []; + + // grab all chapters in the range + let chapterTexts = await this.getChapterTexts(passageRef); + console.log(passageRef); + console.log(BOOKS); + if (!chapterTexts) return []; + + // split first chapter by start verse + const chapterFromVerse = this.getChapterFromStartVerse(chapterTexts[0], passageRef); + if (!chapterFromVerse) return []; + chapterTexts[0] = chapterFromVerse; + + // split last chapter by end verse + const lastIndex = chapterTexts.length - 1; + const chapterToVerse = this.getChapterToEndVerse(chapterTexts[lastIndex], passageRef); + if (!chapterToVerse) return []; + chapterTexts[lastIndex] = chapterToVerse; + + // clean up chapter texts + chapterTexts = chapterTexts.map((text, i) => { + text = this.removeHeadings(text); + text = this.removeFootnoteRefs(text); + text = this.removeEOF(text); + + const chapter = passageRef.startChapter + i; + if (text.startsWith("> ")) { + const quoteMd = text.match(/^(?:> )+/)![0]; + return text.replace(quoteMd, `${quoteMd}**${chapter}.** `); + } + + if (text.startsWith("- ")) { + const listMd = text.match(/^(?:- )+/)![0]; + return text.replace(listMd, `${listMd}**${chapter}.** `); + } + + return text = `**${chapter}.** ${text}`; + }); + + // suggest + return [{ + passageRef: this.encodePassageRef(passageRef), + suggestText: this.generateSuggestText(chapterTexts[0]), + fullText: chapterTexts.join("\n\n") + }]; + } + + renderSuggestion(item: PassageSuggestion, el: HTMLElement): void { + el.innerText = item.suggestText; + } + + selectSuggestion(item: PassageSuggestion, _: MouseEvent | KeyboardEvent): void { + if (!this.context) return; + let text = item.fullText.replace(/\n/gm, "\n> "); + text = `> [!bible]+ ${item.passageRef}\n> ${text}`; + this.context.editor.replaceRange(text, this.context.start, this.context.end); + } + + /** + * Parses a passage reference from the given text. + * + * @param text the text to parse. + * @returns a parsed PassageReference or null. + */ + private parsePassageRef(text: string): PassageReference | null { + const passageRefMatch = text.match(this.passageRegex); + if (!passageRefMatch) return null; + + let chapterRef = this.parseMultiChapterRef(passageRefMatch[2]); + if (!chapterRef) chapterRef = this.parseMultiPartChapterRef(passageRefMatch[2]); + if (!chapterRef) chapterRef = this.parsePartChapterRef(passageRefMatch[2]); + if (!chapterRef) return null; + + const book = this.getBook(passageRefMatch[1]); + if (!book) return null; + + return { book, ...chapterRef }; + } + + /** + * Parses a multi-chapter reference from the given text. + * Reference is in the format: `startChapter[ - endChapter]`. + * + * @param text the text to parse. + * @returns a parsed ChapterReference or null. + */ + private parseMultiChapterRef(text: string): ChapterReference | null { + const regex = /^(\d{1,3})(?: ?- ?(\d{1,3}))?$/i; + const match = text.match(regex); + if (!match) return null; + + const startChapter = parseInt(match[1]); + return { + startChapter, + startVerse: 1, + endChapter: match[2] ? parseInt(match[2]) : startChapter, + endVerse: -1, + }; + } + + /** + * Parses a multi-(part)-chapter reference from the given text. + * Reference is in the format: `startChapter:startVerse - endChapter:endVerse`. + * + * @param text the text to parse. + * @returns a parsed ChapterReference or null. + */ + private parseMultiPartChapterRef(text: string): ChapterReference | null { + const regex = /^(\d{1,3}):(\d{1,3}) ?- ?(\d{1,3}):(\d{1,3})$/i; + const match = text.match(regex); + if (!match) return null; + + return { + startChapter: parseInt(match[1]), + startVerse: parseInt(match[2]), + endChapter: parseInt(match[3]), + endVerse: parseInt(match[4]), + }; + } + + /** + * Parses a part-chapter reference from the given text. + * Reference is in the format: `startChapter:startVerse[-endVerse]`. + * + * @param text the text to parse. + * @returns a parsed ChapterReference or null. + */ + private parsePartChapterRef(text: string): ChapterReference | null { + const regex = /^(\d{1,3}):(\d{1,3})(?: ?- ?(\d{1,3}))?$/i; + const match = text.match(regex); + if (!match) return null; + + const startChapter = parseInt(match[1]); + const startVerse = parseInt(match[2]); + return { + startChapter, + startVerse, + endChapter: startChapter, + endVerse: match[3] ? parseInt(match[3]) : startVerse, + }; + } + + /** + * Retrieves a book based on it's alias. + * @param alias the alias of the book. + * @returns the book object if found, otherwise undefined. + */ + private getBook(alias: string): Book | undefined { + alias = alias.toLowerCase(); + return BOOKS.find((book) => { + if (book.name.toLowerCase() === alias) return book; + if (book.aliases.includes(alias)) return book; + }) + } + + /** + * Retrieves the texts of the chapters within the specified passage reference. + * + * @param ref the passage reference. + * @returns a promise - an array of chapter texts or null. + */ + private async getChapterTexts(ref: PassageReference): Promise { + let basePath = ""; + for (let alias of [ref.book.name, ...ref.book.aliases]) { + console.log(alias); + basePath = [ + this.settings.biblesPath, + this.settings.defaultVersionShorthand, + alias + ].join("/"); + console.log(basePath); + if (await this.app.vault.adapter.exists(basePath)) { + ref.book.aliases.push(ref.book.name); + ref.book.aliases.remove(alias); + ref.book.name = alias; + console.log(ref); + break; + } + } + + const chapterTexts: string[] = []; + + for (let ch = ref.startChapter; ch <= ref.endChapter; ch++) { + const path = basePath + `/${ref.book.name} ${ch}.md`; + const file = this.app.vault.getFileByPath(path); + + if (!file) { + new Notice(`Could not find chapter at: ${path}`); + return null; + } + + chapterTexts.push(await this.app.vault.cachedRead(file)); + } + + return chapterTexts; + } + + /** + * Retrieves the text in the chapter from the start verse to the end. + * + * @param text the text to cut. + * @param ref the passage reference. + * @returns the text from the start verse or null. + */ + private getChapterFromStartVerse(text: string, ref: PassageReference): string | null { + const regex = new RegExp(`(?:> |- )*${ref.startVerse}`); + const match = text.match(regex); + if (!match) { + const verse = ref.startVerse; + const chapter = ref.startChapter; + const book = ref.book.name; + new Notice(`Could not find verse ${verse} in ${book} ${chapter}.`); + return null; + } + + const verseLabel = match[0]; + const parts = text.split(regex); + return verseLabel + parts[1]; + }; + + /** + * Retrieves the text in the chapter from the start to the end verse. + * + * @param text the text to cut. + * @param ref the passage reference. + * @returns the text up to the end verse or null. + */ + private getChapterToEndVerse(text: string, ref: PassageReference): string | null { + if (ref.endVerse === -1) return text; + + const regex = new RegExp(`(?:^(?:> |- )*)?${ref.endVerse + 1}`, "m"); + return text.split(regex, 1)[0].trim(); + }; + + /** Removes headings from the given text. */ + private removeHeadings(text: string): string { + return text.replace(/^#.*[\n\r\f]*/gm, ""); + } + + /** Removes footnote refs from the given text. */ + private removeFootnoteRefs(text: string): string { + return text.replace(/ \[\^\d{1,4}\]/g, ""); + } + + /** Removes the end-of-file content from the given text. */ + private removeEOF(text: string): string { + // split at chapter divider + let split = text.split(/^\-\-\-$/m); + // split at footnotes + split = split[0].split(/^\[\^\d+\]:/m, 1); + return split[0].trim(); + } + + /** Generates suggestion text. */ + private generateSuggestText(text: string): string { + text = text.split(/<\/sup>/, 2)[1]; + text = text.replace(/(?:\d+<\/sup>|> |- )/g, ""); + text = text.replace(/Lord<\/span>/g, "Lord"); + text = text.replace(/\n/g, " "); + text = text.replace(/ /g, " "); + return text.slice(0, 45) + "..."; + } + + /** + * Encodes a PassageReference into a string representation. + * + * @param ref the PassageReference to encode. + * @returns the encoded string representation. + */ + private encodePassageRef(ref: PassageReference): string { + // whole chapter/s ref + if (ref.startVerse === 1 && ref.endVerse === -1) { + if (ref.startChapter === ref.endChapter) { + return `${ref.book.name} ${ref.startChapter}`; + } + return `${ref.book.name} ${ref.startChapter}-${ref.endChapter}`; + } + + // part chapter ref + if (ref.startChapter === ref.endChapter) { + if (ref.startVerse === ref.endVerse) { + return `${ref.book.name} ${ref.startChapter}:${ref.startVerse}`; + } + return `${ref.book.name} ${ref.startChapter}:${ref.startVerse}-${ref.endVerse}`; + } + + // part chapters ref + const a = `${ref.startChapter}:${ref.startVerse}`; + const b = `${ref.endChapter}:${ref.endVerse}`; + return `${ref.book.name} ${a}-${b}`; + } +} \ No newline at end of file diff --git a/src/config/settings.ts b/src/config/settings.ts new file mode 100644 index 0000000..5c6d140 --- /dev/null +++ b/src/config/settings.ts @@ -0,0 +1,4 @@ +export interface LocalBibleRefSettings { + biblesPath: string; + defaultVersionShorthand: string; +} \ No newline at end of file diff --git a/src/data/books.ts b/src/data/books.ts new file mode 100644 index 0000000..1feb69c --- /dev/null +++ b/src/data/books.ts @@ -0,0 +1,73 @@ +export interface Book { + name: string; + aliases: string[]; +} + +export const BOOKS: Book[] = [ + { name: "Genesis", aliases: ["gen"] }, + { name: "Exodus", aliases: ["exo"] }, + { name: "Leviticus", aliases: ["lev"] }, + { name: "Numbers", aliases: ["num"] }, + { name: "Deuteronomy", aliases: ["deu"] }, + { name: "Joshua", aliases: ["jos"] }, + { name: "Judges", aliases: ["jdg"] }, + { name: "Ruth", aliases: ["rut"] }, + { name: "1 Samuel", aliases: ["1sa"] }, + { name: "2 Samuel", aliases: ["2sa"] }, + { name: "1 Kings", aliases: ["1ki"] }, + { name: "2 Kings", aliases: ["2ki"] }, + { name: "1 Chronicles", aliases: ["1ch"] }, + { name: "2 Chronicles", aliases: ["2ch"] }, + { name: "Ezra", aliases: ["ezr"] }, + { name: "Nehemiah", aliases: ["neh"] }, + { name: "Esther", aliases: ["est"] }, + { name: "Job", aliases: ["job"] }, + { name: "Psalms", aliases: ["psa"] }, + { name: "Proverbs", aliases: ["pro"] }, + { name: "Ecclesiastes", aliases: ["ecc"] }, + { name: "Song of Songs", aliases: ["sng", "Song of Solomon"] }, + { name: "Isaiah", aliases: ["isa"] }, + { name: "Jeremiah", aliases: ["jer"] }, + { name: "Lamentations", aliases: ["lam"] }, + { name: "Ezekiel", aliases: ["ezk"] }, + { name: "Daniel", aliases: ["dan"] }, + { name: "Hosea", aliases: ["hos"] }, + { name: "Joel", aliases: ["jol"] }, + { name: "Amos", aliases: ["amo"] }, + { name: "Obadiah", aliases: ["oba"] }, + { name: "Jonah", aliases: ["jon"] }, + { name: "Micah", aliases: ["mic"] }, + { name: "Nahum", aliases: ["nam"] }, + { name: "Habakkuk", aliases: ["hab"] }, + { name: "Zephaniah", aliases: ["zep"] }, + { name: "Haggai", aliases: ["hag"] }, + { name: "Zechariah", aliases: ["zec"] }, + { name: "Malachi", aliases: ["mal"] }, + { name: "Matthew", aliases: ["mat"] }, + { name: "Mark", aliases: ["mrk"] }, + { name: "Luke", aliases: ["luk"] }, + { name: "John", aliases: ["jhn"] }, + { name: "Acts", aliases: ["act"] }, + { name: "Romans", aliases: ["rom"] }, + { name: "1 Corinthians", aliases: ["1co"] }, + { name: "2 Corinthians", aliases: ["2co"] }, + { name: "Galatians", aliases: ["gal"] }, + { name: "Ephesians", aliases: ["eph"] }, + { name: "Philippians", aliases: ["php"] }, + { name: "Colossians", aliases: ["col"] }, + { name: "1 Thessalonians", aliases: ["1th"] }, + { name: "2 Thessalonians", aliases: ["2th"] }, + { name: "1 Timothy", aliases: ["1ti"] }, + { name: "2 Timothy", aliases: ["2ti"] }, + { name: "Titus", aliases: ["tit"] }, + { name: "Philemon", aliases: ["phm"] }, + { name: "Hebrews", aliases: ["heb"] }, + { name: "James", aliases: ["jas"] }, + { name: "1 Peter", aliases: ["1pe"] }, + { name: "2 Peter", aliases: ["2pe"] }, + { name: "1 John", aliases: ["1jn"] }, + { name: "2 John", aliases: ["2jn"] }, + { name: "3 John", aliases: ["3jn"] }, + { name: "Jude", aliases: ["jud"] }, + { name: "Revelation", aliases: ["rev"] }, +]; \ No newline at end of file diff --git a/styles.css b/styles.css index 71cc60f..31fabc5 100644 --- a/styles.css +++ b/styles.css @@ -1,8 +1,4 @@ -/* - -This CSS file will be included with your plugin, and -available in the app when your plugin is enabled. - -If your plugin does not need CSS, delete this file. - -*/ +.callout[data-callout="bible"] { + --callout-color: var(--callout-quote); + --callout-icon: lucide-book-open-text; +} \ No newline at end of file