Skip to content

Commit

Permalink
Merge pull request #29 from jaanonim/verse-quoting
Browse files Browse the repository at this point in the history
Verse quoting
  • Loading branch information
jaanonim authored Sep 1, 2024
2 parents 7f8753d + d94d90f commit 6551d82
Show file tree
Hide file tree
Showing 13 changed files with 223 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Obsidian Plugin Release
# credits: https://github.com/scambier/obsidian-omnisearch/blob/master/.github/workflows/release.yml

env:
VERSION: 1.2.5
VERSION: 1.3.0

on: [workflow_dispatch]

Expand Down
7 changes: 2 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@

**List of supported languages can be found [here](./Languages.md).**

You need to just type for example: `@ John 1:1-6`.
You need to just type for example: `@ John 1:1-6` to link verse to quote verse use for example: `> John 1:1-6`.

`@` char is a trigger for suggestion and it can be changed in settings.
`@` and `>` char is a trigger for suggestion and it can be changed in settings.
In settings you can select witch version of bible you want to use and books names in witch language will be detected.

I'm from Poland so plugin supports polish books names (eq. `J 1:1-6`, `Mt 24,1`). If you would like it to support your language books names read the guide in [Languages.md](./Languages.md).

**New future 🎉** <br>
Preview of verse after hover over the link.

The plugins is heavily inspired by [obsidian-bible-reference](https://github.com/tim-hub/obsidian-bible-reference) (also i have "borrowed" some code from there) so check it out.

It's available in Obsidian community plugins as
Expand Down
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "youversion-linker",
"name": "YouVersion Linker",
"version": "1.2.5",
"version": "1.3.0",
"minAppVersion": "0.15.0",
"description": "Automatically link bible verses in your notes to YouVersion bible.",
"author": "jaanonim",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "youversion-linker",
"version": "1.2.5",
"version": "1.3.0",
"description": "Obsidian plugin that automatically link bible verses to YouVersion bible.",
"main": "main.js",
"scripts": {
Expand Down
82 changes: 62 additions & 20 deletions src/EditorSuggester.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import getBooks from "./Books";
import { bookRegex, linkRegex, separatorRegex } from "./Regex";
import { ObsidianYouversionLinkerSettings } from "./SettingsData";
import Verse from "./Verse";
import VerseEmbed from "./VerseEmbed";
import VerseLink from "./VerseLink";
import ObsidianYouversionLinker from "./main";
import {
Expand All @@ -26,8 +28,19 @@ export class EditorSuggester extends EditorSuggest<VerseLink> {
file: TFile | null
): EditorSuggestTriggerInfo | null {
const currentLine = editor.getLine(cursor.line);
const pos = currentLine.search(new RegExp(this.settings.trigger, "u"));
if (pos < 0) return null;

const link_pos = currentLine.search(
new RegExp(this.settings.linkTrigger, "u")
);
const embed_pos = currentLine.search(
new RegExp(this.settings.embedTrigger, "u")
);

if (link_pos < 0 && embed_pos < 0) return null;
const isLink =
link_pos >= 0 &&
(cursor.ch - link_pos > cursor.ch - embed_pos || embed_pos < 0);
const pos = isLink ? link_pos : embed_pos;
const currentContent = currentLine.substring(pos + 1, cursor.ch).trim();

const matches = currentContent.match(linkRegex);
Expand All @@ -42,26 +55,41 @@ export class EditorSuggester extends EditorSuggest<VerseLink> {
line: cursor.line,
ch: pos,
},
query: match,
query: (isLink ? "@" : ">") + match,
};
}
return null;
}, null);
}

getSuggestions(
context: EditorSuggestContext
): VerseLink[] | Promise<VerseLink[]> {
return getSuggestionsFromQuery(context.query, this.settings);
const query = context.query;
const isLink = query[0] !== ">";

if (query[0] !== "@" && query[0] !== ">") {
console.error(`INTERNAL: query should start with @ or >`);
}

return getSuggestionsFromQuery(
query.substring(1),
isLink,
this.settings
);
}

