rst: add missing line/column info for some warnings (#18383)
* rst: add missing line/column info for some warnings * add workaround * use TLineInfo/FileIndex for storing file names * fix blank lines in include file (rm harmful strip) * don't use ref TLineInfo * return `hasToc` as output parameter for uniformity * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * address review - stylistic things * Update compiler/docgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * unify RST warnings/errors names * doAssert + minor name change * fix a bug caught by doAssert * apply strbasics.strip to final HTML/Latex * rm redundant filename * fix test after rebase * delete `order` from rnFootnoteRef, also display errors/warnings properly when footnote references are from different files * Update compiler/lineinfos.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * revert because of error: Error: cannot prove that it's safe to initialize 'info' with the runtime value for the discriminator 'kind' * Update lib/packages/docutils/rstgen.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * apply suggestion * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> * add Table for string->file name mapping * do not import compiler/lineinfos * fix ambiguous calls Co-authored-by: Timothee Cour <timothee.cour2@gmail.com> Co-authored-by: narimiran <narimiran@disroot.org>
This commit is contained in:
parent
44c5afe448
commit
8c7ee96457
8 changed files with 342 additions and 193 deletions
|
|
@ -196,7 +196,7 @@
|
|||
|
||||
import
|
||||
os, strutils, rstast, std/enumutils, algorithm, lists, sequtils,
|
||||
std/private/miscdollars
|
||||
std/private/miscdollars, tables
|
||||
from highlite import SourceLanguage, getSourceLanguage
|
||||
|
||||
type
|
||||
|
|
@ -217,6 +217,7 @@ type
|
|||
mcWarning = "Warning",
|
||||
mcError = "Error"
|
||||
|
||||
# keep the order in sync with compiler/docgen.nim and compiler/lineinfos.nim:
|
||||
MsgKind* = enum ## the possible messages
|
||||
meCannotOpenFile = "cannot open '$1'",
|
||||
meExpected = "'$1' expected",
|
||||
|
|
@ -225,10 +226,11 @@ type
|
|||
meNewSectionExpected = "new section expected $1",
|
||||
meGeneralParseError = "general parse error",
|
||||
meInvalidDirective = "invalid directive: '$1'",
|
||||
meInvalidRstField = "invalid field: $1",
|
||||
meInvalidField = "invalid field: $1",
|
||||
meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1",
|
||||
mwRedefinitionOfLabel = "redefinition of label '$1'",
|
||||
mwUnknownSubstitution = "unknown substitution '$1'",
|
||||
mwBrokenLink = "broken link '$1'",
|
||||
mwUnsupportedLanguage = "language '$1' not supported",
|
||||
mwUnsupportedField = "field '$1' not supported",
|
||||
mwRstStyle = "RST style: $1"
|
||||
|
|
@ -489,7 +491,9 @@ type
|
|||
autoNumIdx: int # order of occurence: fnAutoNumber, fnAutoNumberLabel
|
||||
autoSymIdx: int # order of occurence: fnAutoSymbol
|
||||
label: string # valid for fnAutoNumberLabel
|
||||
|
||||
RstFileTable* = object
|
||||
filenameToIdx*: Table[string, FileIndex]
|
||||
idxToFilename*: seq[string]
|
||||
RstSharedState = object
|
||||
options: RstParseOptions # parsing options
|
||||
hLevels: LevelMap # hierarchy of heading styles
|
||||
|
|
@ -501,15 +505,19 @@ type
|
|||
subs: seq[Substitution] # substitutions
|
||||
refs*: seq[Substitution] # references
|
||||
anchors*: seq[AnchorSubst] # internal target substitutions
|
||||
lineFootnoteNum: seq[int] # footnote line, auto numbers .. [#]
|
||||
lineFootnoteNumRef: seq[int] # footnote line, their reference [#]_
|
||||
lineFootnoteSym: seq[int] # footnote line, auto symbols .. [*]
|
||||
lineFootnoteSymRef: seq[int] # footnote line, their reference [*]_
|
||||
lineFootnoteNum: seq[TLineInfo] # footnote line, auto numbers .. [#]
|
||||
lineFootnoteNumRef: seq[TLineInfo] # footnote line, their reference [#]_
|
||||
currFootnoteNumRef: int # ... their counter for `resolveSubs`
|
||||
lineFootnoteSym: seq[TLineInfo] # footnote line, auto symbols .. [*]
|
||||
lineFootnoteSymRef: seq[TLineInfo] # footnote line, their reference [*]_
|
||||
currFootnoteSymRef: int # ... their counter for `resolveSubs`
|
||||
footnotes: seq[FootnoteSubst] # correspondence b/w footnote label,
|
||||
# number, order of occurrence
|
||||
msgHandler: MsgHandler # How to handle errors.
|
||||
findFile: FindFileHandler # How to find files.
|
||||
filename: string
|
||||
filenames*: RstFileTable # map file name <-> FileIndex (for storing
|
||||
# file names for warnings after 1st stage)
|
||||
currFileIdx: FileIndex # current index in `filesnames`
|
||||
hasToc*: bool
|
||||
|
||||
PRstSharedState* = ref RstSharedState
|
||||
|
|
@ -579,6 +587,25 @@ proc whichRoleAux(sym: string): RstNodeKind =
|
|||
else: # unknown role
|
||||
result = rnUnknownRole
|
||||
|
||||
proc len(filenames: RstFileTable): int = filenames.idxToFilename.len
|
||||
|
||||
proc setCurrFilename(s: PRstSharedState, file1: string) =
|
||||
let nextIdx = s.filenames.len.FileIndex
|
||||
let v = getOrDefault(s.filenames.filenameToIdx, file1, default = nextIdx)
|
||||
if v == nextIdx:
|
||||
s.filenames.filenameToIdx[file1] = v
|
||||
s.filenames.idxToFilename.add file1
|
||||
s.currFileIdx = v
|
||||
|
||||
proc getFilename(filenames: RstFileTable, fid: FileIndex): string =
|
||||
doAssert(0 <= fid.int and fid.int < filenames.len,
|
||||
"incorrect FileIndex $1 (range 0..$2)" % [
|
||||
$fid.int, $(filenames.len - 1)])
|
||||
result = filenames.idxToFilename[fid.int]
|
||||
|
||||
proc currFilename(s: PRstSharedState): string =
|
||||
getFilename(s.filenames, s.currFileIdx)
|
||||
|
||||
proc newRstSharedState*(options: RstParseOptions,
|
||||
filename: string,
|
||||
findFile: FindFileHandler,
|
||||
|
|
@ -589,32 +616,37 @@ proc newRstSharedState*(options: RstParseOptions,
|
|||
currRoleKind: whichRoleAux(r),
|
||||
options: options,
|
||||
msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler,
|
||||
filename: filename,
|
||||
findFile: if not isNil(findFile): findFile else: defaultFindFile
|
||||
)
|
||||
setCurrFilename(result, filename)
|
||||
|
||||
proc curLine(p: RstParser): int = p.line + currentTok(p).line
|
||||
|
||||
proc findRelativeFile(p: RstParser; filename: string): string =
|
||||
result = p.s.filename.splitFile.dir / filename
|
||||
result = p.s.currFilename.splitFile.dir / filename
|
||||
if not fileExists(result):
|
||||
result = p.s.findFile(filename)
|
||||
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
|
||||
p.s.msgHandler(p.s.filename, curLine(p),
|
||||
p.s.msgHandler(p.s.currFilename, curLine(p),
|
||||
p.col + currentTok(p).col, msgKind, arg)
|
||||
|
||||
proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) =
|
||||
## Print warnings for footnotes/substitutions.
|
||||
## TODO: their line/column info is not known, to fix it.
|
||||
s.msgHandler(s.filename, LineRstInit, ColRstInit, msgKind, arg)
|
||||
s.msgHandler(s.currFilename, LineRstInit, ColRstInit, msgKind, arg)
|
||||
|
||||
proc rstMessage*(filenames: RstFileTable, f: MsgHandler,
|
||||
info: TLineInfo, msgKind: MsgKind, arg: string) =
|
||||
## Print warnings using `info`, i.e. in 2nd-pass warnings for
|
||||
## footnotes/substitutions/references or from ``rstgen.nim``.
|
||||
let file = getFilename(filenames, info.fileIndex)
|
||||
f(file, info.line.int, info.col.int, msgKind, arg)
|
||||
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
|
||||
p.s.msgHandler(p.s.filename, p.line + line,
|
||||
p.s.msgHandler(p.s.currFilename, p.line + line,
|
||||
p.col + col, msgKind, arg)
|
||||
|
||||
proc rstMessage(p: RstParser, msgKind: MsgKind) =
|
||||
p.s.msgHandler(p.s.filename, curLine(p),
|
||||
p.s.msgHandler(p.s.currFilename, curLine(p),
|
||||
p.col + currentTok(p).col, msgKind,
|
||||
currentTok(p).symbol)
|
||||
|
||||
|
|
@ -827,11 +859,18 @@ proc addFootnoteNumManual(p: var RstParser, num: int) =
|
|||
return
|
||||
p.s.footnotes.add((fnManualNumber, num, -1, -1, $num))
|
||||
|
||||
proc lineInfo(p: RstParser, iTok: int): TLineInfo =
|
||||
result.col = int16(p.col + p.tok[iTok].col)
|
||||
result.line = uint16(p.line + p.tok[iTok].line)
|
||||
result.fileIndex = p.s.currFileIdx
|
||||
|
||||
proc lineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx)
|
||||
|
||||
proc addFootnoteNumAuto(p: var RstParser, label: string) =
|
||||
## add auto-numbered footnote.
|
||||
## Empty label [#] means it'll be resolved by the occurrence.
|
||||
if label == "": # simple auto-numbered [#]
|
||||
p.s.lineFootnoteNum.add curLine(p)
|
||||
p.s.lineFootnoteNum.add lineInfo(p)
|
||||
p.s.footnotes.add((fnAutoNumber, -1, p.s.lineFootnoteNum.len, -1, label))
|
||||
else: # auto-numbered with label [#label]
|
||||
for fnote in p.s.footnotes:
|
||||
|
|
@ -841,7 +880,7 @@ proc addFootnoteNumAuto(p: var RstParser, label: string) =
|
|||
p.s.footnotes.add((fnAutoNumberLabel, -1, -1, -1, label))
|
||||
|
||||
proc addFootnoteSymAuto(p: var RstParser) =
|
||||
p.s.lineFootnoteSym.add curLine(p)
|
||||
p.s.lineFootnoteSym.add lineInfo(p)
|
||||
p.s.footnotes.add((fnAutoSymbol, -1, -1, p.s.lineFootnoteSym.len, ""))
|
||||
|
||||
proc orderFootnotes(s: PRstSharedState) =
|
||||
|
|
@ -850,7 +889,15 @@ proc orderFootnotes(s: PRstSharedState) =
|
|||
## Save the result back to `s.footnotes`.
|
||||
|
||||
# Report an error if found any mismatch in number of automatic footnotes
|
||||
proc listFootnotes(lines: seq[int]): string =
|
||||
proc listFootnotes(locations: seq[TLineInfo]): string =
|
||||
var lines: seq[string]
|
||||
for info in locations:
|
||||
if s.filenames.len > 1:
|
||||
let file = getFilename(s.filenames, info.fileIndex)
|
||||
lines.add file & ":"
|
||||
else: # no need to add file name here if there is only 1
|
||||
lines.add ""
|
||||
lines[^1].add $info.line
|
||||
result.add $lines.len & " (lines " & join(lines, ", ") & ")"
|
||||
if s.lineFootnoteNum.len != s.lineFootnoteNumRef.len:
|
||||
rstMessage(s, meFootnoteMismatch,
|
||||
|
|
@ -1157,7 +1204,7 @@ proc whichRole(p: RstParser, sym: string): RstNodeKind =
|
|||
|
||||
proc toInlineCode(n: PRstNode, language: string): PRstNode =
|
||||
## Creates rnInlineCode and attaches `n` contents as code (in 3rd son).
|
||||
result = newRstNode(rnInlineCode)
|
||||
result = newRstNode(rnInlineCode, info=n.info)
|
||||
let args = newRstNode(rnDirArg)
|
||||
var lang = language
|
||||
if language == "cpp": lang = "c++"
|
||||
|
|
@ -1179,6 +1226,7 @@ proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode =
|
|||
result = newRstNode(kind, newSons)
|
||||
|
||||
proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
|
||||
## Finalizes node `n` that was tentatively determined as interpreted text.
|
||||
var newKind = n.kind
|
||||
var newSons = n.sons
|
||||
|
||||
|
|
@ -1207,12 +1255,10 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
|
|||
newKind = rnHyperlink
|
||||
newSons = @[a, b]
|
||||
setRef(p, rstnodeToRefname(a), b)
|
||||
elif n.kind == rnInterpretedText:
|
||||
result = newRstNode(newKind, newSons)
|
||||
else: # some link that will be resolved in `resolveSubs`
|
||||
newKind = rnRef
|
||||
else:
|
||||
newKind = rnRef
|
||||
newSons = @[n]
|
||||
result = newRstNode(newKind, newSons)
|
||||
result = newRstNode(newKind, sons=newSons, info=n.info)
|
||||
elif match(p, p.idx, ":w:"):
|
||||
# a role:
|
||||
let (roleName, lastIdx) = getRefname(p, p.idx+1)
|
||||
|
|
@ -1300,20 +1346,19 @@ proc parseWordOrRef(p: var RstParser, father: PRstNode) =
|
|||
else:
|
||||
# check for reference (probably, long one like some.ref.with.dots_ )
|
||||
var saveIdx = p.idx
|
||||
var isRef = false
|
||||
var reference: PRstNode = nil
|
||||
inc p.idx
|
||||
while currentTok(p).kind in {tkWord, tkPunct}:
|
||||
if currentTok(p).kind == tkPunct:
|
||||
if isInlineMarkupEnd(p, "_", exact=true):
|
||||
isRef = true
|
||||
reference = newRstNode(rnRef, info=lineInfo(p, saveIdx))
|
||||
break
|
||||
if not validRefnamePunct(currentTok(p).symbol):
|
||||
break
|
||||
inc p.idx
|
||||
if isRef:
|
||||
let r = newRstNode(rnRef)
|
||||
for i in saveIdx..p.idx-1: r.add newLeaf(p.tok[i].symbol)
|
||||
father.add r
|
||||
if reference != nil:
|
||||
for i in saveIdx..p.idx-1: reference.add newLeaf(p.tok[i].symbol)
|
||||
father.add reference
|
||||
inc p.idx # skip final _
|
||||
else: # 1 normal word
|
||||
father.add newLeaf(p.tok[saveIdx].symbol)
|
||||
|
|
@ -1387,6 +1432,8 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
|
|||
else: rstMessage(p, meExpected, postfix, line, col)
|
||||
|
||||
proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
|
||||
result = newRstNodeA(p, rnCodeBlock)
|
||||
result.info = lineInfo(p)
|
||||
var args = newRstNode(rnDirArg)
|
||||
if currentTok(p).kind == tkWord:
|
||||
args.add(newLeaf(p))
|
||||
|
|
@ -1411,7 +1458,6 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
|
|||
inc p.idx
|
||||
var lb = newRstNode(rnLiteralBlock)
|
||||
lb.add(n)
|
||||
result = newRstNodeA(p, rnCodeBlock)
|
||||
result.add(args)
|
||||
result.add(PRstNode(nil))
|
||||
result.add(lb)
|
||||
|
|
@ -1495,6 +1541,7 @@ proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode =
|
|||
|
||||
proc parseInline(p: var RstParser, father: PRstNode) =
|
||||
var n: PRstNode # to be used in `if` condition
|
||||
let saveIdx = p.idx
|
||||
case currentTok(p).kind
|
||||
of tkPunct:
|
||||
if isInlineMarkupStart(p, "***"):
|
||||
|
|
@ -1537,12 +1584,12 @@ proc parseInline(p: var RstParser, father: PRstNode) =
|
|||
n = n.toOtherRole(k, roleName)
|
||||
father.add(n)
|
||||
elif isInlineMarkupStart(p, "`"):
|
||||
var n = newRstNode(rnInterpretedText)
|
||||
var n = newRstNode(rnInterpretedText, info=lineInfo(p, p.idx+1))
|
||||
parseUntil(p, n, "`", false) # bug #17260
|
||||
n = parsePostfix(p, n)
|
||||
father.add(n)
|
||||
elif isInlineMarkupStart(p, "|"):
|
||||
var n = newRstNode(rnSubstitutionReferences)
|
||||
var n = newRstNode(rnSubstitutionReferences, info=lineInfo(p, p.idx+1))
|
||||
parseUntil(p, n, "|", false)
|
||||
father.add(n)
|
||||
elif roSupportMarkdown in p.s.options and
|
||||
|
|
@ -1552,15 +1599,14 @@ proc parseInline(p: var RstParser, father: PRstNode) =
|
|||
elif isInlineMarkupStart(p, "[") and nextTok(p).symbol != "[" and
|
||||
(n = parseFootnoteName(p, reference=true); n != nil):
|
||||
var nn = newRstNode(rnFootnoteRef)
|
||||
nn.info = lineInfo(p, saveIdx+1)
|
||||
nn.add n
|
||||
let (fnType, _) = getFootnoteType(n)
|
||||
case fnType
|
||||
of fnAutoSymbol:
|
||||
p.s.lineFootnoteSymRef.add curLine(p)
|
||||
nn.order = p.s.lineFootnoteSymRef.len
|
||||
p.s.lineFootnoteSymRef.add lineInfo(p)
|
||||
of fnAutoNumber:
|
||||
p.s.lineFootnoteNumRef.add curLine(p)
|
||||
nn.order = p.s.lineFootnoteNumRef.len
|
||||
p.s.lineFootnoteNumRef.add lineInfo(p)
|
||||
else: discard
|
||||
father.add(nn)
|
||||
else:
|
||||
|
|
@ -1693,7 +1739,7 @@ proc parseField(p: var RstParser): PRstNode =
|
|||
## Returns a parsed rnField node.
|
||||
##
|
||||
## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
|
||||
result = newRstNode(rnField)
|
||||
result = newRstNode(rnField, info=lineInfo(p))
|
||||
var col = currentTok(p).col
|
||||
var fieldname = newRstNode(rnFieldName)
|
||||
parseUntil(p, fieldname, ":", false)
|
||||
|
|
@ -2469,6 +2515,7 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode
|
|||
## Both rnDirArg and rnFieldList children nodes might be nil, so you need to
|
||||
## check them before accessing.
|
||||
result = newRstNodeA(p, k)
|
||||
if k == rnCodeBlock: result.info = lineInfo(p)
|
||||
var args: PRstNode = nil
|
||||
var options: PRstNode = nil
|
||||
if hasArg in flags:
|
||||
|
|
@ -2593,14 +2640,16 @@ proc dirInclude(p: var RstParser): PRstNode =
|
|||
|
||||
var q: RstParser
|
||||
initParser(q, p.s)
|
||||
q.s.filename = path
|
||||
let saveFileIdx = p.s.currFileIdx
|
||||
setCurrFilename(p.s, path)
|
||||
getTokens(
|
||||
inputString[startPosition..endPosition].strip(),
|
||||
inputString[startPosition..endPosition],
|
||||
q.tok)
|
||||
# workaround a GCC bug; more like the interior pointer bug?
|
||||
#if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0:
|
||||
# InternalError("Too many binary zeros in include file")
|
||||
result = parseDoc(q)
|
||||
p.s.currFileIdx = saveFileIdx
|
||||
|
||||
proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
|
||||
## Parses a code block.
|
||||
|
|
@ -2634,7 +2683,7 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
|
|||
if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList)
|
||||
assert result.sons[1].kind == rnFieldList
|
||||
# Hook the extra field and specify the Nim language as value.
|
||||
var extraNode = newRstNode(rnField)
|
||||
var extraNode = newRstNode(rnField, info=lineInfo(p))
|
||||
extraNode.add(newRstNode(rnFieldName))
|
||||
extraNode.add(newRstNode(rnFieldBody))
|
||||
extraNode.sons[0].add newLeaf("default-language")
|
||||
|
|
@ -2837,9 +2886,8 @@ proc parseDotDot(p: var RstParser): PRstNode =
|
|||
else:
|
||||
result = parseComment(p, col)
|
||||
|
||||
proc rstParsePass1*(fragment, filename: string,
|
||||
proc rstParsePass1*(fragment: string,
|
||||
line, column: int,
|
||||
options: RstParseOptions,
|
||||
sharedState: PRstSharedState): PRstNode =
|
||||
## Parses an RST `fragment`.
|
||||
## The result should be further processed by
|
||||
|
|
@ -2872,7 +2920,8 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
|
|||
var key = addNodes(n)
|
||||
var e = getEnv(key)
|
||||
if e != "": result = newLeaf(e)
|
||||
else: rstMessage(s, mwUnknownSubstitution, key)
|
||||
else: rstMessage(s.filenames, s.msgHandler, n.info,
|
||||
mwUnknownSubstitution, key)
|
||||
of rnHeadline, rnOverline:
|
||||
# fix up section levels depending on presence of a title and subtitle
|
||||
if s.hTitleCnt == 2:
|
||||
|
|
@ -2890,12 +2939,14 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
|
|||
let text = newRstNode(rnInner, n.sons)
|
||||
result.sons = @[text, y]
|
||||
else:
|
||||
let s = findMainAnchor(s, refn)
|
||||
if s != "":
|
||||
let anchor = findMainAnchor(s, refn)
|
||||
if anchor != "":
|
||||
result = newRstNode(rnInternalRef)
|
||||
let text = newRstNode(rnInner, n.sons)
|
||||
result.sons = @[text, # visible text of reference
|
||||
newLeaf(s)] # link itself
|
||||
result.sons = @[text, # visible text of reference
|
||||
newLeaf(anchor)] # link itself
|
||||
else:
|
||||
rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn)
|
||||
of rnFootnote:
|
||||
var (fnType, num) = getFootnoteType(n.sons[0])
|
||||
case fnType
|
||||
|
|
@ -2922,20 +2973,22 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
|
|||
result.add(nn)
|
||||
var refn = fnType.prefix
|
||||
# create new rnFootnoteRef, add final label, and finalize target refn:
|
||||
result = newRstNode(rnFootnoteRef)
|
||||
result = newRstNode(rnFootnoteRef, info = n.info)
|
||||
case fnType
|
||||
of fnManualNumber:
|
||||
addLabel num
|
||||
refn.add $num
|
||||
of fnAutoNumber:
|
||||
addLabel getFootnoteNum(s, n.order)
|
||||
refn.add $n.order
|
||||
inc s.currFootnoteNumRef
|
||||
addLabel getFootnoteNum(s, s.currFootnoteNumRef)
|
||||
refn.add $s.currFootnoteNumRef
|
||||
of fnAutoNumberLabel:
|
||||
addLabel getFootnoteNum(s, rstnodeToRefname(n))
|
||||
refn.add rstnodeToRefname(n)
|
||||
of fnAutoSymbol:
|
||||
addLabel getAutoSymbol(s, n.order)
|
||||
refn.add $n.order
|
||||
inc s.currFootnoteSymRef
|
||||
addLabel getAutoSymbol(s, s.currFootnoteSymRef)
|
||||
refn.add $s.currFootnoteSymRef
|
||||
of fnCitation:
|
||||
result.add n.sons[0]
|
||||
refn.add rstnodeToRefname(n)
|
||||
|
|
@ -2943,7 +2996,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
|
|||
if anch != "":
|
||||
result.add newLeaf(anch) # add link
|
||||
else:
|
||||
rstMessage(s, mwUnknownSubstitution, refn)
|
||||
rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn)
|
||||
result.add newLeaf(refn) # add link
|
||||
of rnLeaf:
|
||||
discard
|
||||
|
|
@ -2971,14 +3024,18 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
|
|||
result.sons = newSons
|
||||
|
||||
proc rstParse*(text, filename: string,
|
||||
line, column: int, hasToc: var bool,
|
||||
line, column: int,
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler = nil,
|
||||
msgHandler: MsgHandler = nil): PRstNode =
|
||||
## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`.
|
||||
msgHandler: MsgHandler = nil):
|
||||
tuple[node: PRstNode, filenames: RstFileTable, hasToc: bool] =
|
||||
## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`,
|
||||
## note that 2nd tuple element should be fed to `initRstGenerator`
|
||||
## argument `filenames` (it is being filled here at least with `filename`
|
||||
## and possibly with other files from RST ``.. include::`` statement).
|
||||
var sharedState = newRstSharedState(options, filename, findFile, msgHandler)
|
||||
let unresolved = rstParsePass1(text, filename, line, column,
|
||||
options, sharedState)
|
||||
let unresolved = rstParsePass1(text, line, column, sharedState)
|
||||
preparePass2(sharedState, unresolved)
|
||||
result = resolveSubs(sharedState, unresolved)
|
||||
hasToc = sharedState.hasToc
|
||||
result.node = resolveSubs(sharedState, unresolved)
|
||||
result.filenames = sharedState.filenames
|
||||
result.hasToc = sharedState.hasToc
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ type
|
|||
rnLeaf # a leaf; the node's text field contains the
|
||||
# leaf val
|
||||
|
||||
FileIndex* = distinct int32
|
||||
TLineInfo* = object
|
||||
line*: uint16
|
||||
col*: int16
|
||||
fileIndex*: FileIndex
|
||||
|
||||
PRstNode* = ref RstNode ## an RST node
|
||||
RstNodeSeq* = seq[PRstNode]
|
||||
|
|
@ -92,21 +97,32 @@ type
|
|||
level*: int ## level of headings starting from 1 (main
|
||||
## chapter) to larger ones (minor sub-sections)
|
||||
## level=0 means it's document title or subtitle
|
||||
of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem:
|
||||
of rnFootnote, rnCitation, rnOptionListItem:
|
||||
order*: int ## footnote order (for auto-symbol footnotes and
|
||||
## auto-numbered ones without a label)
|
||||
of rnRef, rnSubstitutionReferences,
|
||||
rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef:
|
||||
info*: TLineInfo ## To have line/column info for warnings at
|
||||
## nodes that are post-processed after parsing
|
||||
else:
|
||||
discard
|
||||
anchor*: string ## anchor, internal link target
|
||||
## (aka HTML id tag, aka Latex label/hypertarget)
|
||||
sons*: RstNodeSeq ## the node's sons
|
||||
|
||||
proc `==`*(a, b: FileIndex): bool {.borrow.}
|
||||
|
||||
proc len*(n: PRstNode): int =
|
||||
result = len(n.sons)
|
||||
|
||||
proc newRstNode*(kind: RstNodeKind, sons: seq[PRstNode] = @[],
|
||||
anchor = ""): PRstNode =
|
||||
result = PRstNode(kind: kind, sons: sons, anchor: anchor)
|
||||
|
||||
proc newRstNode*(kind: RstNodeKind, info: TLineInfo,
|
||||
sons: seq[PRstNode] = @[]): PRstNode =
|
||||
result = PRstNode(kind: kind, sons: sons)
|
||||
result.info = info
|
||||
|
||||
proc newRstNode*(kind: RstNodeKind, s: string): PRstNode {.deprecated.} =
|
||||
assert kind in {rnLeaf, rnSmiley}
|
||||
|
|
@ -388,7 +404,7 @@ proc renderRstToStr*(node: PRstNode, indent=0): string =
|
|||
result.add " adType=" & node.adType
|
||||
of rnHeadline, rnOverline, rnMarkdownHeadline:
|
||||
result.add " level=" & $node.level
|
||||
of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem:
|
||||
of rnFootnote, rnCitation, rnOptionListItem:
|
||||
result.add (if node.order == 0: "" else: " order=" & $node.order)
|
||||
else:
|
||||
discard
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
## can be done by simply searching for [footnoteName].
|
||||
|
||||
import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
|
||||
algorithm, parseutils
|
||||
algorithm, parseutils, std/strbasics
|
||||
|
||||
import ../../std/private/since
|
||||
|
||||
|
|
@ -72,11 +72,11 @@ type
|
|||
tocPart*: seq[TocEntry]
|
||||
hasToc*: bool
|
||||
theIndex: string # Contents of the index file to be dumped at the end.
|
||||
options*: RstParseOptions
|
||||
findFile*: FindFileHandler
|
||||
msgHandler*: MsgHandler
|
||||
outDir*: string ## output directory, initialized by docgen.nim
|
||||
destFile*: string ## output (HTML) file, initialized by docgen.nim
|
||||
filenames*: RstFileTable
|
||||
filename*: string ## source Nim or Rst file
|
||||
meta*: array[MetaEnum, string]
|
||||
currentSection: string ## \
|
||||
|
|
@ -112,9 +112,9 @@ proc init(p: var CodeBlockParams) =
|
|||
|
||||
proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
||||
config: StringTableRef, filename: string,
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler = nil,
|
||||
msgHandler: MsgHandler = nil) =
|
||||
msgHandler: MsgHandler = nil,
|
||||
filenames = default(RstFileTable)) =
|
||||
## Initializes a ``RstGenerator``.
|
||||
##
|
||||
## You need to call this before using a ``RstGenerator`` with any other
|
||||
|
|
@ -160,9 +160,9 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
|||
g.target = target
|
||||
g.tocPart = @[]
|
||||
g.filename = filename
|
||||
g.filenames = filenames
|
||||
g.splitAfter = 20
|
||||
g.theIndex = ""
|
||||
g.options = options
|
||||
g.findFile = findFile
|
||||
g.currentSection = ""
|
||||
g.id = 0
|
||||
|
|
@ -908,9 +908,8 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
|
|||
[d.config.getOrDefault"doc.smiley_format" % n.text])
|
||||
|
||||
proc getField1Int(d: PDoc, n: PRstNode, fieldName: string): int =
|
||||
# TODO: proper column/line info
|
||||
template err(msg: string) =
|
||||
d.msgHandler(d.filename, 1, 0, meInvalidRstField, msg)
|
||||
rstMessage(d.filenames, d.msgHandler, n.info, meInvalidField, msg)
|
||||
let value = n.getFieldValue
|
||||
var number: int
|
||||
let nChars = parseInt(value, number)
|
||||
|
|
@ -958,7 +957,8 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
|
|||
params.langStr = n.getFieldValue.strip
|
||||
params.lang = params.langStr.getSourceLanguage
|
||||
else:
|
||||
d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument)
|
||||
rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedField,
|
||||
n.getArgument)
|
||||
|
||||
proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams =
|
||||
## Iterates over all code block fields and returns processed params.
|
||||
|
|
@ -1069,7 +1069,8 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) =
|
|||
dispA(d.target, result, blockStart, blockStart, [])
|
||||
if params.lang == langNone:
|
||||
if len(params.langStr) > 0:
|
||||
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
|
||||
rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedLanguage,
|
||||
params.langStr)
|
||||
for letter in m.text: escChar(d.target, result, letter, emText)
|
||||
else:
|
||||
renderCodeLang(result, params.lang, m.text, d.target)
|
||||
|
|
@ -1564,23 +1565,24 @@ proc rstToHtml*(s: string, options: RstParseOptions,
|
|||
result = ""
|
||||
|
||||
const filen = "input"
|
||||
let (rst, filenames, _) = rstParse(s, filen,
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
options, myFindFile, msgHandler)
|
||||
var d: RstGenerator
|
||||
initRstGenerator(d, outHtml, config, filen, options, myFindFile, msgHandler)
|
||||
var dummyHasToc = false
|
||||
var rst = rstParse(s, filen, line=LineRstInit, column=ColRstInit,
|
||||
dummyHasToc, options, myFindFile, msgHandler)
|
||||
initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, filenames)
|
||||
result = ""
|
||||
renderRstToOut(d, rst, result)
|
||||
strbasics.strip(result)
|
||||
|
||||
|
||||
proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, since: (1, 3).} =
|
||||
## Convenience proc for `renderRstToOut` and `initRstGenerator`.
|
||||
runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}"""
|
||||
if rstSource.len == 0: return
|
||||
var option: bool
|
||||
let (rst, filenames, _) = rstParse(rstSource, "",
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
options)
|
||||
var rstGenera: RstGenerator
|
||||
rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options)
|
||||
rstGenera.renderRstToOut(
|
||||
rstParse(rstSource, "", line=LineRstInit, column=ColRstInit,
|
||||
option, options),
|
||||
result)
|
||||
rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", filenames=filenames)
|
||||
rstGenera.renderRstToOut(rst, result)
|
||||
strbasics.strip(result)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue