diff --git a/src/bibleref.nim b/src/bibleref.nim index 86eda08..3e337fb 100644 --- a/src/bibleref.nim +++ b/src/bibleref.nim @@ -113,6 +113,20 @@ proc fetchPlainPassages( for passage in fetchPassages(reference, translation, cfg): result.add(formatPlain(passage, translation, keepVerseNumbers)) +proc fetchMarkdownPassages( + reference, + translation: string, + cfg: CombinedConfig): seq[string] = + + case translation + of "akjv", "kjv": + result = kjv.fetchMarkdownPassages(reference) + of "mev": + result = mev.fetchMarkdownPassages(reference) + else: + for passage in fetchPassages(reference, translation, cfg): + result.add(formatMarkdown(passage, translation)) + when isMainModule: const USAGE = """Usage: bibleref [options] @@ -189,7 +203,7 @@ Options: query.translation, keepVerseNumbers = true, cfg)) - else: + of "reading", "text", "raw": for passage in fetchPassages(query.referenceText, query.translation, cfg): formattedPassages.add( case $args["--output-format"]: @@ -201,6 +215,11 @@ Options: passage else: formatMarkdown(passage, query.translation)) + else: + formattedPassages.add(fetchMarkdownPassages( + query.referenceText, + query.translation, + cfg)) echo formattedPassages.join("\p\p") diff --git a/src/embedded_bible.nim b/src/embedded_bible.nim index 3945eeb..ec40e08 100644 --- a/src/embedded_bible.nim +++ b/src/embedded_bible.nim @@ -23,6 +23,10 @@ type offset: int targetText: string + EmbeddedOutputFormat = enum + eofPlain + eofMarkdown + BibleIndex = object segments: seq[BibleSegment] verses: Table[string, bool] @@ -292,7 +296,8 @@ proc addSegmentWithFootnotes( line: var string, segment: BibleSegment, keepVerseNumbers: bool, - notes: var seq[string]) = + notes: var seq[string], + outputFormat: EmbeddedOutputFormat) = var text = segment.text var annotations = segment.annotations @@ -304,13 +309,20 @@ proc addSegmentWithFootnotes( notes.add(annotation.targetText) let insertAt = max(0, min(text.len, annotation.offset + inserted)) - let marker = "[" & label & "]" + let marker = + case outputFormat + of eofPlain: "[" & label & "]" + of eofMarkdown: "[^" & label & "]" text.insert(marker, insertAt) inserted += marker.len let marker = - if keepVerseNumbers: $segment.verse & " " - else: "" + if keepVerseNumbers: + case outputFormat + of eofPlain: $segment.verse & " " + of eofMarkdown: "**" & $segment.verse & "** " + else: + "" let content = marker & text if line.len == 0: @@ -320,13 +332,20 @@ proc addSegmentWithFootnotes( else: line.add("\n" & repeat(" ", segment.indent) & content) -proc addFootnotes(lines: var seq[string], notes: seq[string]) = +proc addFootnotes( + lines: var seq[string], + notes: seq[string], + outputFormat: EmbeddedOutputFormat) = if notes.len == 0: return lines.add("") for idx, note in notes: - lines.add(footnoteLabel(idx) & ". " & note) + let label = footnoteLabel(idx) + lines.add( + case outputFormat + of eofPlain: label & ". " & note + of eofMarkdown: "[^" & label & "]: " & note) proc selectedChapters(index: BibleIndex, reference: PassageReference): seq[tuple[chapter: int, verses: Table[string, bool]]] = @@ -353,11 +372,12 @@ proc selectedChapters(index: BibleIndex, reference: PassageReference): result.add((chapter, verses)) result[chapterPositions[chapter]].verses[key] = true -proc plainChapter( +proc formattedChapter( index: BibleIndex, code: string, selected: Table[string, bool], - keepVerseNumbers: bool): string = + keepVerseNumbers: bool, + outputFormat: EmbeddedOutputFormat): string = var lines: seq[string] var currentLine = "" @@ -404,17 +424,21 @@ proc plainChapter( renderSegment.verse = 0 if marker: - currentLine.addSegmentWithFootnotes(renderSegment, keepVerseNumbers, notes) + currentLine.addSegmentWithFootnotes( + renderSegment, + keepVerseNumbers, + notes, + outputFormat) else: var noVerseSegment = renderSegment noVerseSegment.text = renderSegment.text noVerseSegment.breakKind = sbSame - currentLine.addSegmentWithFootnotes(noVerseSegment, false, notes) + currentLine.addSegmentWithFootnotes(noVerseSegment, false, notes, outputFormat) firstSelected = false flushLine() - lines.addFootnotes(notes) + lines.addFootnotes(notes, outputFormat) var wrapped: seq[string] for line in lines: @@ -428,25 +452,75 @@ proc plainChapter( wrapped.add(wrapWords(prepared, maxLineWidth = 74, newLine = "\p")) wrapped.join("\p") +proc fetchFormattedPassages( + rows, + reference, + translationName: string, + keepVerseNumbers: bool, + outputFormat: EmbeddedOutputFormat): seq[string] = + + let index = loadBibleIndex(rows, translationName) + for parsedReference in parseReferences(reference): + var parts: seq[string] + for chapter in index.selectedChapters(parsedReference): + parts.add(index.formattedChapter( + parsedReference.book.code, + chapter.verses, + keepVerseNumbers, + outputFormat)) + + let body = parts.join("\p\p") + let referenceSeparator = + case outputFormat + of eofPlain: + if body.contains("\pa. "): "\p\p" + else: "\p" + of eofMarkdown: + if body.contains("\p[^a]: "): "\p\p" + else: "\p" + + let referenceLine = + case outputFormat + of eofPlain: + "– " & $parsedReference & " (" & translationName.toUpperAscii & ")" + of eofMarkdown: + "> -- *" & $parsedReference & " (" & + translationName.toUpperAscii & ")*" + + let formattedBody = + case outputFormat + of eofPlain: + body + of eofMarkdown: + var markdownLines: seq[string] + for line in body.splitLines: + if line.len == 0: + markdownLines.add("") + elif line.startsWith("[^"): + markdownLines.add(line) + else: + markdownLines.add("> " & line) + markdownLines.join("\p") + + result.add(formattedBody & referenceSeparator & referenceLine) + proc fetchPlainPassages*( rows, reference, translationName: string, keepVerseNumbers = true): seq[string] = - let index = loadBibleIndex(rows, translationName) - for parsedReference in parseReferences(reference): - var parts: seq[string] - for chapter in index.selectedChapters(parsedReference): - parts.add(index.plainChapter( - parsedReference.book.code, - chapter.verses, - keepVerseNumbers)) + fetchFormattedPassages( + rows, + reference, + translationName, + keepVerseNumbers, + eofPlain) - let body = parts.join("\p\p") - let referenceSeparator = - if body.contains("\pa. "): "\p\p" - else: "\p" - - result.add(body & referenceSeparator & "– " & $parsedReference & " (" & - translationName.toUpperAscii & ")") +proc fetchMarkdownPassages*(rows, reference, translationName: string): seq[string] = + fetchFormattedPassages( + rows, + reference, + translationName, + keepVerseNumbers = true, + eofMarkdown) diff --git a/src/kjv.nim b/src/kjv.nim index 313db8e..6135806 100644 --- a/src/kjv.nim +++ b/src/kjv.nim @@ -8,3 +8,6 @@ proc fetchPassages*(reference: string): seq[string] = proc fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string] = embedded_bible.fetchPlainPassages(kjvRows, reference, "KJV", keepVerseNumbers) + +proc fetchMarkdownPassages*(reference: string): seq[string] = + embedded_bible.fetchMarkdownPassages(kjvRows, reference, "KJV") diff --git a/src/mev.nim b/src/mev.nim index 75d41f0..ecfd161 100644 --- a/src/mev.nim +++ b/src/mev.nim @@ -18,3 +18,10 @@ proc fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string else: raise newException(ValueError, "MEV data is not embedded; generate data/private/mev.tsv and rebuild") + +proc fetchMarkdownPassages*(reference: string): seq[string] = + when hasEmbeddedTranslationData("mev"): + embedded_bible.fetchMarkdownPassages(mevRows, reference, "MEV") + else: + raise newException(ValueError, + "MEV data is not embedded; generate data/private/mev.tsv and rebuild") diff --git a/tests/test_offline_kjv.nim b/tests/test_offline_kjv.nim index 1028bb7..b1372f5 100644 --- a/tests/test_offline_kjv.nim +++ b/tests/test_offline_kjv.nim @@ -204,3 +204,37 @@ HEB 4 7 2 line 1 do not harden your hearts." "a. Jn 8:12\n" & "\n" & "– John 8:59-9:1 (TEST)" + + test "markdown output renders native footnotes": + const rows = + "JHN\t8\t1\t0\tparagraph\t0\tBut Jesus went to the Mount of Olives.\t" & + """{"annotations":[{"kind":"crossReference","marker":"a","offset":40,"targetText":"Mt 21:1"}]}""" & "\n" & + "JHN\t8\t2\t0\tsame\t0\tEarly in the morning He returned to the temple.\t" & + """{"annotations":[{"kind":"crossReference","marker":"a","offset":49,"targetText":"Mt 26:55; Lk 4:20"}]}""" & "\n" + let passages = embedded_bible.fetchMarkdownPassages(rows, "John 8:1-2", "TEST") + + check passages[0] == "> **1** But Jesus went to the Mount of Olives.[^a] **2** Early in the\n" & + "> morning He returned to the temple.[^b]\n" & + "\n" & + "[^a]: Mt 21:1\n" & + "[^b]: Mt 26:55; Lk 4:20\n" & + "\n" & + "> -- *John 8:1-2 (TEST)*" + + test "markdown output resets native footnotes per chapter": + const rows = + "JHN\t8\t59\t0\tparagraph\t0\tThey took up stones.\t" & + """{"annotations":[{"kind":"crossReference","marker":"a","offset":21,"targetText":"Lev 24:16"}]}""" & "\n" & + "JHN\t9\t1\t0\tparagraph\t0\tAs Jesus passed by.\t" & + """{"annotations":[{"kind":"crossReference","marker":"a","offset":20,"targetText":"Jn 8:12"}]}""" & "\n" + let passages = embedded_bible.fetchMarkdownPassages(rows, "John 8:59-9:1", "TEST") + + check passages[0] == "> **59** They took up stones.[^a]\n" & + "\n" & + "[^a]: Lev 24:16\n" & + "\n" & + "> **1** As Jesus passed by.[^a]\n" & + "\n" & + "[^a]: Jn 8:12\n" & + "\n" & + "> -- *John 8:59-9:1 (TEST)*"