renderSuggestion(value: VerseLink, el: HTMLElement): void {
renderSuggestion(value: Verse, el: HTMLElement): void {
value.render(el);
}

selectSuggestion(value: VerseLink, evt: MouseEvent | KeyboardEvent): void {
async selectSuggestion(
value: Verse,
evt: MouseEvent | KeyboardEvent
): Promise<void> {
if (this.context) {
(this.context.editor as Editor).replaceRange(
value.toLink(),
await value.toReplace(),
this.context.start,
this.context.end
);
Expand All @@ -71,8 +99,9 @@ export class EditorSuggester extends EditorSuggest<VerseLink> {

export function getSuggestionsFromQuery(
query: string,
isLink: boolean,
settings: ObsidianYouversionLinkerSettings
): VerseLink[] {
): Verse[] {
console.debug("get suggestion for query ", query.toLowerCase());

const bookName = query.match(bookRegex)?.first();
Expand All @@ -95,17 +124,30 @@ export function getSuggestionsFromQuery(
const verseEndNumber =
numbers.length === 3 ? parseInt(numbers[2]) : undefined;

return booksUrl.flatMap((bookUrl) =>
settings.bibleVersions.map(
(version) =>
new VerseLink(
version,
bookUrl,
bookName,
chapterNumber,
verseNumber,
verseEndNumber
)
)
return booksUrl.flatMap(
(bookUrl) =>
settings.bibleVersions
.map((version) => {
if (isLink) {
return new VerseLink(
version,
bookUrl,
bookName,
chapterNumber,
verseNumber,
verseEndNumber
);
} else if (verseNumber !== undefined) {
return new VerseEmbed(
version,
bookUrl,
bookName,
chapterNumber,
verseNumber,
verseEndNumber
);
}
})
.filter((v) => v !== undefined) as Verse[]
);
}
10 changes: 7 additions & 3 deletions src/GenerateLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ export default function GenerateLinks(

const match = [...line.matchAll(linkRegex)];
match.forEach((match) => {
const suggestions = getSuggestionsFromQuery(match[0], settings);
suggestions.forEach((s) => {
const suggestions = getSuggestionsFromQuery(
match[0],
true,
settings
);
suggestions.forEach(async (s) => {
if (match.index === undefined) return;
editor.replaceRange(
s.toLink(),
await s.toReplace(),
{
line: i,
ch: match.index,
Expand Down
98 changes: 62 additions & 36 deletions src/LinkPreview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,64 +2,90 @@ import { requestUrl } from "obsidian";
import tippy from "tippy.js";
import { htmlCleanupRegex, htmlDataRegex } from "./Regex";

type cacheType = { [key: string]: any };
type CacheElement = {
info: { version: string; title: string };
verses: string;
err: boolean;
};

type CacheType = { [key: string]: CacheElement };

export default class LinkPreviewManager {
static cache: cacheType = {};
static cache: CacheType = {};

static async processLink(link: HTMLAnchorElement) {
if (!this.cache[link.href]) {
const res = await requestUrl(link.href);
let text = await res.text;

const match = text.match(htmlDataRegex);
if (match) {
const json_text = match[0].replace(htmlCleanupRegex, "");

try {
const data = JSON.parse(json_text);

if (data.props.pageProps.type !== "verse") {
throw 1;
}

const info =
data.props.pageProps.referenceTitle.title +
" " +
data.props.pageProps.version.local_abbreviation;
const verses = data.props.pageProps.verses
.map((ele: any) => ele.content)
.join(" ");
this.cache[link.href] = { info, verses };
} catch {
this.cache[link.href] = { err: true };
}
} else {
this.cache[link.href] = { err: true };
}
}
const content = await this.processUrl(link.href);

const popup = document.createElement("div");
popup.addClass("preview-youversion");

if (this.cache[link.href].err) {
if (content.err) {
popup
.createSpan({ cls: "error-youversion" })
.setText("Verse preview is unavailable for this link.");
} else {
popup
.createSpan({ cls: "content-youversion" })
.setText(this.cache[link.href]?.verses);
.setText(content.verses);
popup
.createSpan({ cls: "info-youversion" })
.setText(this.cache[link.href].info);
.setText(content.info.title + " " + content.info.version);
}

tippy(link, { content: popup, allowHTML: true });
}

static async processUrl(url: string): Promise<CacheElement> {
if (!this.cache[url]) {
try {
const res = await requestUrl(url);
let text = await res.text;

const match = text.match(htmlDataRegex);
if (match) {
const json_text = match[0].replace(htmlCleanupRegex, "");

const data = JSON.parse(json_text);

if (data.props.pageProps.type !== "verse") {
throw 1;
}

const info = {
title: data.props.pageProps.referenceTitle.title,
version:
data.props.pageProps.version.local_abbreviation,
};
const verses = data.props.pageProps.verses
.map((ele: any) => ele.content)
.join(" ");

if (verses.length < 1) {
throw 1;
}

this.cache[url] = { err: false, info, verses };
} else {
throw 1;
}
} catch {
this.cache[url] = {
err: true,
info: { title: "", version: "" },
verses: "",
};
}
}
return this.cache[url];
}

static clearCache(notClear: Array<string>) {
let dict: cacheType = {};
console.info(
`Clearing cache... (${Math.abs(
Object.keys(this.cache).length - notClear.length
)} items)`
);
let dict: CacheType = {};
notClear.forEach((ele) => {
dict[ele] = this.cache[ele];
});
Expand Down
23 changes: 19 additions & 4 deletions src/SettingTab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,27 @@ export default class SettingTab extends PluginSettingTab {
this.bibleVersionSettings();

new Setting(containerEl)
.setName("Trigger")
.setDesc("Trigger for autocomplete in edit mode. Supports regex.")
.setName("Link trigger")
.setDesc(
"Trigger for autocomplete for linking verse in edit mode. Supports regex."
)
.addText((text) => {
text.setValue(this.plugin.settings.linkTrigger);
text.onChange(async (value) => {
this.plugin.settings.linkTrigger = value;
await this.plugin.saveSettings();
});
});

new Setting(containerEl)
.setName("Quote Trigger")
.setDesc(
"Trigger for autocomplete for quoting verse in edit mode. Supports regex."
)
.addText((text) => {
text.setValue(this.plugin.settings.trigger);
text.setValue(this.plugin.settings.embedTrigger);
text.onChange(async (value) => {
this.plugin.settings.trigger = value;
this.plugin.settings.embedTrigger = value;
await this.plugin.saveSettings();
});
});
Expand Down
6 changes: 4 additions & 2 deletions src/SettingsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export interface ObsidianYouversionLinkerSettings {
bibleVersions: BibleVersion[];
linkPreviewRead: boolean;
linkPreviewLive: boolean;
trigger: string;
linkTrigger: string;
embedTrigger: string;
selectedBooksLanguages: string[];
}

Expand All @@ -20,6 +21,7 @@ export const DEFAULT_SETTINGS: ObsidianYouversionLinkerSettings = {
],
linkPreviewRead: true,
linkPreviewLive: true,
trigger: "@",
linkTrigger: "@",
embedTrigger: ">",
selectedBooksLanguages: ["English"],
};
Loading

0 comments on commit 6551d82

Please sign in to comment.