Render embedded markdown footnotes

This commit is contained in:
2026-07-05 22:21:37 -05:00
parent 0139240073
commit b5e8175618
5 changed files with 164 additions and 27 deletions
+20 -1
View File
@@ -113,6 +113,20 @@ proc fetchPlainPassages(
for passage in fetchPassages(reference, translation, cfg): for passage in fetchPassages(reference, translation, cfg):
result.add(formatPlain(passage, translation, keepVerseNumbers)) 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: when isMainModule:
const USAGE = """Usage: const USAGE = """Usage:
bibleref <reference> [options] bibleref <reference> [options]
@@ -189,7 +203,7 @@ Options:
query.translation, query.translation,
keepVerseNumbers = true, keepVerseNumbers = true,
cfg)) cfg))
else: of "reading", "text", "raw":
for passage in fetchPassages(query.referenceText, query.translation, cfg): for passage in fetchPassages(query.referenceText, query.translation, cfg):
formattedPassages.add( formattedPassages.add(
case $args["--output-format"]: case $args["--output-format"]:
@@ -201,6 +215,11 @@ Options:
passage passage
else: else:
formatMarkdown(passage, query.translation)) formatMarkdown(passage, query.translation))
else:
formattedPassages.add(fetchMarkdownPassages(
query.referenceText,
query.translation,
cfg))
echo formattedPassages.join("\p\p") echo formattedPassages.join("\p\p")
+100 -26
View File
@@ -23,6 +23,10 @@ type
offset: int offset: int
targetText: string targetText: string
EmbeddedOutputFormat = enum
eofPlain
eofMarkdown
BibleIndex = object BibleIndex = object
segments: seq[BibleSegment] segments: seq[BibleSegment]
verses: Table[string, bool] verses: Table[string, bool]
@@ -292,7 +296,8 @@ proc addSegmentWithFootnotes(
line: var string, line: var string,
segment: BibleSegment, segment: BibleSegment,
keepVerseNumbers: bool, keepVerseNumbers: bool,
notes: var seq[string]) = notes: var seq[string],
outputFormat: EmbeddedOutputFormat) =
var text = segment.text var text = segment.text
var annotations = segment.annotations var annotations = segment.annotations
@@ -304,13 +309,20 @@ proc addSegmentWithFootnotes(
notes.add(annotation.targetText) notes.add(annotation.targetText)
let insertAt = max(0, min(text.len, annotation.offset + inserted)) 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) text.insert(marker, insertAt)
inserted += marker.len inserted += marker.len
let marker = let marker =
if keepVerseNumbers: $segment.verse & " " if keepVerseNumbers:
else: "" case outputFormat
of eofPlain: $segment.verse & " "
of eofMarkdown: "**" & $segment.verse & "** "
else:
""
let content = marker & text let content = marker & text
if line.len == 0: if line.len == 0:
@@ -320,13 +332,20 @@ proc addSegmentWithFootnotes(
else: else:
line.add("\n" & repeat(" ", segment.indent) & content) 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: if notes.len == 0:
return return
lines.add("") lines.add("")
for idx, note in notes: 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): proc selectedChapters(index: BibleIndex, reference: PassageReference):
seq[tuple[chapter: int, verses: Table[string, bool]]] = seq[tuple[chapter: int, verses: Table[string, bool]]] =
@@ -353,11 +372,12 @@ proc selectedChapters(index: BibleIndex, reference: PassageReference):
result.add((chapter, verses)) result.add((chapter, verses))
result[chapterPositions[chapter]].verses[key] = true result[chapterPositions[chapter]].verses[key] = true
proc plainChapter( proc formattedChapter(
index: BibleIndex, index: BibleIndex,
code: string, code: string,
selected: Table[string, bool], selected: Table[string, bool],
keepVerseNumbers: bool): string = keepVerseNumbers: bool,
outputFormat: EmbeddedOutputFormat): string =
var lines: seq[string] var lines: seq[string]
var currentLine = "" var currentLine = ""
@@ -404,17 +424,21 @@ proc plainChapter(
renderSegment.verse = 0 renderSegment.verse = 0
if marker: if marker:
currentLine.addSegmentWithFootnotes(renderSegment, keepVerseNumbers, notes) currentLine.addSegmentWithFootnotes(
renderSegment,
keepVerseNumbers,
notes,
outputFormat)
else: else:
var noVerseSegment = renderSegment var noVerseSegment = renderSegment
noVerseSegment.text = renderSegment.text noVerseSegment.text = renderSegment.text
noVerseSegment.breakKind = sbSame noVerseSegment.breakKind = sbSame
currentLine.addSegmentWithFootnotes(noVerseSegment, false, notes) currentLine.addSegmentWithFootnotes(noVerseSegment, false, notes, outputFormat)
firstSelected = false firstSelected = false
flushLine() flushLine()
lines.addFootnotes(notes) lines.addFootnotes(notes, outputFormat)
var wrapped: seq[string] var wrapped: seq[string]
for line in lines: for line in lines:
@@ -428,25 +452,75 @@ proc plainChapter(
wrapped.add(wrapWords(prepared, maxLineWidth = 74, newLine = "\p")) wrapped.add(wrapWords(prepared, maxLineWidth = 74, newLine = "\p"))
wrapped.join("\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*( proc fetchPlainPassages*(
rows, rows,
reference, reference,
translationName: string, translationName: string,
keepVerseNumbers = true): seq[string] = keepVerseNumbers = true): seq[string] =
let index = loadBibleIndex(rows, translationName) fetchFormattedPassages(
for parsedReference in parseReferences(reference): rows,
var parts: seq[string] reference,
for chapter in index.selectedChapters(parsedReference): translationName,
parts.add(index.plainChapter( keepVerseNumbers,
parsedReference.book.code, eofPlain)
chapter.verses,
keepVerseNumbers))
let body = parts.join("\p\p") proc fetchMarkdownPassages*(rows, reference, translationName: string): seq[string] =
let referenceSeparator = fetchFormattedPassages(
if body.contains("\pa. "): "\p\p" rows,
else: "\p" reference,
translationName,
result.add(body & referenceSeparator & " " & $parsedReference & " (" & keepVerseNumbers = true,
translationName.toUpperAscii & ")") eofMarkdown)
+3
View File
@@ -8,3 +8,6 @@ proc fetchPassages*(reference: string): seq[string] =
proc fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string] = proc fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string] =
embedded_bible.fetchPlainPassages(kjvRows, reference, "KJV", keepVerseNumbers) embedded_bible.fetchPlainPassages(kjvRows, reference, "KJV", keepVerseNumbers)
proc fetchMarkdownPassages*(reference: string): seq[string] =
embedded_bible.fetchMarkdownPassages(kjvRows, reference, "KJV")
+7
View File
@@ -18,3 +18,10 @@ proc fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string
else: else:
raise newException(ValueError, raise newException(ValueError,
"MEV data is not embedded; generate data/private/mev.tsv and rebuild") "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")
+34
View File
@@ -204,3 +204,37 @@ HEB 4 7 2 line 1 do not harden your hearts."
"a. Jn 8:12\n" & "a. Jn 8:12\n" &
"\n" & "\n" &
" John 8:59-9:1 (TEST)" " 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)*"