mirror of
https://github.com/nim-lang/Nim.git
synced 2026-07-19 07:21:19 +00:00
docs: Latex generation improvements (#18141)
* docs: improve Latex generation * make it work on Windows + fix ] escaping * minor fixes with escapes and style
This commit is contained in:
@@ -549,7 +549,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var string,
|
||||
var msg = "Example:"
|
||||
if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
|
||||
dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
|
||||
"\n\\textbf{$1}\n", [msg])
|
||||
"\n\n\\textbf{$1}\n", [msg])
|
||||
inc d.listingCounter
|
||||
let id = $d.listingCounter
|
||||
dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""])
|
||||
|
||||
@@ -8,7 +8,8 @@ split.item.toc = "20"
|
||||
# after this number of characters
|
||||
|
||||
doc.section = """
|
||||
\rsthA{$sectionTitle}\label{$sectionID}
|
||||
\rsthA[$sectionTitle]{$sectionTitle}\label{$sectionID}
|
||||
|
||||
$content
|
||||
"""
|
||||
|
||||
@@ -17,12 +18,16 @@ doc.section.toc = ""
|
||||
|
||||
doc.item = """
|
||||
|
||||
\vspace{1em}
|
||||
\phantomsection\addcontentsline{toc}{subsection}{$uniqueName}
|
||||
|
||||
\begin{rstpre}
|
||||
\begin{rstdocitem}
|
||||
$header
|
||||
\end{rstpre}
|
||||
\end{rstdocitem}
|
||||
|
||||
\begin{addmargin}[0.05\linewidth]{0pt}
|
||||
$desc
|
||||
\end{addmargin}
|
||||
"""
|
||||
|
||||
doc.item.toc = ""
|
||||
@@ -42,46 +47,118 @@ $content
|
||||
|
||||
# $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor
|
||||
doc.listing_start = "\\begin{rstpre}\n"
|
||||
doc.listing_end = "\n\\end{rstpre}\n"
|
||||
doc.listing_end = "\n\\end{rstpre}\n\n"
|
||||
|
||||
doc.file = """
|
||||
% This file was generated by Nim.
|
||||
% Generated: $date $time UTC
|
||||
\documentclass[a4paper]{article}
|
||||
\usepackage[left=2cm,right=3cm,top=3cm,bottom=3cm]{geometry}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[T1]{fontenc}
|
||||
%
|
||||
% Compile it by: xelatex (up to 3 times to get labels generated)
|
||||
% -------
|
||||
%
|
||||
\documentclass[a4paper,11pt]{article}
|
||||
\usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry}
|
||||
|
||||
% for 2-sided printing with larger inner "binding" margin
|
||||
%\usepackage[a4paper,xetex,twoside,left=4cm,right=2cm,top=1.5cm,bottom=2cm]{geometry}
|
||||
% for e-readers with 1.77:1 aspect ratio (e.g. 1920x1080)
|
||||
%\usepackage[xetex,paperheight=27.6cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry}
|
||||
% for e-readers with 1.45:1 aspect ratio (e.g. 1200x825)
|
||||
%\usepackage[xetex,paperheight=22.5cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry}
|
||||
% for e-readers with 1.33:1 aspect ratio (e.g. 1872x1404)
|
||||
%\usepackage[xetex,paperheight=20.7cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry}
|
||||
|
||||
\usepackage{fontspec}
|
||||
% logic to select default font with some fall-back fonts.
|
||||
\IfFontExistsTF{Times New Roman}{%
|
||||
\setmainfont{Times New Roman} % the default font
|
||||
\typeout{========================================= nim: using Times New Roman}
|
||||
}{
|
||||
\IfFontExistsTF{FreeSerif}{%
|
||||
\setmainfont{FreeSerif} % fallback #1 - official GNU font, resembles Times
|
||||
\typeout{========================================= nim: using FreeSerif}
|
||||
}{
|
||||
\IfFontExistsTF{DejaVuSerif}{%
|
||||
\setmainfont{DejaVuSerif} % fallback #2 - very widespread free font
|
||||
\typeout{========================================= nim: using DejaVuSerif}
|
||||
}{
|
||||
\typeout{!!!!!!!!!!!!!!!!!!! Fonts not found !!!!!!!!!!!!!!!!!!!!!!!}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
% default monospace font for code:
|
||||
\usepackage{GoMono}
|
||||
\usepackage{relsize}
|
||||
% make this monospace font 2 steps smaller to hold 80-character line
|
||||
\newcommand{\rstverbblockfont}{\smaller[2]}
|
||||
\newcommand{\rstverbinlinefont}{\smaller}
|
||||
|
||||
\usepackage{parskip} % paragraphs delimited by vertical space, no indent
|
||||
\usepackage{graphicx}
|
||||
\usepackage{lmodern}
|
||||
\usepackage{fancyvrb, courier}
|
||||
\usepackage{tabularx}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{enumitem} % for option list, enumList, and rstfootnote
|
||||
|
||||
\usepackage{dingbat} % for \carriagereturn, etc
|
||||
\usepackage{fvextra} % for code blocks (works better than original fancyvrb)
|
||||
\fvset{
|
||||
breaklines,
|
||||
breakafter={=}:|\_\{\}[](){,}.;+-*/'",
|
||||
breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}},
|
||||
breaksymbolright=\color{red}{\small\carriagereturn}
|
||||
}
|
||||
\fvinlineset{%
|
||||
breaklines,
|
||||
breakafter={=}:|\_\{\}[](){,}.;+-*/'",
|
||||
% that does not work at all when we underline inline code by ulem :-(
|
||||
commandchars=\\\{\}
|
||||
}
|
||||
|
||||
\usepackage{scrextend} % for the `addmargin` environment
|
||||
|
||||
\usepackage{xcolor}
|
||||
\usepackage[tikz]{mdframed}
|
||||
\usetikzlibrary{shadows}
|
||||
\mdfsetup{%
|
||||
linewidth=3,
|
||||
topline=false,
|
||||
rightline=false,
|
||||
bottomline=false}
|
||||
\usepackage[urlbordercolor=blue,linkbordercolor=cyan,
|
||||
pdfborderstyle={/S/U/W 1}]{hyperref}
|
||||
\usepackage{enumitem} % for option list, enumList, and rstfootnote
|
||||
|
||||
\begin{document}
|
||||
\title{$title $version $subtitle}
|
||||
\author{$author}
|
||||
\usepackage[most]{tcolorbox} % boxes around admonitions, code blocks, doc.item
|
||||
|
||||
\tolerance 1414
|
||||
\hbadness 1414
|
||||
\emergencystretch 1.5em
|
||||
\hfuzz 0.3pt
|
||||
\widowpenalty=10000
|
||||
\vfuzz \hfuzz
|
||||
\raggedbottom
|
||||
\newtcolorbox{rstadmonition}[1][]{blanker, breakable,
|
||||
left=3mm, right=3mm, top=1mm, bottom=1mm,
|
||||
before upper=\indent, parbox=false, #1}
|
||||
|
||||
\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6}
|
||||
|
||||
\newtcolorbox{rstprebox}[1][]{blanker, breakable,
|
||||
left=3mm, right=3mm, top=1mm, bottom=1mm,
|
||||
borderline ={0.1em}{0pt}{rstframecolor},
|
||||
before upper=\indent, parbox=false, #1}
|
||||
|
||||
\newenvironment{rstpre}{%
|
||||
\VerbatimEnvironment\begingroup\begin{rstprebox}%
|
||||
\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}%
|
||||
{\end{Verbatim}\end{rstprebox}\endgroup}
|
||||
|
||||
\newtcolorbox{rstdocitembox}[1][]{blanker, breakable,
|
||||
left=3mm, right=3mm, top=1mm, bottom=1mm,
|
||||
borderline ={1pt}{0pt}{cyan},
|
||||
before upper=\indent, parbox=false, #1}
|
||||
|
||||
% Inline code formatting: grey underline,
|
||||
% use \Verb from fvextras e.g. to display -- correctly as double -
|
||||
\usepackage[normalem]{ulem}
|
||||
\newcommand\rstuline{\bgroup\markoverwith{\textcolor{rstframecolor}{\rule[-0.8ex]{2pt}{1.0pt}}}\ULon}
|
||||
|
||||
\newcommand{\rstcode}[1]{%
|
||||
{\rstverbinlinefont\Verb{\rstuline{#1}}}%
|
||||
}
|
||||
|
||||
\newcommand{\rstcodeitem}[1]{\Verb{#1}}
|
||||
|
||||
\newenvironment{rstdocitem}{%
|
||||
\VerbatimEnvironment\begingroup\begin{rstdocitembox}%
|
||||
\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}%
|
||||
{\end{Verbatim}\end{rstdocitembox}\endgroup}
|
||||
|
||||
\maketitle
|
||||
|
||||
\newenvironment{rstpre}{\VerbatimEnvironment\begingroup\begin{Verbatim}[fontsize=\footnotesize , commandchars=\\\{\}]}{\end{Verbatim}\endgroup}
|
||||
\newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}}
|
||||
\ifdim\linewidth<30em
|
||||
\def\rstoptleftmargin{0.4\linewidth}
|
||||
@@ -93,37 +170,41 @@ bottomline=false}
|
||||
\newenvironment{rstoptlist}{%
|
||||
\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}}
|
||||
|
||||
% to pack tabularx into a new environment, special syntax is needed :-(
|
||||
\newenvironment{rsttab}[1]{\tabularx{\linewidth}{#1}}{\endtabularx}
|
||||
\usepackage{tabulary} % tables with adjustable cell width and no overflow
|
||||
% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088)
|
||||
\tymin=60pt
|
||||
\tymax=\maxdimen
|
||||
% to pack tabulary into a new environment, special syntax is needed :-(
|
||||
\newenvironment{rsttab}[1]{\tabulary{\linewidth}{#1}}{\endtabulary}
|
||||
|
||||
\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}}
|
||||
\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}}
|
||||
|
||||
\newcommand{\rsthA}[1]{\section{#1}}
|
||||
\newcommand{\rsthB}[1]{\subsection{#1}}
|
||||
\newcommand{\rsthC}[1]{\subsubsection{#1}}
|
||||
\newcommand{\rsthD}[1]{\paragraph{#1}}
|
||||
\newcommand{\rsthE}[1]{\paragraph{#1}}
|
||||
\newcommand{\rsthA}[2][]{\section[#1]{#2}}
|
||||
\newcommand{\rsthB}[2][]{\subsection[#1]{#2}}
|
||||
\newcommand{\rsthC}[2][]{\subsubsection[#1]{#2}}
|
||||
\newcommand{\rsthD}[2][]{\paragraph[#1]{#2}}
|
||||
\newcommand{\rsthE}[2][]{\paragraph[#1]{#2}}
|
||||
|
||||
\newcommand{\rstovA}[1]{\section*{#1}}
|
||||
\newcommand{\rstovB}[1]{\subsection*{#1}}
|
||||
\newcommand{\rstovC}[1]{\subsubsection*{#1}}
|
||||
\newcommand{\rstovD}[1]{\paragraph*{#1}}
|
||||
\newcommand{\rstovE}[1]{\paragraph*{#1}}
|
||||
\newcommand{\rstovA}[2][]{\section*[#1]{#2}}
|
||||
\newcommand{\rstovB}[2][]{\subsection*[#1]{#2}}
|
||||
\newcommand{\rstovC}[2][]{\subsubsection*[#1]{#2}}
|
||||
\newcommand{\rstovD}[2][]{\paragraph*[#1]{#2}}
|
||||
\newcommand{\rstovE}[2][]{\paragraph*[#1]{#2}}
|
||||
|
||||
% Syntax highlighting:
|
||||
\newcommand{\spanDecNumber}[1]{#1}
|
||||
\newcommand{\spanBinNumber}[1]{#1}
|
||||
\newcommand{\spanHexNumber}[1]{#1}
|
||||
\newcommand{\spanOctNumber}[1]{#1}
|
||||
\newcommand{\spanFloatNumber}[1]{#1}
|
||||
\newcommand{\spanDecNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanBinNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanHexNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanOctNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanFloatNumber}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanIdentifier}[1]{#1}
|
||||
\newcommand{\spanKeyword}[1]{\textbf{#1}}
|
||||
\newcommand{\spanStringLit}[1]{#1}
|
||||
\newcommand{\spanLongStringLit}[1]{#1}
|
||||
\newcommand{\spanStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanLongStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanCharLit}[1]{#1}
|
||||
\newcommand{\spanEscapeSequence}[1]{#1}
|
||||
\newcommand{\spanOperator}[1]{#1}
|
||||
\newcommand{\spanOperator}[1]{\textbf{#1}}
|
||||
\newcommand{\spanPunctuation}[1]{#1}
|
||||
\newcommand{\spanComment}[1]{\emph{#1}}
|
||||
\newcommand{\spanLongComment}[1]{\emph{#1}}
|
||||
@@ -132,7 +213,7 @@ bottomline=false}
|
||||
\newcommand{\spanTagEnd}[1]{#1}
|
||||
\newcommand{\spanKey}[1]{#1}
|
||||
\newcommand{\spanValue}[1]{#1}
|
||||
\newcommand{\spanRawData}[1]{#1}
|
||||
\newcommand{\spanRawData}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
\newcommand{\spanAssembler}[1]{#1}
|
||||
\newcommand{\spanPreprocessor}[1]{#1}
|
||||
\newcommand{\spanDirective}[1]{#1}
|
||||
@@ -142,11 +223,20 @@ bottomline=false}
|
||||
\newcommand{\spanLabel}[1]{#1}
|
||||
\newcommand{\spanReference}[1]{#1}
|
||||
\newcommand{\spanOther}[1]{#1}
|
||||
\newcommand{\spantok}[1]{\frame{#1}}
|
||||
\newcommand{\spantok}[1]{\fbox{#1}}
|
||||
\newcommand{\spanPrompt}[1]{\textcolor{red}{\textbf{#1}}}
|
||||
\newcommand{\spanProgramOutput}[1]{\textcolor{gray}{\textbf{#1}}}
|
||||
\newcommand{\spanProgramOutput}[1]{\textcolor{darkgray}{\textbf{#1}}}
|
||||
\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}}
|
||||
\newcommand{\spanoption}[1]{\textbf{#1}}
|
||||
\newcommand{\spanoption}[1]{\textbf{\textcolor{darkgray}{#1}}}
|
||||
|
||||
\begin{document}
|
||||
\title{$title $version $subtitle}
|
||||
\author{$author}
|
||||
|
||||
% Never allow text overflow to margin:
|
||||
\setlength\emergencystretch{\hsize}\hbadness=10000
|
||||
|
||||
\maketitle
|
||||
|
||||
$content
|
||||
\end{document}
|
||||
|
||||
@@ -349,8 +349,24 @@ proc renderRstToJson*(node: PRstNode): string =
|
||||
## }
|
||||
renderRstToJsonNode(node).pretty
|
||||
|
||||
proc renderRstToText*(node: PRstNode): string =
|
||||
## minimal text representation of markup node
|
||||
const code = {rnCodeFragment, rnInterpretedText, rnInlineLiteral, rnInlineCode}
|
||||
if node == nil:
|
||||
return ""
|
||||
case node.kind
|
||||
of rnLeaf, rnSmiley:
|
||||
result.add node.text
|
||||
else:
|
||||
if node.kind in code: result.add "`"
|
||||
for i in 0 ..< node.sons.len:
|
||||
if node.kind in {rnInlineCode, rnCodeBlock} and i == 0:
|
||||
continue # omit language specifier
|
||||
result.add renderRstToText(node.sons[i])
|
||||
if node.kind in code: result.add "`"
|
||||
|
||||
proc renderRstToStr*(node: PRstNode, indent=0): string =
|
||||
## Writes the parsed RST `node` into a compact string
|
||||
## Writes the parsed RST `node` into an AST tree with compact string
|
||||
## representation in the format (one line per every sub-node):
|
||||
## ``indent - kind - [text|level|order|adType] - anchor (if non-zero)``
|
||||
## (suitable for debugging of RST parsing).
|
||||
|
||||
@@ -60,6 +60,10 @@ type
|
||||
MetaEnum* = enum
|
||||
metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion
|
||||
|
||||
EscapeMode = enum # in Latex text inside options [] and URLs is
|
||||
# escaped slightly differently than in normal text
|
||||
emText, emOption, emUrl # emText is currently used for code also
|
||||
|
||||
RstGenerator* = object of RootObj
|
||||
target*: OutputTarget
|
||||
config*: StringTableRef
|
||||
@@ -84,6 +88,7 @@ type
|
||||
id*: int ## A counter useful for generating IDs.
|
||||
onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int;
|
||||
content: string)
|
||||
escMode*: EscapeMode
|
||||
|
||||
PDoc = var RstGenerator ## Alias to type less.
|
||||
|
||||
@@ -161,6 +166,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
||||
g.findFile = findFile
|
||||
g.currentSection = ""
|
||||
g.id = 0
|
||||
g.escMode = emText
|
||||
let fileParts = filename.splitFile
|
||||
if fileParts.ext == ".nim":
|
||||
g.currentSection = "Module " & fileParts.name
|
||||
@@ -189,28 +195,34 @@ proc addHtmlChar(dest: var string, c: char) =
|
||||
of '\"': add(dest, """)
|
||||
else: add(dest, c)
|
||||
|
||||
proc addRtfChar(dest: var string, c: char) =
|
||||
proc addTexChar(dest: var string, c: char, escMode: EscapeMode) =
|
||||
## Escapes 10 special Latex characters and sometimes ` and [, ].
|
||||
## TODO: @ is always a normal symbol (besides the header), am I wrong?
|
||||
## All escapes that need to work in text and code blocks (`emText` mode)
|
||||
## should start from \ (to be compatible with fancyvrb/fvextra).
|
||||
case c
|
||||
of '{': add(dest, "\\{")
|
||||
of '}': add(dest, "\\}")
|
||||
of '\\': add(dest, "\\\\")
|
||||
else: add(dest, c)
|
||||
|
||||
proc addTexChar(dest: var string, c: char) =
|
||||
# Escapes 10 special Latex characters. Note that [, ], and ` are not
|
||||
# considered as such. TODO: neither is @, am I wrong?
|
||||
case c
|
||||
of '_', '{', '}', '$', '&', '#', '%': add(dest, "\\" & c)
|
||||
of '_', '$', '&', '#', '%': add(dest, "\\" & c)
|
||||
# \~ and \^ have a special meaning unless they are followed by {}
|
||||
of '~', '^': add(dest, "\\" & c & "{}")
|
||||
# Latex loves to substitute ` to opening quote, even in texttt mode!
|
||||
of '`': add(dest, "\\textasciigrave{}")
|
||||
# add {} to avoid gobbling up space by \textbackslash
|
||||
of '\\': add(dest, "\\textbackslash{}")
|
||||
# Using { and } in URL in Latex: https://tex.stackexchange.com/a/469175
|
||||
of '{':
|
||||
add(dest, if escMode == emUrl: "\\%7B" else: "\\{")
|
||||
of '}':
|
||||
add(dest, if escMode == emUrl: "\\%7D" else: "\\}")
|
||||
of ']':
|
||||
# escape ] inside an optional argument in e.g. \section[static[T]]{..
|
||||
add(dest, if escMode == emOption: "\\text{]}" else: "]")
|
||||
else: add(dest, c)
|
||||
|
||||
proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} =
|
||||
proc escChar*(target: OutputTarget, dest: var string,
|
||||
c: char, escMode: EscapeMode) {.inline.} =
|
||||
case target
|
||||
of outHtml: addHtmlChar(dest, c)
|
||||
of outLatex: addTexChar(dest, c)
|
||||
of outLatex: addTexChar(dest, c, escMode)
|
||||
|
||||
proc addSplitter(target: OutputTarget; dest: var string) {.inline.} =
|
||||
case target
|
||||
@@ -229,7 +241,7 @@ proc nextSplitPoint*(s: string, start: int): int =
|
||||
inc(result)
|
||||
dec(result) # last valid index
|
||||
|
||||
proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
|
||||
proc esc*(target: OutputTarget, s: string, splitAfter = -1, escMode = emText): string =
|
||||
## Escapes the HTML.
|
||||
result = ""
|
||||
if splitAfter >= 0:
|
||||
@@ -240,11 +252,11 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1): string =
|
||||
#if (splitter != " ") or (partLen + k - j + 1 > splitAfter):
|
||||
partLen = 0
|
||||
addSplitter(target, result)
|
||||
for i in countup(j, k): escChar(target, result, s[i])
|
||||
for i in countup(j, k): escChar(target, result, s[i], escMode)
|
||||
inc(partLen, k - j + 1)
|
||||
j = k + 1
|
||||
else:
|
||||
for i in countup(0, len(s) - 1): escChar(target, result, s[i])
|
||||
for i in countup(0, len(s) - 1): escChar(target, result, s[i], escMode)
|
||||
|
||||
|
||||
proc disp(target: OutputTarget, xml, tex: string): string =
|
||||
@@ -760,6 +772,8 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||
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)
|
||||
@@ -768,13 +782,14 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||
d.tocPart[length].header = tmp
|
||||
|
||||
dispA(d.target, result, "\n<h$1><a class=\"toc-backref\"" &
|
||||
"$2 href=\"#$5\">$3</a></h$1>", "\\rsth$4{$3}$2\n",
|
||||
[$n.level, refname.idS, tmp, $chr(n.level - 1 + ord('A')), refname])
|
||||
"$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])
|
||||
else:
|
||||
dispA(d.target, result, "\n<h$1$2>$3</h$1>",
|
||||
"\\rsth$4{$3}$2\n", [
|
||||
"\\rsth$4[$5]{$3}$2\n", [
|
||||
$n.level, refname.idS, tmp,
|
||||
$chr(n.level - 1 + ord('A'))])
|
||||
$chr(n.level - 1 + ord('A')), tocName])
|
||||
|
||||
# Generate index entry using spaces to indicate TOC level for the output HTML.
|
||||
assert n.level >= 0
|
||||
@@ -802,9 +817,10 @@ proc renderOverline(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
|
||||
var tocName = esc(d.target, renderRstToText(n), escMode=emOption)
|
||||
dispA(d.target, result, "<h$1$2><center>$3</center></h$1>",
|
||||
"\\rstov$4{$3}$2\n", [$n.level,
|
||||
rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A'))])
|
||||
"\\rstov$4[$5]{$3}$2\n", [$n.level,
|
||||
rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A')), tocName])
|
||||
|
||||
|
||||
proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) =
|
||||
@@ -870,7 +886,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) =
|
||||
htmlOut = "<img$3 src=\"$1\"$2/>"
|
||||
|
||||
# support for `:target:` links for images:
|
||||
var target = esc(d.target, getFieldValue(n, "target").strip())
|
||||
var target = esc(d.target, getFieldValue(n, "target").strip(), escMode=emUrl)
|
||||
if target.len > 0:
|
||||
# `htmlOut` needs to be of the following format for link to work for images:
|
||||
# <a class="reference external" href="target"><img src=\"$1\"$2/></a>
|
||||
@@ -1031,16 +1047,16 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) =
|
||||
blockEnd = "</span></tt>"
|
||||
of outLatex:
|
||||
if n.kind == rnCodeBlock:
|
||||
blockStart = "\n\n\\begin{rstpre}" & n.anchor.idS & "\n"
|
||||
blockEnd = "\n\\end{rstpre}\n"
|
||||
blockStart = "\n\n" & n.anchor.idS & "\\begin{rstpre}\n"
|
||||
blockEnd = "\n\\end{rstpre}\n\n"
|
||||
else: # rnInlineCode
|
||||
blockStart = "\\texttt{"
|
||||
blockStart = "\\rstcode{"
|
||||
blockEnd = "}"
|
||||
dispA(d.target, result, blockStart, blockStart, [])
|
||||
if params.lang == langNone:
|
||||
if len(params.langStr) > 0:
|
||||
d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
|
||||
for letter in m.text: escChar(d.target, result, letter)
|
||||
for letter in m.text: escChar(d.target, result, letter, emText)
|
||||
else:
|
||||
renderCodeLang(result, params.lang, m.text, d.target)
|
||||
dispA(d.target, result, blockEnd, blockEnd)
|
||||
@@ -1055,9 +1071,8 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
|
||||
dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
|
||||
|
||||
proc texColumns(n: PRstNode): string =
|
||||
result = ""
|
||||
let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1
|
||||
for i in countup(1, nColumns): add(result, "|X")
|
||||
result = "L".repeat(nColumns)
|
||||
|
||||
proc renderField(d: PDoc, n: PRstNode, result: var string) =
|
||||
var b = false
|
||||
@@ -1144,19 +1159,38 @@ proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) =
|
||||
renderAux(d, n,
|
||||
htmlHead & "<span$2 class=\"" & htmlCls & "-text\"><b>" & txt &
|
||||
":</b></span>\n" & "$1</div>\n",
|
||||
"\n\n\\begin{mdframed}[linecolor=" & texColor & "]$2\n" &
|
||||
"\n\n\\begin{rstadmonition}[borderline west={0.2em}{0pt}{" &
|
||||
texColor & "}]$2\n" &
|
||||
"{" & texSz & "\\color{" & texColor & "}{\\textbf{" & txt & ":}}} " &
|
||||
"$1\n\\end{mdframed}\n",
|
||||
"$1\n\\end{rstadmonition}\n",
|
||||
result)
|
||||
|
||||
proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, external: bool) =
|
||||
var linkStr = ""
|
||||
block:
|
||||
let mode = d.escMode
|
||||
d.escMode = emUrl
|
||||
renderRstToOut(d, link, linkStr)
|
||||
d.escMode = mode
|
||||
var textStr = ""
|
||||
renderRstToOut(d, text, textStr)
|
||||
if external:
|
||||
dispA(d.target, result,
|
||||
"<a class=\"reference external\" href=\"$2\">$1</a>",
|
||||
"\\href{$2}{$1}", [textStr, linkStr])
|
||||
else:
|
||||
dispA(d.target, result,
|
||||
"<a class=\"reference internal\" href=\"#$2\">$1</a>",
|
||||
"\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [textStr, linkStr])
|
||||
|
||||
proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
if n == nil: return
|
||||
case n.kind
|
||||
of rnInner: renderAux(d, n, result)
|
||||
of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, result)
|
||||
of rnOverline: renderOverline(d, n, result)
|
||||
of rnTransition: renderAux(d, n, "<hr$2 />\n", "\\hrule$2\n", result)
|
||||
of rnParagraph: renderAux(d, n, "<p$2>$1</p>\n", "$2\n$1\n\n", result)
|
||||
of rnTransition: renderAux(d, n, "<hr$2 />\n", "\n\n\\vspace{0.6em}\\hrule$2\n", result)
|
||||
of rnParagraph: renderAux(d, n, "<p$2>$1</p>\n", "\n\n$2\n$1\n\n", result)
|
||||
of rnBulletList:
|
||||
renderAux(d, n, "<ul$2 class=\"simple\">$1</ul>\n",
|
||||
"\\begin{itemize}\n$2\n$1\\end{itemize}\n", result)
|
||||
@@ -1202,7 +1236,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
renderAux(d, n,
|
||||
"<div class=\"option-list-label\"><tt><span class=\"option\">" &
|
||||
"$1</span></tt></div>",
|
||||
"\\item[$1]", result)
|
||||
"\\item[\\rstcodeitem{\\spanoption{$1}}]", result)
|
||||
of rnDescription:
|
||||
renderAux(d, n, "<div class=\"option-list-description\">$1</div>",
|
||||
" $1\n", result)
|
||||
@@ -1210,7 +1244,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
doAssert false, "renderRstToOut"
|
||||
of rnLiteralBlock:
|
||||
renderAux(d, n, "<pre$2>$1</pre>\n",
|
||||
"\\begin{rstpre}\n$2\n$1\n\\end{rstpre}\n", result)
|
||||
"\n\n\\begin{rstpre}\n$2\n$1\n\\end{rstpre}\n\n", result)
|
||||
of rnQuotedLiteralBlock:
|
||||
doAssert false, "renderRstToOut"
|
||||
of rnLineBlock:
|
||||
@@ -1237,8 +1271,8 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
of rnTable, rnGridTable, rnMarkdownTable:
|
||||
renderAux(d, n,
|
||||
"<table$2 border=\"1\" class=\"docutils\">$1</table>",
|
||||
"\\begin{table}\n$2\n\\begin{rsttab}{" &
|
||||
texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result)
|
||||
"\n$2\n\\begin{rsttab}{" &
|
||||
texColumns(n) & "}\n\\hline\n$1\\end{rsttab}", result)
|
||||
of rnTableRow:
|
||||
if len(n) >= 1:
|
||||
if d.target == outLatex:
|
||||
@@ -1275,21 +1309,13 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
"\\item[\\textsuperscript{[$3]}]$2 $1\n",
|
||||
[body, n.anchor.idS, mark, n.anchor])
|
||||
of rnRef:
|
||||
var tmp = ""
|
||||
renderAux(d, n, tmp)
|
||||
dispA(d.target, result,
|
||||
"<a class=\"reference external\" href=\"#$2\">$1</a>",
|
||||
"$1\\ref{$2}", [tmp, rstnodeToRefname(n)])
|
||||
renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=false)
|
||||
of rnStandaloneHyperlink:
|
||||
renderAux(d, n,
|
||||
"<a class=\"reference external\" href=\"$1\">$1</a>",
|
||||
"\\href{$1}{$1}", result)
|
||||
renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true)
|
||||
of rnInternalRef:
|
||||
var tmp = ""
|
||||
renderAux(d, n.sons[0], tmp)
|
||||
dispA(d.target, result,
|
||||
"<a class=\"reference internal\" href=\"#$2\">$1</a>",
|
||||
"\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [tmp, n.sons[1].text])
|
||||
renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false)
|
||||
of rnHyperlink:
|
||||
renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=true)
|
||||
of rnFootnoteRef:
|
||||
var tmp = "["
|
||||
renderAux(d, n.sons[0], tmp)
|
||||
@@ -1299,14 +1325,6 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
"$1</a></strong></sup>",
|
||||
"\\textsuperscript{\\hyperlink{$2}{\\textbf{$1}}}",
|
||||
[tmp, n.sons[1].text])
|
||||
of rnHyperlink:
|
||||
var tmp0 = ""
|
||||
var tmp1 = ""
|
||||
renderRstToOut(d, n.sons[0], tmp0)
|
||||
renderRstToOut(d, n.sons[1], tmp1)
|
||||
dispA(d.target, result,
|
||||
"<a class=\"reference external\" href=\"$2\">$1</a>",
|
||||
"\\href{$2}{$1}", [tmp0, tmp1])
|
||||
of rnDirArg, rnRaw: renderAux(d, n, result)
|
||||
of rnRawHtml:
|
||||
if d.target != outLatex and not lastSon(n).isNil:
|
||||
@@ -1334,7 +1352,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
dispA(d.target, result,
|
||||
"<tt class=\"docutils literal\"><span class=\"pre $2\">" &
|
||||
"$1</span></tt>",
|
||||
"\\texttt{\\span$2{$1}}", [tmp0, class])
|
||||
"\\rstcode{\\span$2{$1}}", [tmp0, class])
|
||||
else: # rnUnknownRole, not necessarily code/monospace font
|
||||
dispA(d.target, result, "<span class=\"$2\">$1</span>", "\\span$2{$1}",
|
||||
[tmp0, class])
|
||||
@@ -1351,7 +1369,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
of rnInlineLiteral, rnInterpretedText:
|
||||
renderAux(d, n,
|
||||
"<tt class=\"docutils literal\"><span class=\"pre\">$1</span></tt>",
|
||||
"\\texttt{$1}", result)
|
||||
"\\rstcode{$1}", result)
|
||||
of rnInlineTarget:
|
||||
var tmp = ""
|
||||
renderAux(d, n, tmp)
|
||||
@@ -1360,7 +1378,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||
"\\label{$2}\\hypertarget{$2}{$1}",
|
||||
[tmp, rstnodeToRefname(n)])
|
||||
of rnSmiley: renderSmiley(d, n, result)
|
||||
of rnLeaf: result.add(esc(d.target, n.text))
|
||||
of rnLeaf: result.add(esc(d.target, n.text, escMode=d.escMode))
|
||||
of rnContents: d.hasToc = true
|
||||
of rnDefaultRole: discard
|
||||
of rnTitle:
|
||||
|
||||
@@ -259,7 +259,7 @@ A2 A3
|
||||
A4 A5
|
||||
==== === """
|
||||
let output1 = rstToLatex(input1, {})
|
||||
doAssert "{|X|X|}" in output1 # 2 columns
|
||||
doAssert "{LL}" in output1 # 2 columns
|
||||
doAssert count(output1, "\\\\") == 4 # 4 rows
|
||||
for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]:
|
||||
doAssert cell in output1
|
||||
@@ -274,7 +274,7 @@ A0 A1 X
|
||||
Ax Y
|
||||
==== === = """
|
||||
let output2 = rstToLatex(input2, {})
|
||||
doAssert "{|X|X|X|}" in output2 # 3 columns
|
||||
doAssert "{LLL}" in output2 # 3 columns
|
||||
doAssert count(output2, "\\\\") == 2 # 2 rows
|
||||
for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]:
|
||||
doAssert cell in output2
|
||||
|
||||
@@ -284,22 +284,22 @@ proc buildDoc(nimArgs, destPath: string) =
|
||||
|
||||
proc nim2pdf(src: string, dst: string, nimArgs: string) =
|
||||
# xxx expose as a `nim` command or in some other reusable way.
|
||||
let outDir = "build" / "pdflatextmp" # xxx factor pending https://github.com/timotheecour/Nim/issues/616
|
||||
let outDir = "build" / "xelatextmp" # xxx factor pending https://github.com/timotheecour/Nim/issues/616
|
||||
# note: this will generate temporary files in gitignored `outDir`: aux toc log out tex
|
||||
exec("$# rst2tex $# --outdir:$# $#" % [findNim().quoteShell(), nimArgs, outDir.quoteShell, src.quoteShell])
|
||||
let texFile = outDir / src.lastPathPart.changeFileExt("tex")
|
||||
for i in 0..<2: # call LaTeX twice to get cross references right:
|
||||
let pdflatexLog = outDir / "pdflatex.log"
|
||||
for i in 0..<3: # call LaTeX three times to get cross references right:
|
||||
let xelatexLog = outDir / "xelatex.log"
|
||||
# `>` should work on windows, if not, we can use `execCmdEx`
|
||||
let cmd = "pdflatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, pdflatexLog.quoteShell]
|
||||
exec(cmd) # on error, user can inspect `pdflatexLog`
|
||||
let cmd = "xelatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, xelatexLog.quoteShell]
|
||||
exec(cmd) # on error, user can inspect `xelatexLog`
|
||||
moveFile(texFile.changeFileExt("pdf"), dst)
|
||||
|
||||
proc buildPdfDoc*(nimArgs, destPath: string) =
|
||||
var pdfList: seq[string]
|
||||
createDir(destPath)
|
||||
if os.execShellCmd("pdflatex -version") != 0:
|
||||
doAssert false, "pdflatex not found" # or, raise an exception
|
||||
if os.execShellCmd("xelatex -version") != 0:
|
||||
doAssert false, "xelatex not found" # or, raise an exception
|
||||
else:
|
||||
for src in items(rstPdfList):
|
||||
let dst = destPath / src.lastPathPart.changeFileExt("pdf")
|
||||
|
||||
Reference in New Issue
Block a user