Add ANSI output format

This commit is contained in:
2026-07-06 08:54:57 -05:00
parent b5e8175618
commit 14f5be9ad1
6 changed files with 378 additions and 39 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# Package # Package
version = "1.1.1" version = "1.2.0"
author = "Jonathan Bernard" author = "Jonathan Bernard"
description = "Simple Nim CLI for retrieving Biblical passages" description = "Simple Nim CLI for retrieving Biblical passages"
license = "MIT" license = "MIT"
+49 -6
View File
@@ -106,9 +106,15 @@ proc fetchPlainPassages(
case translation case translation
of "akjv", "kjv": of "akjv", "kjv":
result = kjv.fetchPlainPassages(reference, keepVerseNumbers) result = kjv.fetchPlainPassages(
reference,
keepVerseNumbers,
cfg.getVal("annotations", "all"))
of "mev": of "mev":
result = mev.fetchPlainPassages(reference, keepVerseNumbers) result = mev.fetchPlainPassages(
reference,
keepVerseNumbers,
cfg.getVal("annotations", "all"))
else: else:
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))
@@ -120,13 +126,38 @@ proc fetchMarkdownPassages(
case translation case translation
of "akjv", "kjv": of "akjv", "kjv":
result = kjv.fetchMarkdownPassages(reference) result = kjv.fetchMarkdownPassages(
reference,
cfg.getVal("annotations", "all"))
of "mev": of "mev":
result = mev.fetchMarkdownPassages(reference) result = mev.fetchMarkdownPassages(
reference,
cfg.getVal("annotations", "all"))
else: else:
for passage in fetchPassages(reference, translation, cfg): for passage in fetchPassages(reference, translation, cfg):
result.add(formatMarkdown(passage, translation)) result.add(formatMarkdown(passage, translation))
proc fetchAnsiPassages(
reference,
translation: string,
keepVerseNumbers: bool,
cfg: CombinedConfig): seq[string] =
case translation
of "akjv", "kjv":
result = kjv.fetchAnsiPassages(
reference,
keepVerseNumbers,
cfg.getVal("annotations", "all"))
of "mev":
result = mev.fetchAnsiPassages(
reference,
keepVerseNumbers,
cfg.getVal("annotations", "all"))
else:
for passage in fetchPassages(reference, translation, cfg):
result.add(formatPlain(passage, translation, keepVerseNumbers))
when isMainModule: when isMainModule:
const USAGE = """Usage: const USAGE = """Usage:
bibleref <reference> [options] bibleref <reference> [options]
@@ -139,7 +170,13 @@ Options:
command line for debugging purposes. command line for debugging purposes.
-f, --output-format <format> Select a specific output format. Valid values -f, --output-format <format> Select a specific output format. Valid values
are 'raw', 'markdown', 'plain', 'reading'. are 'ansi', 'raw', 'markdown', 'plain',
'reading'.
--annotations <annotations> Select embedded Bible annotations to render.
Valid values are 'none', 'all', or a
comma-separated list of annotation kinds such
as 'note,crossReference'. Defaults to 'all'.
-t, --translation <translation> -t, --translation <translation>
Select a specific translation. Supported values Select a specific translation. Supported values
@@ -167,7 +204,7 @@ Options:
--api-bible-nkjv-bible-id <id> --api-bible-nkjv-bible-id <id>
Override the API.Bible Bible ID for NKJV. Override the API.Bible Bible ID for NKJV.
""" """
const VERSION = "1.1.1" const VERSION = "1.2.0"
let consoleLogger = newConsoleLogger( let consoleLogger = newConsoleLogger(
levelThreshold=lvlInfo, levelThreshold=lvlInfo,
@@ -197,6 +234,12 @@ Options:
var formattedPassages: seq[string] = @[] var formattedPassages: seq[string] = @[]
for query in queries: for query in queries:
case $args["--output-format"] case $args["--output-format"]
of "ansi":
formattedPassages.add(fetchAnsiPassages(
query.referenceText,
query.translation,
keepVerseNumbers = true,
cfg))
of "plain": of "plain":
formattedPassages.add(fetchPlainPassages( formattedPassages.add(fetchPlainPassages(
query.referenceText, query.referenceText,
+187 -24
View File
@@ -17,6 +17,7 @@ type
indent: int indent: int
text: string text: string
annotations: seq[BibleAnnotation] annotations: seq[BibleAnnotation]
wordsOfChrist: bool
BibleAnnotation = object BibleAnnotation = object
kind: string kind: string
@@ -25,8 +26,18 @@ type
EmbeddedOutputFormat = enum EmbeddedOutputFormat = enum
eofPlain eofPlain
eofAnsi
eofMarkdown eofMarkdown
AnnotationFilterKind = enum
afNone
afAll
afKinds
AnnotationFilter = object
kind: AnnotationFilterKind
kinds: seq[string]
BibleIndex = object BibleIndex = object
segments: seq[BibleSegment] segments: seq[BibleSegment]
verses: Table[string, bool] verses: Table[string, bool]
@@ -34,6 +45,12 @@ type
lastChapterByBook: Table[string, int] lastChapterByBook: Table[string, int]
translationName: string translationName: string
const
ansiReset = "\x1b[0m"
ansiDarkRed = "\x1b[31m"
ansiGray = "\x1b[90m"
ansiCyan = "\x1b[36m"
proc verseKey(code: string, chapter, verse: int): string = proc verseKey(code: string, chapter, verse: int): string =
code & "\t" & $chapter & "\t" & $verse code & "\t" & $chapter & "\t" & $verse
@@ -59,12 +76,20 @@ proc parseSegmentBreak(s: string): SegmentBreak =
else: else:
raise newException(ValueError, "invalid embedded Bible segment break: " & s) raise newException(ValueError, "invalid embedded Bible segment break: " & s)
proc parseAnnotations(metadata: string): seq[BibleAnnotation] = proc parseMetadata(metadata: string):
tuple[annotations: seq[BibleAnnotation], wordsOfChrist: bool] =
if metadata.strip.len == 0: if metadata.strip.len == 0:
return return
let node = parseJson(metadata) let node = parseJson(metadata)
if node.kind != JObject or not node.hasKey("annotations"): if node.kind != JObject:
return
result.wordsOfChrist =
node.hasKey("wordsOfChrist") and node["wordsOfChrist"].getBool
if not node.hasKey("annotations"):
return return
for item in node["annotations"].getElems: for item in node["annotations"].getElems:
@@ -75,7 +100,7 @@ proc parseAnnotations(metadata: string): seq[BibleAnnotation] =
if targetText.strip.len == 0: if targetText.strip.len == 0:
continue continue
result.add(BibleAnnotation( result.annotations.add(BibleAnnotation(
kind: kind:
if item.hasKey("kind"): item["kind"].getStr if item.hasKey("kind"): item["kind"].getStr
else: "", else: "",
@@ -120,6 +145,10 @@ proc loadBibleIndex(rows, translationName: string): BibleIndex =
indent: 0, indent: 0,
text: legacy.text)) text: legacy.text))
else: else:
let metadata =
if parts.len == 8: parseMetadata(parts[7])
else: (annotations: newSeq[BibleAnnotation](), wordsOfChrist: false)
result.addSegment(BibleSegment( result.addSegment(BibleSegment(
code: parts[0], code: parts[0],
chapter: parseInt(parts[1]), chapter: parseInt(parts[1]),
@@ -128,9 +157,8 @@ proc loadBibleIndex(rows, translationName: string): BibleIndex =
breakKind: parseSegmentBreak(parts[4]), breakKind: parseSegmentBreak(parts[4]),
indent: parseInt(parts[5]), indent: parseInt(parts[5]),
text: parts[6], text: parts[6],
annotations: annotations: metadata.annotations,
if parts.len == 8: parseAnnotations(parts[7]) wordsOfChrist: metadata.wordsOfChrist))
else: @[]))
proc requireLastChapter(index: BibleIndex, code: string): int = proc requireLastChapter(index: BibleIndex, code: string): int =
if not index.lastChapterByBook.hasKey(code): if not index.lastChapterByBook.hasKey(code):
@@ -284,6 +312,41 @@ proc fetchPassages*(rows, reference, translationName: string): seq[string] =
for parsedReference in parseReferences(reference): for parsedReference in parseReferences(reference):
result.add(fetchReference(index, parsedReference)) result.add(fetchReference(index, parsedReference))
proc normalizeAnnotationKind(kind: string): string =
case kind.strip
of "note":
"note"
of "crossReference", "crossreference", "cross-reference", "cross_reference":
"crossReference"
else:
raise newException(ValueError,
"unsupported annotation kind '" & kind &
"'; supported annotation kinds: note, crossReference")
proc parseAnnotationFilter(value: string): AnnotationFilter =
let normalized = value.strip
case normalized
of "", "all":
return AnnotationFilter(kind: afAll)
of "none":
return AnnotationFilter(kind: afNone)
else:
result.kind = afKinds
for part in normalized.split(','):
let annotationKind = normalizeAnnotationKind(part)
if annotationKind notin result.kinds:
result.kinds.add(annotationKind)
proc includes(filter: AnnotationFilter, annotation: BibleAnnotation): bool =
case filter.kind
of afNone:
false
of afAll:
true
of afKinds:
annotation.kind in filter.kinds
proc footnoteLabel(index: int): string = proc footnoteLabel(index: int): string =
var n = index var n = index
while true: while true:
@@ -292,12 +355,66 @@ proc footnoteLabel(index: int): string =
if n < 0: if n < 0:
break break
proc annotationMarker(
label: string,
outputFormat: EmbeddedOutputFormat,
resumeWordsOfChrist: bool): string =
case outputFormat
of eofPlain:
"[" & label & "]"
of eofAnsi:
ansiCyan & "[" & label & "]" & ansiReset &
(if resumeWordsOfChrist: ansiDarkRed else: "")
of eofMarkdown:
"[^" & label & "]"
proc visibleLen(s: string): int =
var idx = 0
while idx < s.len:
if s[idx] == '\x1b' and idx + 1 < s.len and s[idx + 1] == '[':
idx += 2
while idx < s.len and not s[idx].isAlphaAscii:
inc idx
if idx < s.len:
inc idx
else:
inc result
inc idx
proc wrapAnsiWords(line: string, maxLineWidth = 74): string =
let indentLen = line.len - line.strip(leading = true, trailing = false).len
let indent = repeat(" ", indentLen)
let words = line.strip.splitWhitespace
if words.len == 0:
return ""
var lines: seq[string]
var current = indent
for word in words:
let separator =
if current.strip.len == 0: ""
else: " "
if current.strip.len > 0 and
current.visibleLen + separator.len + word.visibleLen > maxLineWidth:
lines.add(current)
current = word
else:
current.add(separator & word)
if current.len > 0:
lines.add(current)
lines.join("\p")
proc addSegmentWithFootnotes( 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) = outputFormat: EmbeddedOutputFormat,
annotationFilter: AnnotationFilter) =
var text = segment.text var text = segment.text
var annotations = segment.annotations var annotations = segment.annotations
@@ -305,21 +422,28 @@ proc addSegmentWithFootnotes(
var inserted = 0 var inserted = 0
for annotation in annotations: for annotation in annotations:
if not annotationFilter.includes(annotation):
continue
let label = footnoteLabel(notes.len) let label = footnoteLabel(notes.len)
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 = let marker = annotationMarker(
case outputFormat label,
of eofPlain: "[" & label & "]" outputFormat,
of eofMarkdown: "[^" & label & "]" resumeWordsOfChrist = segment.wordsOfChrist)
text.insert(marker, insertAt) text.insert(marker, insertAt)
inserted += marker.len inserted += marker.len
if outputFormat == eofAnsi and segment.wordsOfChrist:
text = ansiDarkRed & text & ansiReset
let marker = let marker =
if keepVerseNumbers: if keepVerseNumbers:
case outputFormat case outputFormat
of eofPlain: $segment.verse & " " of eofPlain: $segment.verse & " "
of eofAnsi: ansiGray & $segment.verse & ansiReset & " "
of eofMarkdown: "**" & $segment.verse & "** " of eofMarkdown: "**" & $segment.verse & "** "
else: else:
"" ""
@@ -345,6 +469,7 @@ proc addFootnotes(
lines.add( lines.add(
case outputFormat case outputFormat
of eofPlain: label & ". " & note of eofPlain: label & ". " & note
of eofAnsi: ansiCyan & label & ". " & note & ansiReset
of eofMarkdown: "[^" & label & "]: " & note) of eofMarkdown: "[^" & label & "]: " & note)
proc selectedChapters(index: BibleIndex, reference: PassageReference): proc selectedChapters(index: BibleIndex, reference: PassageReference):
@@ -377,7 +502,8 @@ proc formattedChapter(
code: string, code: string,
selected: Table[string, bool], selected: Table[string, bool],
keepVerseNumbers: bool, keepVerseNumbers: bool,
outputFormat: EmbeddedOutputFormat): string = outputFormat: EmbeddedOutputFormat,
annotationFilter: AnnotationFilter): string =
var lines: seq[string] var lines: seq[string]
var currentLine = "" var currentLine = ""
@@ -428,12 +554,18 @@ proc formattedChapter(
renderSegment, renderSegment,
keepVerseNumbers, keepVerseNumbers,
notes, notes,
outputFormat) outputFormat,
annotationFilter)
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, outputFormat) currentLine.addSegmentWithFootnotes(
noVerseSegment,
false,
notes,
outputFormat,
annotationFilter)
firstSelected = false firstSelected = false
@@ -449,7 +581,10 @@ proc formattedChapter(
let prepared = let prepared =
if line.len > 90: line.strip if line.len > 90: line.strip
else: line & " " else: line & " "
wrapped.add(wrapWords(prepared, maxLineWidth = 74, newLine = "\p")) if outputFormat == eofAnsi:
wrapped.add(wrapAnsiWords(prepared))
else:
wrapped.add(wrapWords(prepared, maxLineWidth = 74, newLine = "\p"))
wrapped.join("\p") wrapped.join("\p")
proc fetchFormattedPassages( proc fetchFormattedPassages(
@@ -457,9 +592,12 @@ proc fetchFormattedPassages(
reference, reference,
translationName: string, translationName: string,
keepVerseNumbers: bool, keepVerseNumbers: bool,
outputFormat: EmbeddedOutputFormat): seq[string] = outputFormat: EmbeddedOutputFormat,
annotations: string): seq[string] =
let index = loadBibleIndex(rows, translationName) let index = loadBibleIndex(rows, translationName)
let annotationFilter = parseAnnotationFilter(annotations)
for parsedReference in parseReferences(reference): for parsedReference in parseReferences(reference):
var parts: seq[string] var parts: seq[string]
for chapter in index.selectedChapters(parsedReference): for chapter in index.selectedChapters(parsedReference):
@@ -467,13 +605,15 @@ proc fetchFormattedPassages(
parsedReference.book.code, parsedReference.book.code,
chapter.verses, chapter.verses,
keepVerseNumbers, keepVerseNumbers,
outputFormat)) outputFormat,
annotationFilter))
let body = parts.join("\p\p") let body = parts.join("\p\p")
let referenceSeparator = let referenceSeparator =
case outputFormat case outputFormat
of eofPlain: of eofPlain, eofAnsi:
if body.contains("\pa. "): "\p\p" if body.contains("\pa. "): "\p\p"
elif body.contains("\p" & ansiCyan & "a. "): "\p\p"
else: "\p" else: "\p"
of eofMarkdown: of eofMarkdown:
if body.contains("\p[^a]: "): "\p\p" if body.contains("\p[^a]: "): "\p\p"
@@ -481,7 +621,7 @@ proc fetchFormattedPassages(
let referenceLine = let referenceLine =
case outputFormat case outputFormat
of eofPlain: of eofPlain, eofAnsi:
" " & $parsedReference & " (" & translationName.toUpperAscii & ")" " " & $parsedReference & " (" & translationName.toUpperAscii & ")"
of eofMarkdown: of eofMarkdown:
"> -- *" & $parsedReference & " (" & "> -- *" & $parsedReference & " (" &
@@ -489,7 +629,7 @@ proc fetchFormattedPassages(
let formattedBody = let formattedBody =
case outputFormat case outputFormat
of eofPlain: of eofPlain, eofAnsi:
body body
of eofMarkdown: of eofMarkdown:
var markdownLines: seq[string] var markdownLines: seq[string]
@@ -508,19 +648,42 @@ proc fetchPlainPassages*(
rows, rows,
reference, reference,
translationName: string, translationName: string,
keepVerseNumbers = true): seq[string] = keepVerseNumbers = true,
annotations = "all"): seq[string] =
fetchFormattedPassages( fetchFormattedPassages(
rows, rows,
reference, reference,
translationName, translationName,
keepVerseNumbers, keepVerseNumbers,
eofPlain) eofPlain,
annotations)
proc fetchAnsiPassages*(
rows,
reference,
translationName: string,
keepVerseNumbers = true,
annotations = "all"): seq[string] =
fetchFormattedPassages(
rows,
reference,
translationName,
keepVerseNumbers,
eofAnsi,
annotations)
proc fetchMarkdownPassages*(
rows,
reference,
translationName: string,
annotations = "all"): seq[string] =
proc fetchMarkdownPassages*(rows, reference, translationName: string): seq[string] =
fetchFormattedPassages( fetchFormattedPassages(
rows, rows,
reference, reference,
translationName, translationName,
keepVerseNumbers = true, keepVerseNumbers = true,
eofMarkdown) eofMarkdown,
annotations)
+25 -4
View File
@@ -6,8 +6,29 @@ const kjvRows = embeddedTranslationData("kjv")
proc fetchPassages*(reference: string): seq[string] = proc fetchPassages*(reference: string): seq[string] =
embedded_bible.fetchPassages(kjvRows, reference, "KJV") embedded_bible.fetchPassages(kjvRows, reference, "KJV")
proc fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string] = proc fetchPlainPassages*(
embedded_bible.fetchPlainPassages(kjvRows, reference, "KJV", keepVerseNumbers) reference: string,
keepVerseNumbers = true,
annotations = "all"): seq[string] =
proc fetchMarkdownPassages*(reference: string): seq[string] = embedded_bible.fetchPlainPassages(
embedded_bible.fetchMarkdownPassages(kjvRows, reference, "KJV") kjvRows,
reference,
"KJV",
keepVerseNumbers,
annotations)
proc fetchMarkdownPassages*(reference: string, annotations = "all"): seq[string] =
embedded_bible.fetchMarkdownPassages(kjvRows, reference, "KJV", annotations)
proc fetchAnsiPassages*(
reference: string,
keepVerseNumbers = true,
annotations = "all"): seq[string] =
embedded_bible.fetchAnsiPassages(
kjvRows,
reference,
"KJV",
keepVerseNumbers,
annotations)
+29 -4
View File
@@ -12,16 +12,41 @@ proc fetchPassages*(reference: string): seq[string] =
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 fetchPlainPassages*(reference: string, keepVerseNumbers = true): seq[string] = proc fetchPlainPassages*(
reference: string,
keepVerseNumbers = true,
annotations = "all"): seq[string] =
when hasEmbeddedTranslationData("mev"): when hasEmbeddedTranslationData("mev"):
embedded_bible.fetchPlainPassages(mevRows, reference, "MEV", keepVerseNumbers) embedded_bible.fetchPlainPassages(
mevRows,
reference,
"MEV",
keepVerseNumbers,
annotations)
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] = proc fetchMarkdownPassages*(reference: string, annotations = "all"): seq[string] =
when hasEmbeddedTranslationData("mev"): when hasEmbeddedTranslationData("mev"):
embedded_bible.fetchMarkdownPassages(mevRows, reference, "MEV") embedded_bible.fetchMarkdownPassages(mevRows, reference, "MEV", annotations)
else:
raise newException(ValueError,
"MEV data is not embedded; generate data/private/mev.tsv and rebuild")
proc fetchAnsiPassages*(
reference: string,
keepVerseNumbers = true,
annotations = "all"): seq[string] =
when hasEmbeddedTranslationData("mev"):
embedded_bible.fetchAnsiPassages(
mevRows,
reference,
"MEV",
keepVerseNumbers,
annotations)
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")
+87
View File
@@ -187,6 +187,71 @@ HEB 4 7 2 line 1 do not harden your hearts."
"\n" & "\n" &
" John 8:1-2 (TEST)" " John 8:1-2 (TEST)"
test "plain output can suppress annotation footnotes":
const rows =
"JHN\t8\t1\t0\tparagraph\t0\tJesus spoke.\t" &
"""{"annotations":[{"kind":"crossReference","offset":12,"targetText":"Mt 21:1"}]}""" & "\n"
let passages = embedded_bible.fetchPlainPassages(
rows,
"John 8:1",
"TEST",
annotations = "none")
check passages[0] == "1 Jesus spoke.\n" &
" John 8:1 (TEST)"
test "plain output filters annotation footnotes by kind":
const rows =
"JHN\t8\t1\t0\tparagraph\t0\tJesus spoke plainly.\t" &
"""{"annotations":[{"kind":"note","offset":20,"targetText":"A note."}]}""" & "\n" &
"JHN\t8\t2\t0\tsame\t0\tHe taught.\t" &
"""{"annotations":[{"kind":"crossReference","offset":10,"targetText":"Mt 26:55"}]}""" & "\n"
let notesOnly = embedded_bible.fetchPlainPassages(
rows,
"John 8:1-2",
"TEST",
annotations = "note")
let crossReferencesOnly = embedded_bible.fetchPlainPassages(
rows,
"John 8:1-2",
"TEST",
annotations = "crossReference")
let both = embedded_bible.fetchPlainPassages(
rows,
"John 8:1-2",
"TEST",
annotations = "note,crossReference")
check notesOnly[0] == "1 Jesus spoke plainly.[a] 2 He taught.\n" &
"\n" &
"a. A note.\n" &
"\n" &
" John 8:1-2 (TEST)"
check crossReferencesOnly[0] == "1 Jesus spoke plainly. 2 He taught.[a]\n" &
"\n" &
"a. Mt 26:55\n" &
"\n" &
" John 8:1-2 (TEST)"
check both[0] == "1 Jesus spoke plainly.[a] 2 He taught.[b]\n" &
"\n" &
"a. A note.\n" &
"b. Mt 26:55\n" &
"\n" &
" John 8:1-2 (TEST)"
test "rejects unknown annotation filter kinds":
const rows =
"JHN\t8\t1\t0\tparagraph\t0\tJesus spoke.\t" &
"""{"annotations":[{"kind":"note","offset":12,"targetText":"A note."}]}""" & "\n"
expect ValueError:
discard embedded_bible.fetchPlainPassages(
rows,
"John 8:1",
"TEST",
annotations = "gloss")
test "plain output resets footnote labels per chapter": test "plain output resets footnote labels per chapter":
const rows = const rows =
"JHN\t8\t59\t0\tparagraph\t0\tThey took up stones.\t" & "JHN\t8\t59\t0\tparagraph\t0\tThey took up stones.\t" &
@@ -238,3 +303,25 @@ 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 "ansi output renders verse numbers in gray and words of Christ in dark red":
const rows =
"JHN\t8\t1\t0\tparagraph\t0\tJesus said.\t" &
"""{"wordsOfChrist":true}""" & "\n"
let passages = embedded_bible.fetchAnsiPassages(rows, "John 8:1", "TEST")
check passages[0] == "\x1b[90m1\x1b[0m \x1b[31mJesus said.\x1b[0m\n" &
" John 8:1 (TEST)"
test "ansi output renders footnotes in cyan":
const rows =
"JHN\t8\t1\t0\tparagraph\t0\tJesus said.\t" &
"""{"wordsOfChrist":true,"annotations":[{"kind":"crossReference","offset":11,"targetText":"Mt 21:1"}]}""" &
"\n"
let passages = embedded_bible.fetchAnsiPassages(rows, "John 8:1", "TEST")
check passages[0] == "\x1b[90m1\x1b[0m \x1b[31mJesus said.\x1b[36m[a]\x1b[0m\x1b[31m\x1b[0m\n" &
"\n" &
"\x1b[36ma. Mt 21:1\x1b[0m\n" &
"\n" &
" John 8:1 (TEST)"