M src/xidoc.nim => src/xidoc.nim +2 -0
@@ 29,6 29,8 @@ proc renderXidoc*(body: string, path = "", target = tHtml, snippet = false, safe
verbose: verbose,
stack: @[Frame(
cmdName: "[top]",
+ cmdPos: body.low..body.high,
+ cmdArgPos: body.low..body.high,
path: some(path),
)]
)
M src/xidocpkg/error.nim => src/xidocpkg/error.nim +15 -1
@@ 15,6 15,18 @@ proc xidocWarning*(msge: string) =
when not defined(js):
stderr.writeLine("Warning: " & msge)
+func posToCoords(body: string, pos: int): tuple[row, col: int] =
+ var newlines = 0
+ var lastNewline = -1
+ for i in 0..<pos:
+ if body[i] == '\n':
+ newlines.inc
+ lastNewline = i
+ return (newlines + 1, pos - lastNewline)
+
+func formatPos(pos: tuple[row, col: int]): string =
+ &"{pos.row}:{pos.col}"
+
proc format*(err: XidocError, doc: Document, termColors: bool): FormattedXidocError =
const
red = "\e[91m"
@@ 35,7 47,9 @@ proc format*(err: XidocError, doc: Document, termColors: bool): FormattedXidocEr
let numOpeningBrackets = truncatedArg.count('[')
let numClosingBrackets = truncatedArg.count(']')
truncatedArg.add "]…".repeat(numOpeningBrackets - numClosingBrackets)
- msg &= &"in [{frame.cmdName}{truncatedArg}]\n"
+ let posA = posToCoords(doc.body, frame.cmdPos.a).formatPos
+ let posB = posToCoords(doc.body, frame.cmdPos.b).formatPos
+ msg &= &"[{posA}–{posB}] in [{frame.cmdName}{truncatedArg}]\n"
if termColors:
msg &= reset
msg &= err.msg
M src/xidocpkg/expand.nim => src/xidocpkg/expand.nim +7 -1
@@ 6,6 6,9 @@ import std/options
import std/strformat
import std/strutils
+proc `+`(s: Slice[int], n: int): Slice[int] =
+ s.a + n .. s.b + n
+
proc escapeText*(text: string, target: Target): string =
case target
of tHtml:
@@ 58,7 61,10 @@ proc expand*(doc: Document, str: string, typ: XidocType): XidocValue =
let name = node.name
let command = doc.lookup(commands, name)
ifSome command:
- var frame = Frame(cmdName: name, cmdArg: node.arg)
+ let offset = doc.stack[^1].cmdArgPos.a
+ var frame = Frame(cmdName: name, cmdArg: node.arg,
+ cmdPos: node.pos + offset,
+ cmdArgPos: node.argPos + offset)
doc.stack.add frame
let val = command(node.arg)
discard doc.stack.pop
M src/xidocpkg/parser.nim => src/xidocpkg/parser.nim +12 -3
@@ 8,6 8,7 @@ type
xnkWhitespace
xnkCommand
XidocNode* = object
+ pos*: Slice[int]
case kind*: XidocNodeKind
of xnkString:
str*: string
@@ 16,6 17,7 @@ type
of xnkCommand:
name*: string
arg*: string
+ argPos*: Slice[int]
XidocNodes* = seq[XidocNode]
const nonTextChars = Whitespace + {'[', ']'}
@@ 62,17 64,20 @@ proc parseXidocString(body: string, i: var int): XidocNode =
## Finds a string of non-whitespace non-brackets characters in `body`,
## starting from position `i` and leaving `i` after the found substring.
## Returns a `XidocNode` with kind `xnkString`.
- XidocNode(kind: xnkString, str: parseXidocStringHelper(body, i))
+ let start = i
+ let str = parseXidocStringHelper(body, i)
+ XidocNode(pos: start..<i, kind: xnkString, str: str)
proc parseXidocWhitespace(body: string, i: var int): XidocNode =
## Finds a string of whitespace characters in `body`,
## starting from position `i` and leaving `i` after the found substring.
## Returns a `XidocNode` with kind `xnkWhitespace`.
+ let start = i
var newline = false
while i <= body.high and body[i] in Whitespace:
newline = newline or body[i] == '\n'
i.inc
- XidocNode(kind: xnkWhitespace, newline: newline)
+ XidocNode(pos: start..<i, kind: xnkWhitespace, newline: newline)
proc parseXidocCommand(body: string, i: var int): XidocNode =
## Finds a xidoc command in `body`,
@@ 80,6 85,7 @@ proc parseXidocCommand(body: string, i: var int): XidocNode =
## `body[i]` must be '[' at the start.
## Returns a `XidocNode` with kind `xnkCommand`.
assert body[i] == '['
+ let start = i
i.inc
let name = parseXidocStringHelper(body, i)
if i > body.high:
@@ 91,7 97,10 @@ proc parseXidocCommand(body: string, i: var int): XidocNode =
skipBalancedText(body, i)
if i > body.high:
xidocError "Parse error: Unexpected end of file (did you forget to close a bracket?)"
- result = XidocNode(kind: xnkCommand, name: name, arg: body[argStart..<i])
+ result = XidocNode(
+ pos: start..<i+1,
+ kind: xnkCommand, name: name, arg: body[argStart..<i], argPos: argStart..<i
+ )
i.inc
proc parseXidoc*(body: string, verbose = false): XidocNodes =
M src/xidocpkg/types.nim => src/xidocpkg/types.nim +2 -0
@@ 35,6 35,8 @@ type
args*: Table[string, string]
cmdArg*: string
cmdName*: string
+ cmdPos*: Slice[int]
+ cmdArgPos*: Slice[int]
commands*: Table[string, Command]
lang*: Option[Language]
path*: Option[string]