Add ANSI output format
This commit is contained in:
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||||
|
|||||||
@@ -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)"
|
||||||
|
|||||||
Reference in New Issue
Block a user