diff options
author | Andrey Makarov <ph.makarov@gmail.com> | 2021-06-01 21:47:23 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-01 20:47:23 +0200 |
commit | ba3ec7b04954a9a60adb6e1a64405f7644f799ee (patch) | |
tree | fb3a4f82dda1d12001b9a918b4546c0e386b8e78 | |
parent | c2e3dc0ed16a854981769b426e9e327663447df7 (diff) | |
download | Nim-ba3ec7b04954a9a60adb6e1a64405f7644f799ee.tar.gz |
docs: Latex generation improvements (#18141)
* docs: improve Latex generation * make it work on Windows + fix ] escaping * minor fixes with escapes and style
-rw-r--r-- | compiler/docgen.nim | 2 | ||||
-rw-r--r-- | config/nimdoc.tex.cfg | 202 | ||||
-rw-r--r-- | lib/packages/docutils/rstast.nim | 18 | ||||
-rw-r--r-- | lib/packages/docutils/rstgen.nim | 140 | ||||
-rw-r--r-- | tests/stdlib/trstgen.nim | 4 | ||||
-rw-r--r-- | tools/kochdocs.nim | 14 |
6 files changed, 252 insertions, 128 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e07db9188..18c568ba0 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -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", ""]) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index b19b25678..75382ce26 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -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} diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 81e3ba6d9..2489ce40c 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -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). diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 5ab6f28ee..e3f889283 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -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) = - 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? +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, "\\" & 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: diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 864728686..34b172935 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -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 diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 621dc643f..f25564fad 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -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") |