Implement Markdown definition lists (+ migration) (#20333)

Implements definition lists Markdown extension adopted in a few
implementations including:
* [Pandoc](
  https://pandoc.org/MANUAL.html#definition-lists)
* [kramdown](
  https://kramdown.gettalong.org/quickref.html#definition-lists)
* [PHP extra Markdown](
  https://michelf.ca/projects/php-markdown/extra/#def-list)

Also affected files have been migrated.
RST definition lists are turned off for Markdown: this solves the
problem of broken formatting mentioned in
https://github.com/nim-lang/Nim/pull/20292.
This commit is contained in:
Andrey Makarov 2022-09-11 20:52:43 +03:00 • committed by GitHub
commit 088487f652
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 266 additions and 97 deletions

View file

@ -77,24 +77,29 @@ proc isPartOf*(a, b: PNode): TAnalysisResult =
## cases: ## cases:
## ##
## YES-cases: ## YES-cases:
## ```
## x <| x # for general trees ## x <| x # for general trees
## x[] <| x ## x[] <| x
## x[i] <| x ## x[i] <| x
## x.f <| x ## x.f <| x
## ```
## ##
## NO-cases: ## NO-cases:
## ```
## x !<| y # depending on type and symbol kind ## x !<| y # depending on type and symbol kind
## x[constA] !<| x[constB] ## x[constA] !<| x[constB]
## x.f !<| x.g ## x.f !<| x.g
## x.f !<| y.f iff x !<= y ## x.f !<| y.f iff x !<= y
## ```
## ##
## MAYBE-cases: ## MAYBE-cases:
## ##
## ```
## x[] ?<| y[] iff compatible type ## x[] ?<| y[] iff compatible type
## ##
## ##
## x[] ?<| y depending on type ## x[] ?<| y depending on type
## ## ```
if a.kind == b.kind: if a.kind == b.kind:
case a.kind case a.kind
of nkSym: of nkSym:

View file

@ -9,7 +9,7 @@ specialpaths is simpler because it doesn't need variables to be relocatable at
runtime (eg for use in testament) runtime (eg for use in testament)
interpolation variables: interpolation variables:
$nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary) : $nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary)
in compiler, it's obtainable via getPrefixDir(); for other tools (eg koch), in compiler, it's obtainable via getPrefixDir(); for other tools (eg koch),
this could be getCurrentDir() or getAppFilename().parentDir.parentDir, this could be getCurrentDir() or getAppFilename().parentDir.parentDir,
depending on use case depending on use case

View file

@ -72,10 +72,10 @@ The replace filter replaces substrings in each line.
Parameters and their defaults: Parameters and their defaults:
* `sub: string = ""` * `sub: string = ""`
the substring that is searched for : the substring that is searched for
* `by: string = ""` * `by: string = ""`
the string the substring is replaced with : the string the substring is replaced with
Strip filter Strip filter
@ -87,14 +87,14 @@ each line.
Parameters and their defaults: Parameters and their defaults:
* `startswith: string = ""` * `startswith: string = ""`
strip only the lines that start with *startswith* (ignoring leading : strip only the lines that start with *startswith* (ignoring leading
whitespace). If empty every line is stripped. whitespace). If empty every line is stripped.
* `leading: bool = true` * `leading: bool = true`
strip leading whitespace : strip leading whitespace
* `trailing: bool = true` * `trailing: bool = true`
strip trailing whitespace : strip trailing whitespace
StdTmpl filter StdTmpl filter
@ -109,19 +109,19 @@ statements need `end X` delimiters.
Parameters and their defaults: Parameters and their defaults:
* `metaChar: char = '#'` * `metaChar: char = '#'`
prefix for a line that contains Nim code : prefix for a line that contains Nim code
* `subsChar: char = '$'` * `subsChar: char = '$'`
prefix for a Nim expression within a template line : prefix for a Nim expression within a template line
* `conc: string = " & "` * `conc: string = " & "`
the operation for concatenation : the operation for concatenation
* `emit: string = "result.add"` * `emit: string = "result.add"`
the operation to emit a string literal : the operation to emit a string literal
* `toString: string = "$"` * `toString: string = "$"`
the operation that is applied to each expression : the operation that is applied to each expression
Example:: Example::

View file

@ -45,30 +45,30 @@ Or::
nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL <switches> proj.nim
`proj.nim` `proj.nim`
This is the main *project* filename. Most of the time you will : This is the main *project* filename. Most of the time you will
pass in the same as **FILE**, but for bigger projects this is pass in the same as **FILE**, but for bigger projects this is
the file which is used as main entry point for the program, the the file which is used as main entry point for the program, the
one which users compile to generate a final binary. one which users compile to generate a final binary.
`<switches>` `<switches>`
This would be any of the other idetools available options, like : This would be any of the other idetools available options, like
`--def` or `--suggest` explained in the following sections. `--def` or `--suggest` explained in the following sections.
`COL` `COL`
An integer with the column you are going to query. For the : An integer with the column you are going to query. For the
compiler columns start at zero, so the first column will be compiler columns start at zero, so the first column will be
**0** and the last in an 80 column terminal will be **79**. **0** and the last in an 80 column terminal will be **79**.
`LINE` `LINE`
An integer with the line you are going to query. For the compiler : An integer with the line you are going to query. For the compiler
lines start at **1**. lines start at **1**.
`FILE` `FILE`
The file you want to perform the query on. Usually you will : The file you want to perform the query on. Usually you will
pass in the same value as **proj.nim**. pass in the same value as **proj.nim**.
`DIRTY_FILE` `DIRTY_FILE`
The **FILE** parameter is enough for static analysis, but IDEs : The **FILE** parameter is enough for static analysis, but IDEs
tend to have *unsaved buffers* where the user may still be in tend to have *unsaved buffers* where the user may still be in
the middle of typing a line. In such situations the IDE can the middle of typing a line. In such situations the IDE can
save the current contents to a temporary file and then use the save the current contents to a temporary file and then use the

View file

@ -398,11 +398,11 @@ Runtime type information
programming language: programming language:
Garbage collection Garbage collection
The old GCs use the RTTI for traversing arbitrary Nim types, but usually : The old GCs use the RTTI for traversing arbitrary Nim types, but usually
only the `marker` field which contains a proc that does the traversal. only the `marker` field which contains a proc that does the traversal.
Complex assignments Complex assignments
Sequences and strings are implemented as : Sequences and strings are implemented as
pointers to resizable buffers, but Nim requires copying for pointers to resizable buffers, but Nim requires copying for
assignments. Apart from RTTI the compiler also generates copy procedures assignments. Apart from RTTI the compiler also generates copy procedures
as a specialization. as a specialization.

View file

@ -1023,24 +1023,24 @@ Pre-defined integer types
These integer types are pre-defined: These integer types are pre-defined:
`int` `int`
the generic signed integer type; its size is platform-dependent and has the : the generic signed integer type; its size is platform-dependent and has the
same size as a pointer. This type should be used in general. An integer same size as a pointer. This type should be used in general. An integer
literal that has no type suffix is of this type if it is in the range literal that has no type suffix is of this type if it is in the range
`low(int32)..high(int32)` otherwise the literal's type is `int64`. `low(int32)..high(int32)` otherwise the literal's type is `int64`.
`int`\ XX `int`\ XX
additional signed integer types of XX bits use this naming scheme : additional signed integer types of XX bits use this naming scheme
(example: int16 is a 16-bit wide integer). (example: int16 is a 16-bit wide integer).
The current implementation supports `int8`, `int16`, `int32`, `int64`. The current implementation supports `int8`, `int16`, `int32`, `int64`.
Literals of these types have the suffix 'iXX. Literals of these types have the suffix 'iXX.
`uint` `uint`
the generic `unsigned integer`:idx: type; its size is platform-dependent and : the generic `unsigned integer`:idx: type; its size is platform-dependent and
has the same size as a pointer. An integer literal with the type has the same size as a pointer. An integer literal with the type
suffix `'u` is of this type. suffix `'u` is of this type.
`uint`\ XX `uint`\ XX
additional unsigned integer types of XX bits use this naming scheme : additional unsigned integer types of XX bits use this naming scheme
(example: uint16 is a 16-bit wide unsigned integer). (example: uint16 is a 16-bit wide unsigned integer).
The current implementation supports `uint8`, `uint16`, `uint32`, The current implementation supports `uint8`, `uint16`, `uint32`,
`uint64`. Literals of these types have the suffix 'uXX. `uint64`. Literals of these types have the suffix 'uXX.
@ -1135,12 +1135,12 @@ Pre-defined floating-point types
The following floating-point types are pre-defined: The following floating-point types are pre-defined:
`float` `float`
the generic floating-point type; its size used to be platform-dependent, : the generic floating-point type; its size used to be platform-dependent,
but now it is always mapped to `float64`. but now it is always mapped to `float64`.
This type should be used in general. This type should be used in general.
`float`\ XX `float`\ XX
an implementation may define additional floating-point types of XX bits using : an implementation may define additional floating-point types of XX bits using
this naming scheme (example: `float64` is a 64-bit wide float). The current this naming scheme (example: `float64` is a 64-bit wide float). The current
implementation supports `float32` and `float64`. Literals of these types implementation supports `float32` and `float64`. Literals of these types
have the suffix 'fXX. have the suffix 'fXX.
@ -2092,52 +2092,52 @@ that expects a proc of the calling convention `closure`.
Nim supports these `calling conventions`:idx:\: Nim supports these `calling conventions`:idx:\:
`nimcall`:idx: `nimcall`:idx:
is the default convention used for a Nim **proc**. It is the : is the default convention used for a Nim **proc**. It is the
same as `fastcall`, but only for C compilers that support `fastcall`. same as `fastcall`, but only for C compilers that support `fastcall`.
`closure`:idx: `closure`:idx:
is the default calling convention for a **procedural type** that lacks : is the default calling convention for a **procedural type** that lacks
any pragma annotations. It indicates that the procedure has a hidden any pragma annotations. It indicates that the procedure has a hidden
implicit parameter (an *environment*). Proc vars that have the calling implicit parameter (an *environment*). Proc vars that have the calling
convention `closure` take up two machine words: One for the proc pointer convention `closure` take up two machine words: One for the proc pointer
and another one for the pointer to implicitly passed environment. and another one for the pointer to implicitly passed environment.
`stdcall`:idx: `stdcall`:idx:
This is the stdcall convention as specified by Microsoft. The generated C : This is the stdcall convention as specified by Microsoft. The generated C
procedure is declared with the `__stdcall` keyword. procedure is declared with the `__stdcall` keyword.
`cdecl`:idx: `cdecl`:idx:
The cdecl convention means that a procedure shall use the same convention : The cdecl convention means that a procedure shall use the same convention
as the C compiler. Under Windows the generated C procedure is declared with as the C compiler. Under Windows the generated C procedure is declared with
the `__cdecl` keyword. the `__cdecl` keyword.
`safecall`:idx: `safecall`:idx:
This is the safecall convention as specified by Microsoft. The generated C : This is the safecall convention as specified by Microsoft. The generated C
procedure is declared with the `__safecall` keyword. The word *safe* procedure is declared with the `__safecall` keyword. The word *safe*
refers to the fact that all hardware registers shall be pushed to the refers to the fact that all hardware registers shall be pushed to the
hardware stack. hardware stack.
`inline`:idx: `inline`:idx:
The inline convention means the caller should not call the procedure, : The inline convention means the caller should not call the procedure,
but inline its code directly. Note that Nim does not inline, but leaves but inline its code directly. Note that Nim does not inline, but leaves
this to the C compiler; it generates `__inline` procedures. This is this to the C compiler; it generates `__inline` procedures. This is
only a hint for the compiler: it may completely ignore it, and only a hint for the compiler: it may completely ignore it, and
it may inline procedures that are not marked as `inline`. it may inline procedures that are not marked as `inline`.
`fastcall`:idx: `fastcall`:idx:
Fastcall means different things to different C compilers. One gets whatever : Fastcall means different things to different C compilers. One gets whatever
the C `__fastcall` means. the C `__fastcall` means.
`thiscall`:idx: `thiscall`:idx:
This is the thiscall calling convention as specified by Microsoft, used on : This is the thiscall calling convention as specified by Microsoft, used on
C++ class member functions on the x86 architecture. C++ class member functions on the x86 architecture.
`syscall`:idx: `syscall`:idx:
The syscall convention is the same as `__syscall`:c: in C. It is used for : The syscall convention is the same as `__syscall`:c: in C. It is used for
interrupts. interrupts.
`noconv`:idx: `noconv`:idx:
The generated C code will not have any explicit calling convention and thus : The generated C code will not have any explicit calling convention and thus
use the C compiler's default calling convention. This is needed because use the C compiler's default calling convention. This is needed because
Nim's default calling convention for procedures is `fastcall` to Nim's default calling convention for procedures is `fastcall` to
improve speed. improve speed.

View file

@ -63,10 +63,10 @@ a location. A query location consists of:
``file.nim`` ``file.nim``
This is the name of the module or include file the query refers to. : This is the name of the module or include file the query refers to.
``dirtyfile.nim`` ``dirtyfile.nim``
This is optional. : This is optional.
The `file` parameter is enough for static analysis, but IDEs The `file` parameter is enough for static analysis, but IDEs
tend to have *unsaved buffers* where the user may still be in tend to have *unsaved buffers* where the user may still be in
@ -77,11 +77,11 @@ a location. A query location consists of:
``line`` ``line``
An integer with the line you are going to query. For the compiler : An integer with the line you are going to query. For the compiler
lines start at **1**. lines start at **1**.
``col`` ``col``
An integer with the column you are going to query. For the : An integer with the column you are going to query. For the
compiler columns start at **0**. compiler columns start at **0**.

View file

@ -75,16 +75,16 @@ type
## comment".` ## comment".`
## ##
## `pattern: string` ## `pattern: string`
## the string that was used to create the pattern. For details on how ## : the string that was used to create the pattern. For details on how
## to write a pattern, please see `the official PCRE pattern ## to write a pattern, please see `the official PCRE pattern
## documentation. ## documentation.
## <https://www.pcre.org/original/doc/html/pcrepattern.html>`_ ## <https://www.pcre.org/original/doc/html/pcrepattern.html>`_
## ##
## `captureCount: int` ## `captureCount: int`
## the number of captures that the pattern has. ## : the number of captures that the pattern has.
## ##
## `captureNameId: Table[string, int]` ## `captureNameId: Table[string, int]`
## a table from the capture names to their numeric id. ## : a table from the capture names to their numeric id.
## ##
## ##
## Options ## Options
@ -151,36 +151,36 @@ type
## execution. On failure, it is none, on success, it is some. ## execution. On failure, it is none, on success, it is some.
## ##
## `pattern: Regex` ## `pattern: Regex`
## the pattern that is being matched ## : the pattern that is being matched
## ##
## `str: string` ## `str: string`
## the string that was matched against ## : the string that was matched against
## ##
## `captures[]: string` ## `captures[]: string`
## the string value of whatever was captured at that id. If the value ## : the string value of whatever was captured at that id. If the value
## is invalid, then behavior is undefined. If the id is `-1`, then ## is invalid, then behavior is undefined. If the id is `-1`, then
## the whole match is returned. If the given capture was not matched, ## the whole match is returned. If the given capture was not matched,
## `nil` is returned. See examples for `match`. ## `nil` is returned. See examples for `match`.
## ##
## `captureBounds[]: HSlice[int, int]` ## `captureBounds[]: HSlice[int, int]`
## gets the bounds of the given capture according to the same rules as ## : gets the bounds of the given capture according to the same rules as
## the above. If the capture is not filled, then `None` is returned. ## the above. If the capture is not filled, then `None` is returned.
## The bounds are both inclusive. See examples for `match`. ## The bounds are both inclusive. See examples for `match`.
## ##
## `match: string` ## `match: string`
## the full text of the match. ## : the full text of the match.
## ##
## `matchBounds: HSlice[int, int]` ## `matchBounds: HSlice[int, int]`
## the bounds of the match, as in `captureBounds[]` ## : the bounds of the match, as in `captureBounds[]`
## ##
## `(captureBounds|captures).toTable` ## `(captureBounds|captures).toTable`
## returns a table with each named capture as a key. ## : returns a table with each named capture as a key.
## ##
## `(captureBounds|captures).toSeq` ## `(captureBounds|captures).toSeq`
## returns all the captures by their number. ## : returns all the captures by their number.
## ##
## `$: string` ## `$: string`
## same as `match` ## : same as `match`
pattern*: Regex ## The regex doing the matching. pattern*: Regex ## The regex doing the matching.
## Not nil. ## Not nil.
str*: string ## The string that was matched against. str*: string ## The string that was matched against.
@ -583,11 +583,11 @@ proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[Re
## positions. ## positions.
## ##
## `start` ## `start`
## The start point at which to start matching. `|abc` is `0`; ## : The start point at which to start matching. `|abc` is `0`;
## `a|bc` is `1` ## `a|bc` is `1`
## ##
## `endpos` ## `endpos`
## The maximum index for a match; `int.high` means the end of the ## : The maximum index for a match; `int.high` means the end of the
## string, otherwise it’s an inclusive upper bound. ## string, otherwise it’s an inclusive upper bound.
return str.matchImpl(pattern, start, endpos, 0) return str.matchImpl(pattern, start, endpos, 0)

View file

@ -2064,7 +2064,22 @@ proc getWrappableIndent(p: RstParser): int =
elif nextIndent >= currentTok(p).col: # may be a definition list [case.2] elif nextIndent >= currentTok(p).col: # may be a definition list [case.2]
result = currentTok(p).col result = currentTok(p).col
else: else:
result = nextIndent # [case.3] result = nextIndent # allow parsing next lines [case.3]
proc getMdBlockIndent(p: RstParser): int =
## Markdown version of `getWrappableIndent`.
if currentTok(p).kind == tkIndent:
result = currentTok(p).ival
else:
var nextIndent = p.tok[tokenAfterNewline(p)-1].ival
# TODO: Markdown-compliant definition should allow nextIndent == currInd(p):
if nextIndent <= currInd(p): # parse only this line
result = currentTok(p).col
else:
result = nextIndent # allow parsing next lines [case.3]
template isRst(p: RstParser): bool = roPreferMarkdown notin p.s.options
template isMd(p: RstParser): bool = roPreferMarkdown in p.s.options
proc parseField(p: var RstParser): PRstNode = proc parseField(p: var RstParser): PRstNode =
## Returns a parsed rnField node. ## Returns a parsed rnField node.
@ -2309,6 +2324,39 @@ proc isDefList(p: RstParser): bool =
p.tok[j].kind in {tkWord, tkOther, tkPunct} and p.tok[j].kind in {tkWord, tkOther, tkPunct} and
p.tok[j - 2].symbol != "::" p.tok[j - 2].symbol != "::"
proc `$`(t: Token): string = # for debugging only
result = "(" & $t.kind & " line=" & $t.line & " col=" & $t.col
if t.kind == tkIndent: result = result & " ival=" & $t.ival & ")"
else: result = result & " symbol=" & t.symbol & ")"
proc skipNewlines(p: RstParser, j: int): int =
result = j
while p.tok[result].kind != tkEof and p.tok[result].kind == tkIndent:
inc result # skip blank lines
proc skipNewlines(p: var RstParser) =
p.idx = skipNewlines(p, p.idx)
const maxMdRelInd = 3 ## In Markdown: maximum indentation that does not yet
## make the indented block a code
proc isMdRelInd(outerInd, nestedInd: int): bool =
result = outerInd <= nestedInd and nestedInd <= outerInd + maxMdRelInd
proc isMdDefBody(p: RstParser, j: int, termCol: int): bool =
let defCol = p.tok[j].col
result = p.tok[j].symbol == ":" and
isMdRelInd(termCol, defCol) and
p.tok[j+1].kind == tkWhite and
p.tok[j+2].kind in {tkWord, tkOther, tkPunct}
proc isMdDefListItem(p: RstParser, idx: int): bool =
var j = tokenAfterNewline(p, idx)
j = skipNewlines(p, j)
let termCol = p.tok[j].col
result = isMdRelInd(currInd(p), termCol) and
isMdDefBody(p, j, termCol)
proc isOptionList(p: RstParser): bool = proc isOptionList(p: RstParser): bool =
result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
match(p, p.idx, "/w") or match(p, p.idx, "//w") match(p, p.idx, "/w") or match(p, p.idx, "//w")
@ -2381,8 +2429,10 @@ proc whichSection(p: RstParser): RstNodeKind =
result = rnEnumList result = rnEnumList
elif isOptionList(p): elif isOptionList(p):
result = rnOptionList result = rnOptionList
elif isDefList(p): elif isRst(p) and isDefList(p):
result = rnDefList result = rnDefList
elif isMd(p) and isMdDefListItem(p, p.idx):
result = rnMdDefList
else: else:
result = rnParagraph result = rnParagraph
of tkWord, tkOther, tkWhite: of tkWord, tkOther, tkWhite:
@ -2391,7 +2441,9 @@ proc whichSection(p: RstParser): RstNodeKind =
if isAdornmentHeadline(p, tokIdx): result = rnHeadline if isAdornmentHeadline(p, tokIdx): result = rnHeadline
else: result = rnParagraph else: result = rnParagraph
elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList
elif isDefList(p): result = rnDefList elif isRst(p) and isDefList(p): result = rnDefList
elif isMd(p) and isMdDefListItem(p, p.idx):
result = rnMdDefList
else: result = rnParagraph else: result = rnParagraph
else: result = rnLeaf else: result = rnLeaf
@ -2921,6 +2973,36 @@ proc parseOptionList(p: var RstParser): PRstNode =
if currentTok(p).kind != tkEof: dec p.idx # back to tkIndent if currentTok(p).kind != tkEof: dec p.idx # back to tkIndent
break break
proc parseMdDefinitionList(p: var RstParser): PRstNode =
## Parses (Pandoc/kramdown/PHPextra) Mardkown definition lists.
result = newRstNodeA(p, rnMdDefList)
let termCol = currentTok(p).col
while true:
var item = newRstNode(rnDefItem)
var term = newRstNode(rnDefName)
parseLine(p, term)
skipNewlines(p)
inc p.idx, 2 # skip ":" and space
item.add(term)
while true:
var def = newRstNode(rnDefBody)
let indent = getMdBlockIndent(p)
pushInd(p, indent)
parseSection(p, def)
popInd(p)
item.add(def)
let j = skipNewlines(p, p.idx)
if isMdDefBody(p, j, termCol): # parse next definition body
p.idx = j + 2 # skip ":" and space
else:
break
result.add(item)
let j = skipNewlines(p, p.idx)
if p.tok[j].col == termCol and isMdDefListItem(p, j):
p.idx = j # parse next item
else:
break
proc parseDefinitionList(p: var RstParser): PRstNode = proc parseDefinitionList(p: var RstParser): PRstNode =
result = nil result = nil
var j = tokenAfterNewline(p) - 1 var j = tokenAfterNewline(p) - 1
@ -3094,6 +3176,7 @@ proc parseSection(p: var RstParser, result: PRstNode) =
of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)") of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)")
of rnParagraph: discard of rnParagraph: discard
of rnDefList: a = parseDefinitionList(p) of rnDefList: a = parseDefinitionList(p)
of rnMdDefList: a = parseMdDefinitionList(p)
of rnFieldList: of rnFieldList:
if p.idx > 0: dec p.idx if p.idx > 0: dec p.idx
a = parseFields(p) a = parseFields(p)
@ -3120,9 +3203,6 @@ proc parseSectionWrapper(p: var RstParser): PRstNode =
while result.kind == rnInner and result.len == 1: while result.kind == rnInner and result.len == 1:
result = result.sons[0] result = result.sons[0]
proc `$`(t: Token): string =
result = $t.kind & ' ' & t.symbol
proc parseDoc(p: var RstParser): PRstNode = proc parseDoc(p: var RstParser): PRstNode =
result = parseSectionWrapper(p) result = parseSectionWrapper(p)
if currentTok(p).kind != tkEof: if currentTok(p).kind != tkEof:

View file

@ -27,7 +27,7 @@ type
rnBulletItem, # a bullet item rnBulletItem, # a bullet item
rnEnumList, # an enumerated list rnEnumList, # an enumerated list
rnEnumItem, # an enumerated item rnEnumItem, # an enumerated item
rnDefList, # a definition list rnDefList, rnMdDefList, # a definition list (RST/Markdown)
rnDefItem, # an item of a definition list consisting of ... rnDefItem, # an item of a definition list consisting of ...
rnDefName, # ... a name part ... rnDefName, # ... a name part ...
rnDefBody, # ... and a body part ... rnDefBody, # ... and a body part ...

View file

@ -1212,7 +1212,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
of rnBulletItem, rnEnumItem: of rnBulletItem, rnEnumItem:
renderAux(d, n, "<li$2>$1</li>\n", "\\item $2$1\n", result) renderAux(d, n, "<li$2>$1</li>\n", "\\item $2$1\n", result)
of rnEnumList: renderEnumList(d, n, result) of rnEnumList: renderEnumList(d, n, result)
of rnDefList: of rnDefList, rnMdDefList:
renderAux(d, n, "<dl$2 class=\"docutils\">$1</dl>\n", renderAux(d, n, "<dl$2 class=\"docutils\">$1</dl>\n",
"\\begin{description}\n$2\n$1\\end{description}\n", result) "\\begin{description}\n$2\n$1\\end{description}\n", result)
of rnDefItem: renderAux(d, n, result) of rnDefItem: renderAux(d, n, result)

View file

@ -825,10 +825,10 @@ proc rotateLeft*[T](arg: var openArray[T]; slice: HSlice[int, int];
## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`. ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`.
## ##
## `slice` ## `slice`
## The indices of the element range that should be rotated. ## : The indices of the element range that should be rotated.
## ##
## `dist` ## `dist`
## The distance in amount of elements that the data should be rotated. ## : The distance in amount of elements that the data should be rotated.
## Can be negative, can be any number. ## Can be negative, can be any number.
## ##
## **See also:** ## **See also:**
@ -876,10 +876,10 @@ proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int],
## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`. ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`.
## ##
## `slice` ## `slice`
## The indices of the element range that should be rotated. ## : The indices of the element range that should be rotated.
## ##
## `dist` ## `dist`
## The distance in amount of elements that the data should be rotated. ## : The distance in amount of elements that the data should be rotated.
## Can be negative, can be any number. ## Can be negative, can be any number.
## ##
## **See also:** ## **See also:**

View file

@ -180,15 +180,15 @@ The square brackets `[]` indicate an optional element.
The optional `align` flag can be one of the following: The optional `align` flag can be one of the following:
`<` `<`
Forces the field to be left-aligned within the available : Forces the field to be left-aligned within the available
space. (This is the default for strings.) space. (This is the default for strings.)
`>` `>`
Forces the field to be right-aligned within the available space. : Forces the field to be right-aligned within the available space.
(This is the default for numbers.) (This is the default for numbers.)
`^` `^`
Forces the field to be centered within the available space. : Forces the field to be centered within the available space.
Note that unless a minimum field width is defined, the field width Note that unless a minimum field width is defined, the field width
will always be the same size as the data to fill it, so that the alignment will always be the same size as the data to fill it, so that the alignment

View file

@ -78,19 +78,23 @@ proc floorDivPow2(x: int32; n: int32): int32 {.inline.} =
return x shr n return x shr n
## Returns floor(log_10(2^e)) ## Returns floor(log_10(2^e))
## ```c
## static inline int32_t FloorLog10Pow2(int32_t e) ## static inline int32_t FloorLog10Pow2(int32_t e)
## { ## {
## SF_ASSERT(e >= -1500); ## SF_ASSERT(e >= -1500);
## SF_ASSERT(e <= 1500); ## SF_ASSERT(e <= 1500);
## return FloorDivPow2(e * 1262611, 22); ## return FloorDivPow2(e * 1262611, 22);
## } ## }
## ```
## Returns floor(log_10(3/4 2^e)) ## Returns floor(log_10(3/4 2^e))
## ```c
## static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e) ## static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e)
## { ## {
## SF_ASSERT(e >= -1500); ## SF_ASSERT(e >= -1500);
## SF_ASSERT(e <= 1500); ## SF_ASSERT(e <= 1500);
## return FloorDivPow2(e * 1262611 - 524031, 22); ## return FloorDivPow2(e * 1262611 - 524031, 22);
## } ## }
## ```
## Returns floor(log_2(10^e)) ## Returns floor(log_2(10^e))
proc floorLog2Pow10(e: int32): int32 {.inline.} = proc floorLog2Pow10(e: int32): int32 {.inline.} =

View file

@ -423,21 +423,21 @@ suite "RST parsing":
.. Note:: deflist: .. Note:: deflist:
>> quote >> quote
continuation continuation
""".toAst == expected) """.toAst(rstOptions = preferRst) == expected)
check(dedent""" check(dedent"""
.. Note:: .. Note::
deflist: deflist:
>> quote >> quote
continuation continuation
""".toAst == expected) """.toAst(rstOptions = preferRst) == expected)
check(dedent""" check(dedent"""
.. Note:: .. Note::
deflist: deflist:
>> quote >> quote
>> continuation >> continuation
""".toAst == expected) """.toAst(rstOptions = preferRst) == expected)
# spaces are not significant between `>`: # spaces are not significant between `>`:
check(dedent""" check(dedent"""
@ -445,7 +445,7 @@ suite "RST parsing":
deflist: deflist:
> > quote > > quote
> > continuation > > continuation
""".toAst == expected) """.toAst(rstOptions = preferRst) == expected)
test "Markdown quoted blocks: de-indent handled well": test "Markdown quoted blocks: de-indent handled well":
check(dedent""" check(dedent"""
@ -616,27 +616,28 @@ suite "RST parsing":
check inputTilde.toAst == expected check inputTilde.toAst == expected
test "option list has priority over definition list": test "option list has priority over definition list":
check(dedent""" for opt in [preferMarkdown, preferRst]:
--defusages check(dedent"""
file --defusages
-o set file
""".toAst == -o set
dedent""" """.toAst(rstOptions = opt) ==
rnOptionList dedent"""
rnOptionListItem order=1 rnOptionList
rnOptionGroup rnOptionListItem order=1
rnLeaf '--' rnOptionGroup
rnLeaf 'defusages' rnLeaf '--'
rnDescription rnLeaf 'defusages'
rnInner rnDescription
rnLeaf 'file' rnInner
rnOptionListItem order=2 rnLeaf 'file'
rnOptionGroup rnOptionListItem order=2
rnLeaf '-' rnOptionGroup
rnLeaf 'o' rnLeaf '-'
rnDescription rnLeaf 'o'
rnLeaf 'set' rnDescription
""") rnLeaf 'set'
""")
test "items of 1 option list can be separated by blank lines": test "items of 1 option list can be separated by blank lines":
check(dedent""" check(dedent"""
@ -660,13 +661,13 @@ suite "RST parsing":
rnLeaf 'desc2' rnLeaf 'desc2'
""") """)
test "option list has priority over definition list": test "definition list does not gobble up the following blocks":
check(dedent""" check(dedent"""
defName defName
defBody defBody
-b desc2 -b desc2
""".toAst == """.toAst(rstOptions = preferRst) ==
dedent""" dedent"""
rnInner rnInner
rnDefList rnDefList
@ -1054,7 +1055,7 @@ suite "RST indentation":
term2 term2
Definition2 Definition2
""" """
check(input.toAst == dedent""" check(input.toAst(rstOptions = preferRst) == dedent"""
rnEnumList labelFmt=1) rnEnumList labelFmt=1)
rnEnumItem rnEnumItem
rnAdmonition adType=hint rnAdmonition adType=hint
@ -1157,6 +1158,85 @@ suite "RST indentation":
# "template..." should be parsed as a definition list attached to ":test:": # "template..." should be parsed as a definition list attached to ":test:":
check inputWrong.toAst != ast check inputWrong.toAst != ast
test "Markdown definition lists work in conjunction with bullet lists":
check(dedent"""
* some term
: the definition
Paragraph.""".toAst ==
dedent"""
rnInner
rnBulletList
rnBulletItem
rnMdDefList
rnDefItem
rnDefName
rnLeaf 'some'
rnLeaf ' '
rnLeaf 'term'
rnDefBody
rnInner
rnLeaf 'the'
rnLeaf ' '
rnLeaf 'definition'
rnParagraph
rnLeaf 'Paragraph'
rnLeaf '.'
""")
test "Markdown definition lists work with blank lines and extra paragraphs":
check(dedent"""
Term1
: Definition1
Term2 *inline markup*
: Definition2
Paragraph2
Term3
: * point1
* point2
: term3definition2
""".toAst == dedent"""
rnMdDefList
rnDefItem
rnDefName
rnLeaf 'Term1'
rnDefBody
rnInner
rnLeaf 'Definition1'
rnDefItem
rnDefName
rnLeaf 'Term2'
rnLeaf ' '
rnEmphasis
rnLeaf 'inline'
rnLeaf ' '
rnLeaf 'markup'
rnDefBody
rnParagraph
rnLeaf 'Definition2'
rnParagraph
rnLeaf 'Paragraph2'
rnDefItem
rnDefName
rnLeaf 'Term3'
rnDefBody
rnBulletList
rnBulletItem
rnInner
rnLeaf 'point1'
rnBulletItem
rnInner
rnLeaf 'point2'
rnDefBody
rnInner
rnLeaf 'term3definition2'
""")
suite "Warnings": suite "Warnings":
test "warnings for broken footnotes/links/substitutions": test "warnings for broken footnotes/links/substitutions":
let input = dedent""" let input = dedent"""