From 6972a4e531b43961958e5fe8ef6486c670d518a1 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Sun, 5 Jul 2026 20:55:43 -0500 Subject: [PATCH] Render embedded Bible paragraphs inline --- src/embedded_bible.nim | 68 ++++++++++++++++++++++++++++++-------- tests/test_offline_kjv.nim | 23 +++++++++++++ 2 files changed, 78 insertions(+), 13 deletions(-) diff --git a/src/embedded_bible.nim b/src/embedded_bible.nim index 6f8c2cc..3d89265 100644 --- a/src/embedded_bible.nim +++ b/src/embedded_bible.nim @@ -30,6 +30,17 @@ proc verseKey(code: string, chapter, verse: int): string = proc chapterKey(code: string, chapter: int): string = code & "\t" & $chapter +proc legacySegment(text: string): tuple[breakKind: SegmentBreak, text: string] = + const pilcrow = "\xC2\xB6" + let stripped = text.strip(leading = true, trailing = false) + + if stripped.startsWith(pilcrow): + result.breakKind = sbParagraph + result.text = stripped[pilcrow.len .. ^1].strip(leading = true, trailing = false) + else: + result.breakKind = sbSame + result.text = text + proc parseSegmentBreak(s: string): SegmentBreak = case s of "same": sbSame @@ -64,14 +75,15 @@ proc loadBibleIndex(rows, translationName: string): BibleIndex = "invalid embedded " & translationName & " row: " & line) if parts.len == 4: + let legacy = legacySegment(parts[3]) result.addSegment(BibleSegment( code: parts[0], chapter: parseInt(parts[1]), verse: parseInt(parts[2]), segment: 0, - breakKind: sbLine, + breakKind: legacy.breakKind, indent: 0, - text: parts[3])) + text: legacy.text)) else: result.addSegment(BibleSegment( code: parts[0], @@ -151,9 +163,41 @@ proc addFormattedSegment( lines[^1].add(" ") lines[^1].add(content) else: - if segment.breakKind == sbParagraph and hasPassageLine: - lines.add("") - lines.add(prefix & content) + let isMarkerlessParagraphContinuation = + segment.breakKind == sbParagraph and marker.len == 0 and hasPassageLine + + if isMarkerlessParagraphContinuation: + if lines[^1].len > 0 and not lines[^1][^1].isSpaceAscii: + lines[^1].add(" ") + lines[^1].add(content) + else: + if segment.breakKind == sbParagraph and hasPassageLine: + lines.add("") + lines.add(prefix & content) + +proc normalizeFirstSelectedSegment(segment: BibleSegment): BibleSegment = + result = segment + if result.breakKind == sbSame: + result.breakKind = sbLine + elif result.breakKind == sbParagraph and result.segment == 0: + result.breakKind = sbLine + +proc addAllSegments(lines: var seq[string], index: BibleIndex, code: string) = + var emittedVerses: Table[string, bool] + var firstSelected = true + + discard index.requireLastChapter(code) + for segment in index.segments: + if segment.code == code: + var adjusted = segment + if firstSelected: + adjusted = normalizeFirstSelectedSegment(adjusted) + lines.addFormattedSegment(adjusted, emittedVerses) + firstSelected = false + +proc addRangeSeparator(lines: var seq[string]) = + if lines.len > 1 and lines[^1].len > 0: + lines.add("") proc addVerseLines( lines: var seq[string], @@ -170,8 +214,8 @@ proc addVerseLines( if segment.code == code and selected.hasKey(verseKey(segment.code, segment.chapter, segment.verse)): var adjusted = segment - if firstSelected and adjusted.breakKind == sbSame: - adjusted.breakKind = sbLine + if firstSelected: + adjusted = normalizeFirstSelectedSegment(adjusted) lines.addFormattedSegment(adjusted, emittedVerses) firstSelected = false @@ -180,13 +224,11 @@ proc fetchReference(index: BibleIndex, reference: PassageReference): string = let code = reference.book.code if reference.ranges.len == 0: - var emittedVerses: Table[string, bool] - discard index.requireLastChapter(code) - for segment in index.segments: - if segment.code == code: - lines.addFormattedSegment(segment, emittedVerses) + lines.addAllSegments(index, code) else: - for range in reference.ranges: + for idx, range in reference.ranges: + if idx > 0: + lines.addRangeSeparator() lines.addVerseLines(index, reference, range) lines.join("\n") diff --git a/tests/test_offline_kjv.nim b/tests/test_offline_kjv.nim index f859651..b13ed1a 100644 --- a/tests/test_offline_kjv.nim +++ b/tests/test_offline_kjv.nim @@ -90,6 +90,9 @@ JHN 8 31 0 paragraph 0 Then Jesus said, JHN 8 31 1 same 0 "If you remain in My word, JHN 8 32 0 same 0 you shall know the truth." JHN 8 33 0 paragraph 0 They answered Him. +JHN 8 39 0 paragraph 0 They answered Him, "Abraham is our father." +JHN 8 39 1 paragraph 0 Jesus said to them, "If you were Abraham's children, +JHN 8 40 0 same 0 you would do the works of Abraham." HEB 4 7 0 paragraph 0 again He establishes a certain day: HEB 4 7 1 line 0 "Today, if you will hear His voice, HEB 4 7 2 line 1 do not harden your hearts." @@ -126,8 +129,28 @@ HEB 4 7 2 line 1 do not harden your hearts." "\n" & " [33] They answered Him." + test "joins markerless paragraph continuations": + let passages = embedded_bible.fetchPassages(layoutRows, "John 8:39-40", "TEST") + + check passages[0] == "John 8:39-40\n" & + " [39] They answered Him, \"Abraham is our father.\" " & + "Jesus said to them, \"If you were Abraham's children, " & + "[40] you would do the works of Abraham.\"" + test "supports legacy verse rows": const legacyRows = "JHN\t3\t16\tFor God so loved the world.\n" let passages = embedded_bible.fetchPassages(legacyRows, "John 3:16", "TEST") check passages[0] == "John 3:16\n [16] For God so loved the world." + + test "groups legacy rows into paragraphs using pilcrows": + const legacyRows = + "JHN\t8\t31\tThen Jesus said.\n" & + "JHN\t8\t32\tYou shall know the truth.\n" & + "JHN\t8\t33\t\xC2\xB6 They answered Him.\n" + let passages = embedded_bible.fetchPassages(legacyRows, "John 8:31-33", "TEST") + + check passages[0] == "John 8:31-33\n" & + " [31] Then Jesus said. [32] You shall know the truth.\n" & + "\n" & + " [33] They answered Him."