Fix auto links to subheader when TOC is present (#20279)
Fix links to subheader when TOC is present It was observed (in https://github.com/nim-lang/Nim/pull/20112) that links to 2nd- (and subsequent) -level headings fail if TOC is present, e.g.: ```nim .. contents:: Type relations ============== Convertible relation -------------------- Ref. `Convertible relation`_ ``` The problem here is that links are resolved in `rst.nim` but later `rstgen.nim` fixes ("fixes") anchors to make them unique so that TOC always works (if e.g. there was another sub-section like "Convertible relation"). The solution implemented in this PR is to move that fix-up of anchors into `rst.nim`, so that link resolution could know final anchors. The bug seems to be added in https://github.com/nim-lang/Nim/pull/2332 in 2015, that is it is present in Nim 1.0.
This commit is contained in:
parent
9ca6345122
commit
de9cbf6af1
6 changed files with 165 additions and 104 deletions
|
|
@ -58,10 +58,6 @@ type
|
|||
outHtml, # output is HTML
|
||||
outLatex # output is Latex
|
||||
|
||||
TocEntry = object
|
||||
n*: PRstNode
|
||||
refname*, header*: string
|
||||
|
||||
MetaEnum* = enum
|
||||
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
||||
|
||||
|
|
@ -74,7 +70,7 @@ type
|
|||
config*: StringTableRef
|
||||
splitAfter*: int # split too long entries in the TOC
|
||||
listingCounter*: int
|
||||
tocPart*: seq[TocEntry]
|
||||
tocPart*: seq[PRstNode] # headings for Table of Contents
|
||||
hasToc*: bool
|
||||
theIndex: string # Contents of the index file to be dumped at the end.
|
||||
findFile*: FindFileHandler
|
||||
|
|
@ -120,7 +116,8 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
|||
config: StringTableRef, filename: string,
|
||||
findFile: FindFileHandler = nil,
|
||||
msgHandler: MsgHandler = nil,
|
||||
filenames = default(RstFileTable)) =
|
||||
filenames = default(RstFileTable),
|
||||
hasToc = false) =
|
||||
## Initializes a ``RstGenerator``.
|
||||
##
|
||||
## You need to call this before using a ``RstGenerator`` with any other
|
||||
|
|
@ -165,6 +162,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
|||
g.config = config
|
||||
g.target = target
|
||||
g.tocPart = @[]
|
||||
g.hasToc = hasToc
|
||||
g.filename = filename
|
||||
g.filenames = filenames
|
||||
g.splitAfter = 20
|
||||
|
|
@ -773,31 +771,18 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
|||
var tmp = ""
|
||||
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp)
|
||||
d.currentSection = tmp
|
||||
# Find the last higher level section for unique reference name
|
||||
var sectionPrefix = ""
|
||||
for i in countdown(d.tocPart.high, 0):
|
||||
let n2 = d.tocPart[i].n
|
||||
if n2.level < n.level:
|
||||
sectionPrefix = rstnodeToRefname(n2) & "-"
|
||||
break
|
||||
var refname = sectionPrefix & rstnodeToRefname(n)
|
||||
var tocName = esc(d.target, renderRstToText(n), escMode = emOption)
|
||||
# for Latex: simple text without commands that may break TOC/hyperref
|
||||
if d.hasToc:
|
||||
var length = len(d.tocPart)
|
||||
setLen(d.tocPart, length + 1)
|
||||
d.tocPart[length].refname = refname
|
||||
d.tocPart[length].n = n
|
||||
d.tocPart[length].header = tmp
|
||||
|
||||
d.tocPart.add n
|
||||
dispA(d.target, result, "\n<h$1><a class=\"toc-backref\"" &
|
||||
"$2 href=\"#$5\">$3</a></h$1>", "\\rsth$4[$6]{$3}$2\n",
|
||||
[$n.level, refname.idS, tmp,
|
||||
$chr(n.level - 1 + ord('A')), refname, tocName])
|
||||
[$n.level, n.anchor.idS, tmp,
|
||||
$chr(n.level - 1 + ord('A')), n.anchor, tocName])
|
||||
else:
|
||||
dispA(d.target, result, "\n<h$1$2>$3</h$1>",
|
||||
"\\rsth$4[$5]{$3}$2\n", [
|
||||
$n.level, refname.idS, tmp,
|
||||
$n.level, n.anchor.idS, tmp,
|
||||
$chr(n.level - 1 + ord('A')), tocName])
|
||||
|
||||
# Generate index entry using spaces to indicate TOC level for the output HTML.
|
||||
|
|
@ -810,7 +795,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
|||
# outDir = /foo -\
|
||||
# destFile = /foo/bar/zoo.html -|-> bar/zoo.html
|
||||
d.destFile.relativePath(d.outDir, '/')
|
||||
setIndexTerm(d, htmlFileRelPath, refname, tmp.stripTocHtml,
|
||||
setIndexTerm(d, htmlFileRelPath, n.anchor, tmp.stripTocHtml,
|
||||
spaces(max(0, n.level)) & tmp)
|
||||
|
||||
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||
|
|
@ -829,18 +814,20 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
|||
var tocName = esc(d.target, renderRstToText(n), escMode=emOption)
|
||||
dispA(d.target, result, "<h$1$2><center>$3</center></h$1>",
|
||||
"\\rstov$4[$5]{$3}$2\n", [$n.level,
|
||||
rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A')), tocName])
|
||||
n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName])
|
||||
|
||||
proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
|
||||
proc renderTocEntry(d: PDoc, n: PRstNode, result: var string) =
|
||||
var header = ""
|
||||
for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], header)
|
||||
dispA(d.target, result,
|
||||
"<li><a class=\"reference\" id=\"$1_toc\" href=\"#$1\">$2</a></li>\n",
|
||||
"\\item\\label{$1_toc} $2\\ref{$1}\n", [e.refname, e.header])
|
||||
"\\item\\label{$1_toc} $2\\ref{$1}\n", [n.anchor, header])
|
||||
|
||||
proc renderTocEntries*(d: var RstGenerator, j: var int, lvl: int,
|
||||
result: var string) =
|
||||
var tmp = ""
|
||||
while j <= high(d.tocPart):
|
||||
var a = abs(d.tocPart[j].n.level)
|
||||
var a = abs(d.tocPart[j].level)
|
||||
if a == lvl:
|
||||
renderTocEntry(d, d.tocPart[j], tmp)
|
||||
inc(j)
|
||||
|
|
@ -1630,11 +1617,12 @@ proc rstToHtml*(s: string, options: RstParseOptions,
|
|||
result = ""
|
||||
|
||||
const filen = "input"
|
||||
let (rst, filenames, _) = rstParse(s, filen,
|
||||
let (rst, filenames, t) = rstParse(s, filen,
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
options, myFindFile, msgHandler)
|
||||
var d: RstGenerator
|
||||
initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, filenames)
|
||||
initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler,
|
||||
filenames, hasToc = t)
|
||||
result = ""
|
||||
renderRstToOut(d, rst, result)
|
||||
strbasics.strip(result)
|
||||
|
|
@ -1644,10 +1632,11 @@ proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline,
|
|||
## Convenience proc for `renderRstToOut` and `initRstGenerator`.
|
||||
runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}"""
|
||||
if rstSource.len == 0: return
|
||||
let (rst, filenames, _) = rstParse(rstSource, "",
|
||||
let (rst, filenames, t) = rstParse(rstSource, "",
|
||||
line=LineRstInit, column=ColRstInit,
|
||||
options)
|
||||
var rstGenera: RstGenerator
|
||||
rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", filenames=filenames)
|
||||
rstGenera.initRstGenerator(outLatex, defaultConfig(), "input",
|
||||
filenames=filenames, hasToc = t)
|
||||
rstGenera.renderRstToOut(rst, result)
|
||||
strbasics.strip(result)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue