diff --git a/README.md b/README.md index abff26a..ddb36b2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ **List of supported languages can be found [here](./Languages.md).** -You need to just type for example: `@ John 1:1-6` to link verse to quote verse use 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`. Now it also supports multiple verses like: `John 3:16,18-20`. `@` 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. diff --git a/data/books/pt-br.json b/data/books/pt-br.json index 336e99a..8b70ee8 100644 --- a/data/books/pt-br.json +++ b/data/books/pt-br.json @@ -1,30 +1,37 @@ { "GEN": [ "gênesis", + "genesis", "gn" ], "EXO": [ "êxodo", + "exodo", "ex" ], "LEV": [ "levítico", + "levitico", "lv" ], "NUM": [ "números", + "numeros", "nm" ], "DEU": [ "deuteronômio", + "deuteronomio", "dt" ], "JOS": [ "josué", + "josue", "js" ], "JDG": [ "juízes", + "juizes", "jz" ], "RUT": [ @@ -49,10 +56,12 @@ ], "1CH": [ "1crônicas", + "1cronicas", "1cr" ], "2CH": [ "2crônicas", + "2cronicas", "2cr" ], "EZR": [ @@ -76,6 +85,7 @@ ], "PRO": [ "provérbios", + "proverbios", "pv" ], "ECC": [ @@ -84,10 +94,14 @@ ], "SNG": [ "cantaresdesalomão", + "cantares", + "cânticodoscânticos", + "canticodoscanticos", "ct" ], "ISA": [ "isaías", + "isaias", "is" ], "JER": [ @@ -96,6 +110,8 @@ ], "LAM": [ "lamentações", + "lamentaçoes", + "lamentacoes", "lm" ], "EZK": [ @@ -108,6 +124,7 @@ ], "HOS": [ "oséias", + "oseias", "os" ], "JOL": [ @@ -116,6 +133,7 @@ ], "AMO": [ "amós", + "amos", "am" ], "OBA": [ @@ -128,6 +146,7 @@ ], "MIC": [ "miquéias", + "miqueias", "mq" ], "NAM": [ @@ -168,6 +187,7 @@ ], "JHN": [ "joão", + "joao", "jo" ], "ACT": [ @@ -180,18 +200,22 @@ ], "1CO": [ "1coríntios", + "1corintios", "1co" ], "2CO": [ "2coríntios", + "2corintios", "2co" ], "GAL": [ "gálatas", + "galatas", "gl" ], "EPH": [ "efésios", + "efesios", "ef" ], "PHP": [ @@ -212,10 +236,12 @@ ], "1TI": [ "1timóteo", + "1timoteo", "1tm" ], "2TI": [ "2timóteo", + "2timoteo", "2tm" ], "TIT": [ @@ -244,14 +270,17 @@ ], "1JN": [ "1joão", + "1joao", "1jo" ], "2JN": [ "2joão", + "2joao", "2jo" ], "3JN": [ "3joão", + "3joao", "3jo" ], "JUD": [ diff --git a/src/EditorSuggester.ts b/src/EditorSuggester.ts index 2cecc22..cc13355 100644 --- a/src/EditorSuggester.ts +++ b/src/EditorSuggester.ts @@ -1,7 +1,12 @@ import getBooks from "./Books"; -import { bookRegex, linkRegex, separatorRegex } from "./Regex"; +import { + bookRegex, + linkRegex, + chapterSeparatorRegex, + rangeSeparatorRegex, +} from "./Regex"; import { ObsidianYouversionLinkerSettings } from "./SettingsData"; -import Verse from "./Verse"; +import Verse, { VerseElement } from "./Verse"; import VerseEmbed from "./VerseEmbed"; import VerseLink from "./VerseLink"; import ObsidianYouversionLinker from "./main"; @@ -28,7 +33,6 @@ export class EditorSuggester extends EditorSuggest { file: TFile | null ): EditorSuggestTriggerInfo | null { const currentLine = editor.getLine(cursor.line); - const link_pos = currentLine.search( new RegExp(this.settings.linkTrigger, "u") ); @@ -97,6 +101,19 @@ export class EditorSuggester extends EditorSuggest { } } +export function processVerses(verses_str: Array): Array { + return verses_str + .map((verse) => { + const [start, end] = verse + .split(rangeSeparatorRegex) + .map((v) => (v === undefined ? undefined : parseInt(v))); + return start === undefined + ? undefined + : new VerseElement(start, end); + }) + .filter((v) => v !== undefined) as Array; +} + export function getSuggestionsFromQuery( query: string, isLink: boolean, @@ -117,12 +134,11 @@ export function getSuggestionsFromQuery( } const numbersPartsOfQueryString = query.substring(bookName.length); - const numbers = numbersPartsOfQueryString.split(separatorRegex); - - const chapterNumber = parseInt(numbers[0]); - const verseNumber = numbers.length > 1 ? parseInt(numbers[1]) : undefined; - const verseEndNumber = - numbers.length === 3 ? parseInt(numbers[2]) : undefined; + const [chapter_str, ...verses_str] = numbersPartsOfQueryString.split( + chapterSeparatorRegex + ); + const verses = processVerses(verses_str); + const chapterNumber = parseInt(chapter_str); return booksUrl.flatMap( (bookUrl) => @@ -134,17 +150,15 @@ export function getSuggestionsFromQuery( bookUrl, bookName, chapterNumber, - verseNumber, - verseEndNumber + verses ); - } else if (verseNumber !== undefined) { + } else if (verses.length !== 0) { return new VerseEmbed( version, bookUrl, bookName, chapterNumber, - verseNumber, - verseEndNumber + verses ); } }) diff --git a/src/GenerateLinks.ts b/src/GenerateLinks.ts index 5495d52..530c56a 100644 --- a/src/GenerateLinks.ts +++ b/src/GenerateLinks.ts @@ -2,22 +2,32 @@ import { Editor, MarkdownView } from "obsidian"; import { linkRegex } from "./Regex"; import { getSuggestionsFromQuery } from "./EditorSuggester"; import { ObsidianYouversionLinkerSettings } from "./SettingsData"; +import Verse from "./Verse"; export default function GenerateLinks( editor: Editor, view: MarkdownView, settings: ObsidianYouversionLinkerSettings ) { + const removeDuplicatedSuggestionsHandler = (suggestions: Verse[]) => { + // remove duplicated suggestions + suggestions.map((suggestion) => { + const index = suggestions.indexOf(suggestion); + if (index >= 0) { + suggestions = suggestions.slice(index, 1); + } + }); + return suggestions; + }; + const lines = editor.lineCount(); for (let i = 0; i < lines; i++) { const line = editor.getLine(i); const match = [...line.matchAll(linkRegex)]; match.forEach((match) => { - const suggestions = getSuggestionsFromQuery( - match[0], - true, - settings + const suggestions = removeDuplicatedSuggestionsHandler( + getSuggestionsFromQuery(match[0], true, settings) ); suggestions.forEach(async (s) => { if (match.index === undefined) return; diff --git a/src/Regex.ts b/src/Regex.ts index 7c88729..628ccb1 100644 --- a/src/Regex.ts +++ b/src/Regex.ts @@ -1,9 +1,10 @@ -export const linkRegex = - /([12345]\s?)?\p{L}+\s?\d{1,3}([:,.]\s?\d{1,3}([-–]\d{1,3})?)?/gu; +export const linkRegex = + /([12345]\s?)?\p{L}+\s?\d{1,3}([:,.]\s?\d{1,3}([-–—]\d{1,3})?)?([,.]\s?\d{1,3}([-–—]\d{1,3})?)*/gu; export const bookRegex = /([12345]\s?)?\p{L}+/u; -export const separatorRegex = /[-:,.–]+/; +export const chapterSeparatorRegex = /[:,.]+/; +export const rangeSeparatorRegex = /[-–—]+/; export const htmlDataRegex = /.+?(?=<\/script>\s*<\/body>\s*<\/html>)/; diff --git a/src/Verse.ts b/src/Verse.ts index 9b3094e..784a8b6 100644 --- a/src/Verse.ts +++ b/src/Verse.ts @@ -1,14 +1,27 @@ import { BibleVersion } from "./SettingsData"; import VERSIONS from "../data/versions.json"; +export class VerseElement { + public start: number; + public end: number | undefined; + + public constructor(start: number, end: number | undefined) { + this.start = start; + this.end = end; + } + + public toString() { + return `${this.start}${this.end ? `-${this.end}` : ""}`; + } +} + export default abstract class Verse { constructor( private version: BibleVersion, private bookUrl: string, private book: string, private chapter: number, - private verse: number | undefined, - private verseEnd: number | undefined + private verses: Array ) {} public render(el: HTMLElement) { @@ -24,10 +37,10 @@ export default abstract class Verse { } toSimpleText() { - return this.verse - ? this.verseEnd - ? `${this.book} ${this.chapter}:${this.verse}-${this.verseEnd}` - : `${this.book} ${this.chapter}:${this.verse}` + return this.verses.length > 0 + ? `${this.book} ${this.chapter}:${this.verses + .map((verse) => verse.toString()) + .join(", ")}` : `${this.book} ${this.chapter}`; } @@ -36,9 +49,8 @@ export default abstract class Verse { getUrl(): string { const base = "https://www.bible.com/bible"; let url = `${base}/${this.version.id}/${this.bookUrl}.${this.chapter}`; - if (this.verse) { - url += `.${this.verse}`; - if (this.verseEnd) url += `-${this.verseEnd}`; + if (this.verses.length > 0) { + url += `.${this.verses.map((verse) => verse.toString()).join(",")}`; } return url; } diff --git a/test/regex.test.ts b/test/regex.test.ts index 4bafdd1..c0a3aff 100644 --- a/test/regex.test.ts +++ b/test/regex.test.ts @@ -1,4 +1,4 @@ -import { bookRegex, linkRegex, separatorRegex } from "../src/Regex"; +import { bookRegex, linkRegex } from "../src/Regex"; describe("Regex Tests", () => { // Tests for linkRegex @@ -23,7 +23,7 @@ describe("Regex Tests", () => { expect("matt 9").toMatch(linkRegex); }); - test('matches "jes 9, 10"', () => { + test('matches "jes 9, 10.1"', () => { expect("jes 9,10").toMatch(linkRegex); }); @@ -34,6 +34,14 @@ describe("Regex Tests", () => { test('matches "3 こんにちは 123"', () => { expect("3 こんにちは 123").toMatch(linkRegex); }); + + test('matches "3 こんにちは 123.5"', () => { + expect("3 こんにちは 123").toMatch(linkRegex); + }); + + test('matches "jes 9:10-11.1-3,45"', () => { + expect("jes 9:10-11").toMatch(linkRegex); + }); }); // Tests for bookRegex @@ -70,39 +78,4 @@ describe("Regex Tests", () => { expect("123").not.toMatch(bookRegex); }); }); - - // Tests for separatorRegex - describe("separatorRegex", () => { - test('matches "-"', () => { - expect("-").toMatch(separatorRegex); - }); - - test('matches ":"', () => { - expect(":").toMatch(separatorRegex); - }); - - test('matches ","', () => { - expect(",").toMatch(separatorRegex); - }); - - test('matches "."', () => { - expect(".").toMatch(separatorRegex); - }); - - test('matches "---"', () => { - expect("---").toMatch(separatorRegex); - }); - - test('matches "..."', () => { - expect("...").toMatch(separatorRegex); - }); - - test('does not match "abc"', () => { - expect("abc").not.toMatch(separatorRegex); - }); - - test('does not match "1"', () => { - expect("1").not.toMatch(separatorRegex); - }); - }); });