RST: improve simple tables (#19859)
* RST: improve simple tables * nim 1.0 gotchas * Still allow legacy boundaries like `----`
This commit is contained in:
parent
f7a13f62d6
commit
4341b06f65
16 changed files with 487 additions and 120 deletions
|
|
@ -232,6 +232,7 @@ template declareClosures =
|
||||||
of meExpected: k = errXExpected
|
of meExpected: k = errXExpected
|
||||||
of meGridTableNotImplemented: k = errRstGridTableNotImplemented
|
of meGridTableNotImplemented: k = errRstGridTableNotImplemented
|
||||||
of meMarkdownIllformedTable: k = errRstMarkdownIllformedTable
|
of meMarkdownIllformedTable: k = errRstMarkdownIllformedTable
|
||||||
|
of meIllformedTable: k = errRstIllformedTable
|
||||||
of meNewSectionExpected: k = errRstNewSectionExpected
|
of meNewSectionExpected: k = errRstNewSectionExpected
|
||||||
of meGeneralParseError: k = errRstGeneralParseError
|
of meGeneralParseError: k = errRstGeneralParseError
|
||||||
of meInvalidDirective: k = errRstInvalidDirectiveX
|
of meInvalidDirective: k = errRstInvalidDirectiveX
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ type
|
||||||
errXExpected,
|
errXExpected,
|
||||||
errRstGridTableNotImplemented,
|
errRstGridTableNotImplemented,
|
||||||
errRstMarkdownIllformedTable,
|
errRstMarkdownIllformedTable,
|
||||||
|
errRstIllformedTable,
|
||||||
errRstNewSectionExpected,
|
errRstNewSectionExpected,
|
||||||
errRstGeneralParseError,
|
errRstGeneralParseError,
|
||||||
errRstInvalidDirectiveX,
|
errRstInvalidDirectiveX,
|
||||||
|
|
@ -106,6 +107,7 @@ const
|
||||||
errXExpected: "'$1' expected",
|
errXExpected: "'$1' expected",
|
||||||
errRstGridTableNotImplemented: "grid table is not implemented",
|
errRstGridTableNotImplemented: "grid table is not implemented",
|
||||||
errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table",
|
errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table",
|
||||||
|
errRstIllformedTable: "Illformed table: $1",
|
||||||
errRstNewSectionExpected: "new section expected $1",
|
errRstNewSectionExpected: "new section expected $1",
|
||||||
errRstGeneralParseError: "general parse error",
|
errRstGeneralParseError: "general parse error",
|
||||||
errRstInvalidDirectiveX: "invalid directive: '$1'",
|
errRstInvalidDirectiveX: "invalid directive: '$1'",
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ been renamed to fit this scheme. The ultimate goal is that the programmer can
|
||||||
*guess* a name.
|
*guess* a name.
|
||||||
|
|
||||||
|
|
||||||
------------------- ------------ --------------------------------------
|
=================== ============ ======================================
|
||||||
English word To use Notes
|
English word To use Notes
|
||||||
------------------- ------------ --------------------------------------
|
=================== ============ ======================================
|
||||||
initialize initT `init` is used to create a
|
initialize initT `init` is used to create a
|
||||||
value type `T`
|
value type `T`
|
||||||
new newP `new` is used to create a
|
new newP `new` is used to create a
|
||||||
|
|
@ -82,4 +82,4 @@ literal lit
|
||||||
string str
|
string str
|
||||||
identifier ident
|
identifier ident
|
||||||
indentation indent
|
indentation indent
|
||||||
------------------- ------------ --------------------------------------
|
=================== ============ ======================================
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,9 @@ A leaf of the AST often corresponds to a terminal symbol in the concrete
|
||||||
syntax. Note that the default ``float`` in Nim maps to ``float64`` such that
|
syntax. Note that the default ``float`` in Nim maps to ``float64`` such that
|
||||||
the default AST for a float is ``nnkFloat64Lit`` as below.
|
the default AST for a float is ``nnkFloat64Lit`` as below.
|
||||||
|
|
||||||
----------------- ---------------------------------------------
|
================= =============================================
|
||||||
Nim expression Corresponding AST
|
Nim expression Corresponding AST
|
||||||
----------------- ---------------------------------------------
|
================= =============================================
|
||||||
``42`` ``nnkIntLit(intVal = 42)``
|
``42`` ``nnkIntLit(intVal = 42)``
|
||||||
``42'i8`` ``nnkInt8Lit(intVal = 42)``
|
``42'i8`` ``nnkInt8Lit(intVal = 42)``
|
||||||
``42'i16`` ``nnkInt16Lit(intVal = 42)``
|
``42'i16`` ``nnkInt16Lit(intVal = 42)``
|
||||||
|
|
@ -72,7 +72,7 @@ Nim expression Corresponding AST
|
||||||
``nil`` ``nnkNilLit()``
|
``nil`` ``nnkNilLit()``
|
||||||
``myIdentifier`` ``nnkIdent(strVal = "myIdentifier")``
|
``myIdentifier`` ``nnkIdent(strVal = "myIdentifier")``
|
||||||
``myIdentifier`` after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)``
|
``myIdentifier`` after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)``
|
||||||
----------------- ---------------------------------------------
|
================= =============================================
|
||||||
|
|
||||||
Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes
|
Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes
|
||||||
get transferred into ``nnkSym`` nodes.
|
get transferred into ``nnkSym`` nodes.
|
||||||
|
|
@ -1211,9 +1211,9 @@ AST:
|
||||||
In general, declaring types mirrors this syntax (i.e., ``nnkStaticTy`` for
|
In general, declaring types mirrors this syntax (i.e., ``nnkStaticTy`` for
|
||||||
``static``, etc.). Examples follow (exceptions marked by ``*``):
|
``static``, etc.). Examples follow (exceptions marked by ``*``):
|
||||||
|
|
||||||
------------- ---------------------------------------------
|
============= =============================================
|
||||||
Nim type Corresponding AST
|
Nim type Corresponding AST
|
||||||
------------- ---------------------------------------------
|
============= =============================================
|
||||||
``static`` ``nnkStaticTy``
|
``static`` ``nnkStaticTy``
|
||||||
``tuple`` ``nnkTupleTy``
|
``tuple`` ``nnkTupleTy``
|
||||||
``var`` ``nnkVarTy``
|
``var`` ``nnkVarTy``
|
||||||
|
|
@ -1226,7 +1226,7 @@ Nim type Corresponding AST
|
||||||
``proc`` ``nnkProcTy``
|
``proc`` ``nnkProcTy``
|
||||||
``iterator`` ``nnkIteratorTy``
|
``iterator`` ``nnkIteratorTy``
|
||||||
``object`` ``nnkObjectTy``
|
``object`` ``nnkObjectTy``
|
||||||
------------- ---------------------------------------------
|
============= =============================================
|
||||||
|
|
||||||
Take special care when declaring types as ``proc``. The behavior is similar
|
Take special care when declaring types as ``proc``. The behavior is similar
|
||||||
to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``.
|
to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``.
|
||||||
|
|
|
||||||
|
|
@ -363,7 +363,7 @@ contain the following `escape sequences`:idx:\ :
|
||||||
``\\`` `backslash`:idx:
|
``\\`` `backslash`:idx:
|
||||||
``\"`` `quotation mark`:idx:
|
``\"`` `quotation mark`:idx:
|
||||||
``\'`` `apostrophe`:idx:
|
``\'`` `apostrophe`:idx:
|
||||||
``\`` '0'..'9'+ `character with decimal value d`:idx:;
|
``\`` '0'..'9'+ `character with decimal value d`:idx:;
|
||||||
all decimal digits directly
|
all decimal digits directly
|
||||||
following are used for the character
|
following are used for the character
|
||||||
``\a`` `alert`:idx:
|
``\a`` `alert`:idx:
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,9 @@ The library uses a simple naming scheme that makes use of common abbreviations
|
||||||
to keep the names short but meaningful.
|
to keep the names short but meaningful.
|
||||||
|
|
||||||
|
|
||||||
------------------- ------------ --------------------------------------
|
=================== ============ ======================================
|
||||||
English word To use Notes
|
English word To use Notes
|
||||||
------------------- ------------ --------------------------------------
|
=================== ============ ======================================
|
||||||
initialize initFoo initializes a value type `Foo`
|
initialize initFoo initializes a value type `Foo`
|
||||||
new newFoo initializes a reference type `Foo`
|
new newFoo initializes a reference type `Foo`
|
||||||
via `new` or a value type `Foo`
|
via `new` or a value type `Foo`
|
||||||
|
|
@ -220,7 +220,7 @@ literal lit
|
||||||
string str
|
string str
|
||||||
identifier ident
|
identifier ident
|
||||||
indentation indent
|
indentation indent
|
||||||
------------------- ------------ --------------------------------------
|
=================== ============ ======================================
|
||||||
|
|
||||||
|
|
||||||
Coding Conventions
|
Coding Conventions
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
\usepackage{scrextend} % for the `addmargin` environment
|
\usepackage{scrextend} % for the `addmargin` environment
|
||||||
|
|
||||||
\usepackage{xcolor}
|
\usepackage[table]{xcolor}
|
||||||
\usepackage[urlbordercolor=blue,linkbordercolor=cyan,
|
\usepackage[urlbordercolor=blue,linkbordercolor=cyan,
|
||||||
pdfborderstyle={/S/U/W 1}]{hyperref}
|
pdfborderstyle={/S/U/W 1}]{hyperref}
|
||||||
\usepackage{enumitem} % for option list, enumList, and rstfootnote
|
\usepackage{enumitem} % for option list, enumList, and rstfootnote
|
||||||
|
|
@ -84,6 +84,11 @@
|
||||||
|
|
||||||
\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6}
|
\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6}
|
||||||
|
|
||||||
|
\usepackage{booktabs}
|
||||||
|
\belowrulesep=0ex
|
||||||
|
\aboverulesep=0ex
|
||||||
|
\renewcommand{\arraystretch}{1.1}
|
||||||
|
|
||||||
\newtcolorbox{rstprebox}[1][]{blanker, breakable,
|
\newtcolorbox{rstprebox}[1][]{blanker, breakable,
|
||||||
left=3mm, right=3mm, top=1mm, bottom=1mm,
|
left=3mm, right=3mm, top=1mm, bottom=1mm,
|
||||||
borderline ={0.1em}{0pt}{rstframecolor},
|
borderline ={0.1em}{0pt}{rstframecolor},
|
||||||
|
|
@ -127,6 +132,7 @@
|
||||||
\newenvironment{rstoptlist}{%
|
\newenvironment{rstoptlist}{%
|
||||||
\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}}
|
\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}}
|
||||||
|
|
||||||
|
\usepackage{multirow}
|
||||||
\usepackage{tabulary} % tables with adjustable cell width and no overflow
|
\usepackage{tabulary} % tables with adjustable cell width and no overflow
|
||||||
% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088)
|
% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088)
|
||||||
\tymin=60pt
|
\tymin=60pt
|
||||||
|
|
|
||||||
|
|
@ -49,20 +49,20 @@ contain the following key-value pairs:
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
Key description
|
Key description
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
`Name` the project's name; this needs to be a single word
|
`Name` the project's name; this needs to be a single word
|
||||||
`DisplayName` the project's long name; this can contain spaces. If
|
`DisplayName` the project's long name; this can contain spaces. If
|
||||||
not specified, this is the same as `Name`.
|
not specified, this is the same as `Name`.
|
||||||
`Version` the project's version
|
`Version` the project's version
|
||||||
`OS` the OSes to generate C code for; for example:
|
`OS` the OSes to generate C code for; for example:
|
||||||
`"windows;linux;macosx"`
|
`"windows;linux;macosx"`
|
||||||
`CPU` the CPUs to generate C code for; for example:
|
`CPU` the CPUs to generate C code for; for example:
|
||||||
`"i386;amd64;powerpc"`
|
`"i386;amd64;powerpc"`
|
||||||
`Authors` the project's authors
|
`Authors` the project's authors
|
||||||
`Description` the project's description
|
`Description` the project's description
|
||||||
`App` the application's type: "Console" or "GUI". If
|
`App` the application's type: "Console" or "GUI". If
|
||||||
"Console", niminst generates a special batch file
|
"Console", niminst generates a special batch file
|
||||||
for Windows to open up the command-line shell.
|
for Windows to open up the command-line shell.
|
||||||
`License` the filename of the application's license
|
`License` the filename of the application's license
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -149,9 +149,9 @@ Possible options are:
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
Key description
|
Key description
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
`InstallScript` boolean flag whether an installation shell script
|
`InstallScript` boolean flag whether an installation shell script
|
||||||
should be generated. Example: `InstallScript: "Yes"`
|
should be generated. Example: `InstallScript: "Yes"`
|
||||||
`UninstallScript` boolean flag whether a de-installation shell script
|
`UninstallScript` boolean flag whether a de-installation shell script
|
||||||
should be generated.
|
should be generated.
|
||||||
Example: `UninstallScript: "Yes"`
|
Example: `UninstallScript: "Yes"`
|
||||||
==================== =======================================================
|
==================== =======================================================
|
||||||
|
|
|
||||||
|
|
@ -1185,9 +1185,9 @@ subranges) are called ordinal types. Ordinal types have quite
|
||||||
a few special operations:
|
a few special operations:
|
||||||
|
|
||||||
|
|
||||||
----------------- --------------------------------------------------------
|
================= ========================================================
|
||||||
Operation Comment
|
Operation Comment
|
||||||
----------------- --------------------------------------------------------
|
================= ========================================================
|
||||||
`ord(x)` returns the integer value that is used to
|
`ord(x)` returns the integer value that is used to
|
||||||
represent `x`'s value
|
represent `x`'s value
|
||||||
`inc(x)` increments `x` by one
|
`inc(x)` increments `x` by one
|
||||||
|
|
@ -1198,7 +1198,7 @@ Operation Comment
|
||||||
`succ(x, n)` returns the `n`'th successor of `x`
|
`succ(x, n)` returns the `n`'th successor of `x`
|
||||||
`pred(x)` returns the predecessor of `x`
|
`pred(x)` returns the predecessor of `x`
|
||||||
`pred(x, n)` returns the `n`'th predecessor of `x`
|
`pred(x, n)` returns the `n`'th predecessor of `x`
|
||||||
----------------- --------------------------------------------------------
|
================= ========================================================
|
||||||
|
|
||||||
|
|
||||||
The `inc <system.html#inc,T,int>`_, `dec <system.html#dec,T,int>`_, `succ
|
The `inc <system.html#inc,T,int>`_, `dec <system.html#dec,T,int>`_, `succ
|
||||||
|
|
|
||||||
|
|
@ -255,6 +255,7 @@ type
|
||||||
meExpected = "'$1' expected",
|
meExpected = "'$1' expected",
|
||||||
meGridTableNotImplemented = "grid table is not implemented",
|
meGridTableNotImplemented = "grid table is not implemented",
|
||||||
meMarkdownIllformedTable = "illformed delimiter row of a Markdown table",
|
meMarkdownIllformedTable = "illformed delimiter row of a Markdown table",
|
||||||
|
meIllformedTable = "Illformed table: $1",
|
||||||
meNewSectionExpected = "new section expected $1",
|
meNewSectionExpected = "new section expected $1",
|
||||||
meGeneralParseError = "general parse error",
|
meGeneralParseError = "general parse error",
|
||||||
meInvalidDirective = "invalid directive: '$1'",
|
meInvalidDirective = "invalid directive: '$1'",
|
||||||
|
|
@ -2467,81 +2468,175 @@ proc parseOverline(p: var RstParser): PRstNode =
|
||||||
anchorType=headlineAnchor)
|
anchorType=headlineAnchor)
|
||||||
|
|
||||||
type
|
type
|
||||||
IntSeq = seq[int]
|
ColSpec = object
|
||||||
ColumnLimits = tuple
|
start, stop: int
|
||||||
|
RstCols = seq[ColSpec]
|
||||||
|
ColumnLimits = tuple # for Markdown
|
||||||
first, last: int
|
first, last: int
|
||||||
ColSeq = seq[ColumnLimits]
|
ColSeq = seq[ColumnLimits]
|
||||||
|
|
||||||
proc tokEnd(p: RstParser): int =
|
proc tokStart(p: RstParser, idx: int): int =
|
||||||
result = currentTok(p).col + currentTok(p).symbol.len - 1
|
result = p.tok[idx].col
|
||||||
|
|
||||||
proc getColumns(p: var RstParser, cols: var IntSeq) =
|
proc tokStart(p: RstParser): int =
|
||||||
|
result = tokStart(p, p.idx)
|
||||||
|
|
||||||
|
proc tokEnd(p: RstParser, idx: int): int =
|
||||||
|
result = p.tok[idx].col + p.tok[idx].symbol.len - 1
|
||||||
|
|
||||||
|
proc tokEnd(p: RstParser): int =
|
||||||
|
result = tokEnd(p, p.idx)
|
||||||
|
|
||||||
|
proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int =
|
||||||
|
# Fills table column specification (or separator) `cols` and returns
|
||||||
|
# the next parser index after it.
|
||||||
var L = 0
|
var L = 0
|
||||||
|
result = startIdx
|
||||||
while true:
|
while true:
|
||||||
inc L
|
inc L
|
||||||
setLen(cols, L)
|
setLen(cols, L)
|
||||||
cols[L - 1] = tokEnd(p)
|
cols[L - 1].start = tokStart(p, result)
|
||||||
assert(currentTok(p).kind == tkAdornment)
|
cols[L - 1].stop = tokEnd(p, result)
|
||||||
inc p.idx
|
assert(p.tok[result].kind == tkAdornment)
|
||||||
if currentTok(p).kind != tkWhite: break
|
inc result
|
||||||
inc p.idx
|
if p.tok[result].kind != tkWhite: break
|
||||||
if currentTok(p).kind != tkAdornment: break
|
inc result
|
||||||
if currentTok(p).kind == tkIndent: inc p.idx
|
if p.tok[result].kind != tkAdornment: break
|
||||||
# last column has no limit:
|
if p.tok[result].kind == tkIndent: inc result
|
||||||
cols[L - 1] = 32000
|
|
||||||
|
proc checkColumns(p: RstParser, cols: RstCols) =
|
||||||
|
var
|
||||||
|
i = p.idx
|
||||||
|
col = 0
|
||||||
|
if p.tok[i].symbol[0] != '=':
|
||||||
|
rstMessage(p, mwRstStyle,
|
||||||
|
"only tables with `=` columns specification are allowed")
|
||||||
|
for col in 0 ..< cols.len:
|
||||||
|
if tokEnd(p, i) != cols[col].stop:
|
||||||
|
rstMessage(p, meIllformedTable,
|
||||||
|
"end of table column #$1 should end at position $2" % [
|
||||||
|
$(col+1), $(cols[col].stop+ColRstOffset)],
|
||||||
|
p.tok[i].line, tokEnd(p, i))
|
||||||
|
inc i
|
||||||
|
if col == cols.len - 1:
|
||||||
|
if p.tok[i].kind == tkWhite:
|
||||||
|
inc i
|
||||||
|
if p.tok[i].kind notin {tkIndent, tkEof}:
|
||||||
|
rstMessage(p, meIllformedTable, "extraneous column specification")
|
||||||
|
elif p.tok[i].kind == tkWhite:
|
||||||
|
inc i
|
||||||
|
else:
|
||||||
|
rstMessage(p, meIllformedTable, "no enough table columns",
|
||||||
|
p.tok[i].line, p.tok[i].col)
|
||||||
|
|
||||||
|
proc getSpans(p: RstParser, nextLine: int,
|
||||||
|
cols: RstCols, unitedCols: RstCols): seq[int] =
|
||||||
|
## Calculates how many columns a joined cell occupies.
|
||||||
|
if unitedCols.len > 0:
|
||||||
|
result = newSeq[int](unitedCols.len)
|
||||||
|
var
|
||||||
|
iCell = 0
|
||||||
|
jCell = 0
|
||||||
|
uCell = 0
|
||||||
|
while jCell < cols.len:
|
||||||
|
if cols[jCell].stop < unitedCols[uCell].stop:
|
||||||
|
inc jCell
|
||||||
|
elif cols[jCell].stop == unitedCols[uCell].stop:
|
||||||
|
result[uCell] = jCell - iCell + 1
|
||||||
|
iCell = jCell + 1
|
||||||
|
jCell = jCell + 1
|
||||||
|
inc uCell
|
||||||
|
else:
|
||||||
|
rstMessage(p, meIllformedTable,
|
||||||
|
"spanning underline does not match main table columns",
|
||||||
|
p.tok[nextLine].line, p.tok[nextLine].col)
|
||||||
|
|
||||||
|
proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNode =
|
||||||
|
## Parses 1 row in RST simple table.
|
||||||
|
# Consider that columns may be spanning (united by using underline like ----):
|
||||||
|
let nextLine = tokenAfterNewline(p)
|
||||||
|
var unitedCols: RstCols
|
||||||
|
var afterSpan: int
|
||||||
|
if p.tok[nextLine].kind == tkAdornment and p.tok[nextLine].symbol[0] == '-':
|
||||||
|
afterSpan = getColumns(p, unitedCols, nextLine)
|
||||||
|
if unitedCols == cols and p.tok[nextLine].symbol[0] == colChar:
|
||||||
|
# legacy rst.nim compat.: allow punctuation like `----` in main boundaries
|
||||||
|
afterSpan = nextLine
|
||||||
|
unitedCols.setLen 0
|
||||||
|
else:
|
||||||
|
afterSpan = nextLine
|
||||||
|
template colEnd(i): int =
|
||||||
|
if i == cols.len - 1: high(int) # last column has no limit
|
||||||
|
elif unitedCols.len > 0: unitedCols[i].stop else: cols[i].stop
|
||||||
|
template colStart(i): int =
|
||||||
|
if unitedCols.len > 0: unitedCols[i].start else: cols[i].start
|
||||||
|
var row = newSeq[string](if unitedCols.len > 0: unitedCols.len else: cols.len)
|
||||||
|
var spans: seq[int] = getSpans(p, nextLine, cols, unitedCols)
|
||||||
|
|
||||||
|
let line = currentTok(p).line
|
||||||
|
# Iterate over the lines a single cell may span:
|
||||||
|
while true:
|
||||||
|
var nCell = 0
|
||||||
|
# distribute tokens between cells in the current line:
|
||||||
|
while currentTok(p).kind notin {tkIndent, tkEof}:
|
||||||
|
if tokEnd(p) <= colEnd(nCell):
|
||||||
|
if tokStart(p) < colStart(nCell):
|
||||||
|
if currentTok(p).kind != tkWhite:
|
||||||
|
rstMessage(p, meIllformedTable,
|
||||||
|
"this word crosses table column from the left")
|
||||||
|
else:
|
||||||
|
inc p.idx
|
||||||
|
else:
|
||||||
|
row[nCell].add(currentTok(p).symbol)
|
||||||
|
inc p.idx
|
||||||
|
else:
|
||||||
|
if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite:
|
||||||
|
rstMessage(p, meIllformedTable,
|
||||||
|
"this word crosses table column from the right")
|
||||||
|
inc nCell
|
||||||
|
if currentTok(p).kind == tkIndent: inc p.idx
|
||||||
|
if tokEnd(p) <= colEnd(0): break
|
||||||
|
# Continued current cells because the 1st column is empty.
|
||||||
|
if currentTok(p).kind in {tkEof, tkAdornment}:
|
||||||
|
break
|
||||||
|
for nCell in countup(1, high(row)): row[nCell].add('\n')
|
||||||
|
result = newRstNode(rnTableRow)
|
||||||
|
var q: RstParser
|
||||||
|
for uCell in 0 ..< row.len:
|
||||||
|
initParser(q, p.s)
|
||||||
|
q.col = colStart(uCell)
|
||||||
|
q.line = line - 1
|
||||||
|
getTokens(row[uCell], q.tok)
|
||||||
|
let cell = newRstNode(rnTableDataCell)
|
||||||
|
cell.span = if spans.len == 0: 0 else: spans[uCell]
|
||||||
|
cell.add(parseDoc(q))
|
||||||
|
result.add(cell)
|
||||||
|
if afterSpan > p.idx:
|
||||||
|
p.idx = afterSpan
|
||||||
|
|
||||||
proc parseSimpleTable(p: var RstParser): PRstNode =
|
proc parseSimpleTable(p: var RstParser): PRstNode =
|
||||||
var
|
var cols: RstCols
|
||||||
cols: IntSeq
|
|
||||||
row: seq[string]
|
|
||||||
i, last, line: int
|
|
||||||
c: char
|
|
||||||
q: RstParser
|
|
||||||
a, b: PRstNode
|
|
||||||
result = newRstNodeA(p, rnTable)
|
result = newRstNodeA(p, rnTable)
|
||||||
cols = @[]
|
let startIdx = getColumns(p, cols, p.idx)
|
||||||
row = @[]
|
let colChar = currentTok(p).symbol[0]
|
||||||
a = nil
|
checkColumns(p, cols)
|
||||||
c = currentTok(p).symbol[0]
|
p.idx = startIdx
|
||||||
|
result.colCount = cols.len
|
||||||
while true:
|
while true:
|
||||||
if currentTok(p).kind == tkAdornment:
|
if currentTok(p).kind == tkAdornment:
|
||||||
last = tokenAfterNewline(p)
|
checkColumns(p, cols)
|
||||||
if p.tok[last].kind in {tkEof, tkIndent}:
|
p.idx = tokenAfterNewline(p)
|
||||||
|
if currentTok(p).kind in {tkEof, tkIndent}:
|
||||||
# skip last adornment line:
|
# skip last adornment line:
|
||||||
p.idx = last
|
|
||||||
break
|
break
|
||||||
getColumns(p, cols)
|
if result.sons.len > 0: result.sons[^1].endsHeader = true
|
||||||
setLen(row, cols.len)
|
# fix rnTableDataCell -> rnTableHeaderCell for previous table rows:
|
||||||
if a != nil:
|
for nRow in 0 ..< result.sons.len:
|
||||||
for j in 0 ..< a.len: # fix rnTableDataCell -> rnTableHeaderCell
|
for nCell in 0 ..< result.sons[nRow].len:
|
||||||
a.sons[j] = newRstNode(rnTableHeaderCell, a.sons[j].sons)
|
result.sons[nRow].sons[nCell].kind = rnTableHeaderCell
|
||||||
if currentTok(p).kind == tkEof: break
|
if currentTok(p).kind == tkEof: break
|
||||||
for j in countup(0, high(row)): row[j] = ""
|
let tabRow = parseSimpleTableRow(p, cols, colChar)
|
||||||
# the following while loop iterates over the lines a single cell may span:
|
result.add tabRow
|
||||||
line = currentTok(p).line
|
|
||||||
while true:
|
|
||||||
i = 0
|
|
||||||
while currentTok(p).kind notin {tkIndent, tkEof}:
|
|
||||||
if tokEnd(p) <= cols[i]:
|
|
||||||
row[i].add(currentTok(p).symbol)
|
|
||||||
inc p.idx
|
|
||||||
else:
|
|
||||||
if currentTok(p).kind == tkWhite: inc p.idx
|
|
||||||
inc i
|
|
||||||
if currentTok(p).kind == tkIndent: inc p.idx
|
|
||||||
if tokEnd(p) <= cols[0]: break
|
|
||||||
if currentTok(p).kind in {tkEof, tkAdornment}: break
|
|
||||||
for j in countup(1, high(row)): row[j].add('\n')
|
|
||||||
a = newRstNode(rnTableRow)
|
|
||||||
for j in countup(0, high(row)):
|
|
||||||
initParser(q, p.s)
|
|
||||||
q.col = cols[j]
|
|
||||||
q.line = line - 1
|
|
||||||
getTokens(row[j], q.tok)
|
|
||||||
b = newRstNode(rnTableDataCell)
|
|
||||||
b.add(parseDoc(q))
|
|
||||||
a.add(b)
|
|
||||||
result.add(a)
|
|
||||||
|
|
||||||
proc readTableRow(p: var RstParser): ColSeq =
|
proc readTableRow(p: var RstParser): ColSeq =
|
||||||
if currentTok(p).symbol == "|": inc p.idx
|
if currentTok(p).symbol == "|": inc p.idx
|
||||||
|
|
@ -2574,17 +2669,16 @@ proc isValidDelimiterRow(p: var RstParser, colNum: int): bool =
|
||||||
proc parseMarkdownTable(p: var RstParser): PRstNode =
|
proc parseMarkdownTable(p: var RstParser): PRstNode =
|
||||||
var
|
var
|
||||||
row: ColSeq
|
row: ColSeq
|
||||||
colNum: int
|
|
||||||
a, b: PRstNode
|
a, b: PRstNode
|
||||||
q: RstParser
|
q: RstParser
|
||||||
result = newRstNodeA(p, rnMarkdownTable)
|
result = newRstNodeA(p, rnMarkdownTable)
|
||||||
|
|
||||||
proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) =
|
proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) =
|
||||||
row = readTableRow(p)
|
row = readTableRow(p)
|
||||||
if colNum == 0: colNum = row.len # table header
|
if result.colCount == 0: result.colCount = row.len # table header
|
||||||
elif row.len < colNum: row.setLen(colNum)
|
elif row.len < result.colCount: row.setLen(result.colCount)
|
||||||
a = newRstNode(rnTableRow)
|
a = newRstNode(rnTableRow)
|
||||||
for j in 0 ..< colNum:
|
for j in 0 ..< result.colCount:
|
||||||
b = newRstNode(cellKind)
|
b = newRstNode(cellKind)
|
||||||
initParser(q, p.s)
|
initParser(q, p.s)
|
||||||
q.col = p.col
|
q.col = p.col
|
||||||
|
|
@ -2595,7 +2689,8 @@ proc parseMarkdownTable(p: var RstParser): PRstNode =
|
||||||
result.add(a)
|
result.add(a)
|
||||||
|
|
||||||
parseRow(p, rnTableHeaderCell, result)
|
parseRow(p, rnTableHeaderCell, result)
|
||||||
if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable)
|
if not isValidDelimiterRow(p, result.colCount):
|
||||||
|
rstMessage(p, meMarkdownIllformedTable)
|
||||||
while predNL(p) and currentTok(p).symbol == "|":
|
while predNL(p) and currentTok(p).symbol == "|":
|
||||||
parseRow(p, rnTableDataCell, result)
|
parseRow(p, rnTableDataCell, result)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,12 @@ type
|
||||||
## nodes that are post-processed after parsing
|
## nodes that are post-processed after parsing
|
||||||
of rnNimdocRef:
|
of rnNimdocRef:
|
||||||
tooltip*: string
|
tooltip*: string
|
||||||
|
of rnTable, rnGridTable, rnMarkdownTable:
|
||||||
|
colCount*: int ## Number of (not-united) cells in the table
|
||||||
|
of rnTableRow:
|
||||||
|
endsHeader*: bool ## Is last row in the header of table?
|
||||||
|
of rnTableHeaderCell, rnTableDataCell:
|
||||||
|
span*: int ## Number of table columns that the cell occupies
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
anchor*: string ## anchor, internal link target
|
anchor*: string ## anchor, internal link target
|
||||||
|
|
@ -416,6 +422,13 @@ proc treeRepr*(node: PRstNode, indent=0): string =
|
||||||
result.add (if node.order == 0: "" else: " order=" & $node.order)
|
result.add (if node.order == 0: "" else: " order=" & $node.order)
|
||||||
of rnMarkdownBlockQuoteItem:
|
of rnMarkdownBlockQuoteItem:
|
||||||
result.add " quotationDepth=" & $node.quotationDepth
|
result.add " quotationDepth=" & $node.quotationDepth
|
||||||
|
of rnTable, rnGridTable, rnMarkdownTable:
|
||||||
|
result.add " colCount=" & $node.colCount
|
||||||
|
of rnTableHeaderCell, rnTableDataCell:
|
||||||
|
if node.span > 0:
|
||||||
|
result.add " span=" & $node.span
|
||||||
|
of rnTableRow:
|
||||||
|
if node.endsHeader: result.add " endsHeader"
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'")
|
result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'")
|
||||||
|
|
|
||||||
|
|
@ -1091,10 +1091,6 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) =
|
||||||
else:
|
else:
|
||||||
dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
|
dispA(d.target, result, "<div class=\"$1\">$2</div>", "$2", [arg, tmp])
|
||||||
|
|
||||||
proc texColumns(n: PRstNode): string =
|
|
||||||
let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1
|
|
||||||
result = "L".repeat(nColumns)
|
|
||||||
|
|
||||||
proc renderField(d: PDoc, n: PRstNode, result: var string) =
|
proc renderField(d: PDoc, n: PRstNode, result: var string) =
|
||||||
var b = false
|
var b = false
|
||||||
if d.target == outLatex:
|
if d.target == outLatex:
|
||||||
|
|
@ -1323,24 +1319,49 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
|
||||||
renderAux(d, n,
|
renderAux(d, n,
|
||||||
"<table$2 border=\"1\" class=\"docutils\">$1</table>",
|
"<table$2 border=\"1\" class=\"docutils\">$1</table>",
|
||||||
"\n$2\n\\begin{rsttab}{" &
|
"\n$2\n\\begin{rsttab}{" &
|
||||||
texColumns(n) & "}\n\\hline\n$1\\end{rsttab}", result)
|
"L".repeat(n.colCount) & "}\n\\toprule\n$1" &
|
||||||
|
"\\addlinespace[0.1em]\\bottomrule\n\\end{rsttab}", result)
|
||||||
of rnTableRow:
|
of rnTableRow:
|
||||||
if len(n) >= 1:
|
if len(n) >= 1:
|
||||||
if d.target == outLatex:
|
case d.target
|
||||||
#var tmp = ""
|
of outHtml:
|
||||||
renderRstToOut(d, n.sons[0], result)
|
|
||||||
for i in countup(1, len(n) - 1):
|
|
||||||
result.add(" & ")
|
|
||||||
renderRstToOut(d, n.sons[i], result)
|
|
||||||
result.add("\\\\\n\\hline\n")
|
|
||||||
else:
|
|
||||||
result.add("<tr>")
|
result.add("<tr>")
|
||||||
renderAux(d, n, result)
|
renderAux(d, n, result)
|
||||||
result.add("</tr>\n")
|
result.add("</tr>\n")
|
||||||
of rnTableDataCell:
|
of outLatex:
|
||||||
renderAux(d, n, "<td>$1</td>", "$1", result)
|
if n.sons[0].kind == rnTableHeaderCell:
|
||||||
of rnTableHeaderCell:
|
result.add "\\rowcolor{gray!15} "
|
||||||
renderAux(d, n, "<th>$1</th>", "\\textbf{$1}", result)
|
var spanLines: seq[(int, int)]
|
||||||
|
var nCell = 0
|
||||||
|
for uCell in 0 .. n.len - 1:
|
||||||
|
renderRstToOut(d, n.sons[uCell], result)
|
||||||
|
if n.sons[uCell].span > 0:
|
||||||
|
spanLines.add (nCell + 1, nCell + n.sons[uCell].span)
|
||||||
|
nCell += n.sons[uCell].span
|
||||||
|
else:
|
||||||
|
nCell += 1
|
||||||
|
if uCell != n.len - 1:
|
||||||
|
result.add(" & ")
|
||||||
|
result.add("\\\\")
|
||||||
|
if n.endsHeader: result.add("\\midrule\n")
|
||||||
|
for (start, stop) in spanLines:
|
||||||
|
result.add("\\cmidrule(lr){$1-$2}" % [$start, $stop])
|
||||||
|
result.add("\n")
|
||||||
|
of rnTableHeaderCell, rnTableDataCell:
|
||||||
|
case d.target
|
||||||
|
of outHtml:
|
||||||
|
let tag = if n.kind == rnTableHeaderCell: "th" else: "td"
|
||||||
|
var spanSpec: string
|
||||||
|
if n.span <= 1: spanSpec = ""
|
||||||
|
else:
|
||||||
|
spanSpec = " colspan=\"" & $n.span & "\" style=\"text-align: center\""
|
||||||
|
renderAux(d, n, "<$1$2>$$1</$1>" % [tag, spanSpec], "", result)
|
||||||
|
of outLatex:
|
||||||
|
let text = if n.kind == rnTableHeaderCell: "\\textbf{$1}" else: "$1"
|
||||||
|
var latexStr: string
|
||||||
|
if n.span <= 1: latexStr = text
|
||||||
|
else: latexStr = "\\multicolumn{" & $n.span & "}{c}{" & text & "}"
|
||||||
|
renderAux(d, n, "", latexStr, result)
|
||||||
of rnFootnoteGroup:
|
of rnFootnoteGroup:
|
||||||
renderAux(d, n,
|
renderAux(d, n,
|
||||||
"<hr class=\"footnote\">" &
|
"<hr class=\"footnote\">" &
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
## Nim coroutines implementation, supports several context switching methods:
|
## Nim coroutines implementation, supports several context switching methods:
|
||||||
## -------- ------------
|
## ======== ============
|
||||||
## ucontext available on unix and alike (default)
|
## ucontext available on unix and alike (default)
|
||||||
## setjmp available on unix and alike (x86/64 only)
|
## setjmp available on unix and alike (x86/64 only)
|
||||||
## fibers available and required on windows.
|
## fibers available and required on windows.
|
||||||
## -------- ------------
|
## ======== ============
|
||||||
##
|
##
|
||||||
## -d:nimCoroutines Required to build this module.
|
## -d:nimCoroutines Required to build this module.
|
||||||
## -d:nimCoroutinesUcontext Use ucontext backend.
|
## -d:nimCoroutinesUcontext Use ucontext backend.
|
||||||
|
|
|
||||||
|
|
@ -2383,21 +2383,21 @@ iterator walkDirRec*(dir: string,
|
||||||
##
|
##
|
||||||
## Walking is recursive. `followFilter` controls the behaviour of the iterator:
|
## Walking is recursive. `followFilter` controls the behaviour of the iterator:
|
||||||
##
|
##
|
||||||
## --------------------- ---------------------------------------------
|
## ===================== =============================================
|
||||||
## yieldFilter meaning
|
## yieldFilter meaning
|
||||||
## --------------------- ---------------------------------------------
|
## ===================== =============================================
|
||||||
## ``pcFile`` yield real files (default)
|
## ``pcFile`` yield real files (default)
|
||||||
## ``pcLinkToFile`` yield symbolic links to files
|
## ``pcLinkToFile`` yield symbolic links to files
|
||||||
## ``pcDir`` yield real directories
|
## ``pcDir`` yield real directories
|
||||||
## ``pcLinkToDir`` yield symbolic links to directories
|
## ``pcLinkToDir`` yield symbolic links to directories
|
||||||
## --------------------- ---------------------------------------------
|
## ===================== =============================================
|
||||||
##
|
##
|
||||||
## --------------------- ---------------------------------------------
|
## ===================== =============================================
|
||||||
## followFilter meaning
|
## followFilter meaning
|
||||||
## --------------------- ---------------------------------------------
|
## ===================== =============================================
|
||||||
## ``pcDir`` follow real directories (default)
|
## ``pcDir`` follow real directories (default)
|
||||||
## ``pcLinkToDir`` follow symbolic links to directories
|
## ``pcLinkToDir`` follow symbolic links to directories
|
||||||
## --------------------- ---------------------------------------------
|
## ===================== =============================================
|
||||||
##
|
##
|
||||||
##
|
##
|
||||||
## See also:
|
## See also:
|
||||||
|
|
|
||||||
|
|
@ -552,15 +552,15 @@ proc escape*(s: string): string =
|
||||||
##
|
##
|
||||||
## Escapes these characters:
|
## Escapes these characters:
|
||||||
##
|
##
|
||||||
## ------------ -------------------
|
## ============ ===================
|
||||||
## char is converted to
|
## char is converted to
|
||||||
## ------------ -------------------
|
## ============ ===================
|
||||||
## ``<`` ``<``
|
## ``<`` ``<``
|
||||||
## ``>`` ``>``
|
## ``>`` ``>``
|
||||||
## ``&`` ``&``
|
## ``&`` ``&``
|
||||||
## ``"`` ``"``
|
## ``"`` ``"``
|
||||||
## ``'`` ``'``
|
## ``'`` ``'``
|
||||||
## ------------ -------------------
|
## ============ ===================
|
||||||
##
|
##
|
||||||
## You can also use `addEscaped proc <#addEscaped,string,string>`_.
|
## You can also use `addEscaped proc <#addEscaped,string,string>`_.
|
||||||
result = newStringOfCap(s.len)
|
result = newStringOfCap(s.len)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ discard """
|
||||||
|
|
||||||
[Suite] RST parsing
|
[Suite] RST parsing
|
||||||
|
|
||||||
|
[Suite] RST tables
|
||||||
|
|
||||||
[Suite] RST indentation
|
[Suite] RST indentation
|
||||||
|
|
||||||
[Suite] Warnings
|
[Suite] Warnings
|
||||||
|
|
@ -618,6 +620,233 @@ suite "RST parsing":
|
||||||
rnLeaf 'code'
|
rnLeaf 'code'
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
suite "RST tables":
|
||||||
|
|
||||||
|
test "formatting in tables works":
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
========= ===
|
||||||
|
`build` `a`
|
||||||
|
========= ===
|
||||||
|
""".toAst ==
|
||||||
|
dedent"""
|
||||||
|
rnTable colCount=2
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell
|
||||||
|
rnInlineCode
|
||||||
|
rnDirArg
|
||||||
|
rnLeaf 'nim'
|
||||||
|
[nil]
|
||||||
|
rnLiteralBlock
|
||||||
|
rnLeaf 'build'
|
||||||
|
rnTableDataCell
|
||||||
|
rnInlineCode
|
||||||
|
rnDirArg
|
||||||
|
rnLeaf 'nim'
|
||||||
|
[nil]
|
||||||
|
rnLiteralBlock
|
||||||
|
rnLeaf 'a'
|
||||||
|
""")
|
||||||
|
|
||||||
|
test "tables with slightly overflowed cells cause an error (1)":
|
||||||
|
var error = new string
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
====== ======
|
||||||
|
Inputs Output
|
||||||
|
====== ======
|
||||||
|
""".toAst(error=error) == "")
|
||||||
|
check(error[] == "input(2, 2) Error: Illformed table: " &
|
||||||
|
"this word crosses table column from the right")
|
||||||
|
|
||||||
|
test "tables with slightly overflowed cells cause an error (2)":
|
||||||
|
var error = new string
|
||||||
|
check("" == dedent"""
|
||||||
|
===== ===== ======
|
||||||
|
Input Output
|
||||||
|
===== ===== ======
|
||||||
|
False False False
|
||||||
|
===== ===== ======
|
||||||
|
""".toAst(error=error))
|
||||||
|
check(error[] == "input(2, 8) Error: Illformed table: " &
|
||||||
|
"this word crosses table column from the right")
|
||||||
|
|
||||||
|
test "tables with slightly underflowed cells cause an error":
|
||||||
|
var error = new string
|
||||||
|
check("" == dedent"""
|
||||||
|
===== ===== ======
|
||||||
|
Input Output
|
||||||
|
===== ===== ======
|
||||||
|
False False False
|
||||||
|
===== ===== ======
|
||||||
|
""".toAst(error=error))
|
||||||
|
check(error[] == "input(2, 7) Error: Illformed table: " &
|
||||||
|
"this word crosses table column from the left")
|
||||||
|
|
||||||
|
test "tables with unequal underlines should be reported (1)":
|
||||||
|
var error = new string
|
||||||
|
error[] = "none"
|
||||||
|
check("" == dedent"""
|
||||||
|
===== ======
|
||||||
|
Input Output
|
||||||
|
===== ======
|
||||||
|
False False
|
||||||
|
===== =======
|
||||||
|
""".toAst(error=error))
|
||||||
|
check(error[] == "input(5, 14) Error: Illformed table: " &
|
||||||
|
"end of table column #2 should end at position 13")
|
||||||
|
|
||||||
|
test "tables with unequal underlines should be reported (2)":
|
||||||
|
var error = new string
|
||||||
|
check("" == dedent"""
|
||||||
|
===== ======
|
||||||
|
Input Output
|
||||||
|
===== =======
|
||||||
|
False False
|
||||||
|
===== ======
|
||||||
|
""".toAst(error=error))
|
||||||
|
check(error[] == "input(3, 14) Error: Illformed table: " &
|
||||||
|
"end of table column #2 should end at position 13")
|
||||||
|
|
||||||
|
test "tables with empty first cells":
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
= = =
|
||||||
|
x y z
|
||||||
|
t
|
||||||
|
= = =
|
||||||
|
""".toAst ==
|
||||||
|
dedent"""
|
||||||
|
rnTable colCount=3
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'x'
|
||||||
|
rnTableDataCell
|
||||||
|
rnInner
|
||||||
|
rnLeaf 'y'
|
||||||
|
rnLeaf ' '
|
||||||
|
rnTableDataCell
|
||||||
|
rnInner
|
||||||
|
rnLeaf 'z'
|
||||||
|
rnLeaf ' '
|
||||||
|
rnLeaf 't'
|
||||||
|
""")
|
||||||
|
|
||||||
|
test "tables with spanning cells & separators":
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
===== ===== ======
|
||||||
|
Inputs Output
|
||||||
|
------------ ------
|
||||||
|
A B A or B
|
||||||
|
===== ===== ======
|
||||||
|
False False False
|
||||||
|
True False True
|
||||||
|
----- ----- ------
|
||||||
|
False True True
|
||||||
|
True True True
|
||||||
|
===== ===== ======
|
||||||
|
""".toAst ==
|
||||||
|
dedent"""
|
||||||
|
rnTable colCount=3
|
||||||
|
rnTableRow
|
||||||
|
rnTableHeaderCell span=2
|
||||||
|
rnLeaf 'Inputs'
|
||||||
|
rnTableHeaderCell span=1
|
||||||
|
rnLeaf 'Output'
|
||||||
|
rnTableRow endsHeader
|
||||||
|
rnTableHeaderCell
|
||||||
|
rnLeaf 'A'
|
||||||
|
rnTableHeaderCell
|
||||||
|
rnLeaf 'B'
|
||||||
|
rnTableHeaderCell
|
||||||
|
rnInner
|
||||||
|
rnLeaf 'A'
|
||||||
|
rnLeaf ' '
|
||||||
|
rnLeaf 'or'
|
||||||
|
rnLeaf ' '
|
||||||
|
rnLeaf 'B'
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'False'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'False'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'False'
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell span=1
|
||||||
|
rnLeaf 'True'
|
||||||
|
rnTableDataCell span=1
|
||||||
|
rnLeaf 'False'
|
||||||
|
rnTableDataCell span=1
|
||||||
|
rnLeaf 'True'
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'False'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'True'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'True'
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'True'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'True'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'True'
|
||||||
|
""")
|
||||||
|
|
||||||
|
test "tables with spanning cells with uneqal underlines cause an error":
|
||||||
|
var error = new string
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
===== ===== ======
|
||||||
|
Inputs Output
|
||||||
|
------------- ------
|
||||||
|
A B A or B
|
||||||
|
===== ===== ======
|
||||||
|
""".toAst(error=error) == "")
|
||||||
|
check(error[] == "input(3, 1) Error: Illformed table: " &
|
||||||
|
"spanning underline does not match main table columns")
|
||||||
|
|
||||||
|
let expTable = dedent"""
|
||||||
|
rnTable colCount=2
|
||||||
|
rnTableRow
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'Inputs'
|
||||||
|
rnTableDataCell
|
||||||
|
rnLeaf 'Output'
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "only tables with `=` columns specs are allowed (1)":
|
||||||
|
var warnings = new seq[string]
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
------ ------
|
||||||
|
Inputs Output
|
||||||
|
------ ------
|
||||||
|
""".toAst(warnings=warnings) ==
|
||||||
|
expTable)
|
||||||
|
check(warnings[] ==
|
||||||
|
@["input(1, 1) Warning: RST style: " &
|
||||||
|
"only tables with `=` columns specification are allowed",
|
||||||
|
"input(3, 1) Warning: RST style: " &
|
||||||
|
"only tables with `=` columns specification are allowed"])
|
||||||
|
|
||||||
|
test "only tables with `=` columns specs are allowed (2)":
|
||||||
|
var warnings = new seq[string]
|
||||||
|
check(
|
||||||
|
dedent"""
|
||||||
|
====== ======
|
||||||
|
Inputs Output
|
||||||
|
~~~~~~ ~~~~~~
|
||||||
|
""".toAst(warnings=warnings) ==
|
||||||
|
expTable)
|
||||||
|
check(warnings[] ==
|
||||||
|
@["input(3, 1) Warning: RST style: "&
|
||||||
|
"only tables with `=` columns specification are allowed"])
|
||||||
|
|
||||||
|
|
||||||
suite "RST indentation":
|
suite "RST indentation":
|
||||||
test "nested bullet lists":
|
test "nested bullet lists":
|
||||||
let input = dedent """
|
let input = dedent """
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue