Implement Pandoc Markdown concise link extension (#20304)

* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
This commit is contained in:
Andrey Makarov 2022-09-04 21:52:21 +03:00 • committed by GitHub
commit cde6b2aab8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 325 additions and 152 deletions

View file

@ -4,7 +4,7 @@
Command: Command:
//compile, c compile project with default code generator (C) //compile, c compile project with default code generator (C)
//r compile to $nimcache/projname, run with [arguments] //r compile to $nimcache/projname, run with `arguments`
using backend specified by `--backend` (default: c) using backend specified by `--backend` (default: c)
//doc generate the documentation for inputfile for //doc generate the documentation for inputfile for
backend specified by `--backend` (default: c) backend specified by `--backend` (default: c)

View file

@ -336,7 +336,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files:
.. Note:: ``*.rst`` files have ``:literal:`` as their default role. .. Note:: ``*.rst`` files have ``:literal:`` as their default role.
So for them the rule above is only applicable if the ``:nim:`` role So for them the rule above is only applicable if the ``:nim:`` role
is set up manually as the default [*]_:: is set up manually as the default \[*]::
.. role:: nim(code) .. role:: nim(code)
:language: nim :language: nim
@ -345,7 +345,7 @@ To avoid accidental highlighting follow this rule in ``*.nim`` files:
The first 2 lines are for other RST implementations, The first 2 lines are for other RST implementations,
including Github one. including Github one.
.. [*] this is fulfilled when ``doc/rstcommon.rst`` is included. \[*] this is fulfilled when ``doc/rstcommon.rst`` is included.
Best practices Best practices
============== ==============
@ -431,7 +431,7 @@ including prepending location info, writing to log files, etc.).
``` ```
.. _use_Option: .. _use_Option:
[Ongoing debate] Consider using Option instead of return bool + var argument, (Ongoing debate) Consider using Option instead of return bool + var argument,
unless stack allocation is needed (e.g. for efficiency). unless stack allocation is needed (e.g. for efficiency).
```nim ```nim

View file

@ -245,9 +245,9 @@ underscore `_` to a *link text*.
Link text is either one word or a group of words enclosed by backticks `\`` Link text is either one word or a group of words enclosed by backticks `\``
(for a one word case backticks are usually omitted). (for a one word case backticks are usually omitted).
Link text will be displayed *as is* while *link target* will be set to Link text will be displayed *as is* while *link target* will be set to
the anchor [*]_ of Nim symbol that corresponds to link text. the anchor \[*] of Nim symbol that corresponds to link text.
.. [*] anchors' format is described in `HTML anchor generation`_ section below. [*] anchors' format is described in `HTML anchor generation`_ section below.
If you have a constant: If you have a constant:

View file

@ -239,7 +239,7 @@ symbol for which idetools returns valid output.
skConst skConst
------- -------
| **Third column**: module + [n scope nesting] + const name. | **Third column**: module + \[n scope nesting] + const name.
| **Fourth column**: the type of the const value. | **Fourth column**: the type of the const value.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -254,7 +254,7 @@ skConst
skEnumField skEnumField
----------- -----------
| **Third column**: module + [n scope nesting] + enum type + enum field name. | **Third column**: module + \[n scope nesting] + enum type + enum field name.
| **Fourth column**: enum type grouping other enum fields. | **Fourth column**: enum type grouping other enum fields.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -269,7 +269,7 @@ skEnumField
skForVar skForVar
-------- --------
| **Third column**: module + [n scope nesting] + var name. | **Third column**: module + \[n scope nesting] + var name.
| **Fourth column**: type of the var. | **Fourth column**: type of the var.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -291,7 +291,7 @@ defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in the full line yet. The signature will be returned complete in
posterior instances of the iterator. posterior instances of the iterator.
| **Third column**: module + [n scope nesting] + iterator name. | **Third column**: module + \[n scope nesting] + iterator name.
| **Fourth column**: signature of the iterator including return type. | **Fourth column**: signature of the iterator including return type.
| **Docstring**: docstring if available. | **Docstring**: docstring if available.
@ -308,7 +308,7 @@ posterior instances of the iterator.
skLabel skLabel
------- -------
| **Third column**: module + [n scope nesting] + name. | **Third column**: module + \[n scope nesting] + name.
| **Fourth column**: always the empty string. | **Fourth column**: always the empty string.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -325,7 +325,7 @@ skLabel
skLet skLet
----- -----
| **Third column**: module + [n scope nesting] + let name. | **Third column**: module + \[n scope nesting] + let name.
| **Fourth column**: the type of the let variable. | **Fourth column**: the type of the let variable.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -346,7 +346,7 @@ defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in the full line yet. The signature will be returned complete in
posterior instances of the macro. posterior instances of the macro.
| **Third column**: module + [n scope nesting] + macro name. | **Third column**: module + \[n scope nesting] + macro name.
| **Fourth column**: signature of the macro including return type. | **Fourth column**: signature of the macro including return type.
| **Docstring**: docstring if available. | **Docstring**: docstring if available.
@ -384,7 +384,7 @@ Note that at the moment the word `proc` is returned for the
signature of the found method instead of the expected `method`. signature of the found method instead of the expected `method`.
This may change in the future. This may change in the future.
| **Third column**: module + [n scope nesting] + method name. | **Third column**: module + \[n scope nesting] + method name.
| **Fourth column**: signature of the method including return type. | **Fourth column**: signature of the method including return type.
| **Docstring**: docstring if available. | **Docstring**: docstring if available.
@ -402,7 +402,7 @@ This may change in the future.
skParam skParam
------- -------
| **Third column**: module + [n scope nesting] + param name. | **Third column**: module + \[n scope nesting] + param name.
| **Fourth column**: the type of the parameter. | **Fourth column**: the type of the parameter.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -427,7 +427,7 @@ While at the language level a proc is differentiated from others
by the parameters and return value, the signature of the proc by the parameters and return value, the signature of the proc
returned by idetools returns also the pragmas for the proc. returned by idetools returns also the pragmas for the proc.
| **Third column**: module + [n scope nesting] + proc name. | **Third column**: module + \[n scope nesting] + proc name.
| **Fourth column**: signature of the proc including return type. | **Fourth column**: signature of the proc including return type.
| **Docstring**: docstring if available. | **Docstring**: docstring if available.
@ -446,7 +446,7 @@ returned by idetools returns also the pragmas for the proc.
skResult skResult
-------- --------
| **Third column**: module + [n scope nesting] + result. | **Third column**: module + \[n scope nesting] + result.
| **Fourth column**: the type of the result. | **Fourth column**: the type of the result.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -467,7 +467,7 @@ defined, since at that point in the file the parser hasn't processed
the full line yet. The signature will be returned complete in the full line yet. The signature will be returned complete in
posterior instances of the template. posterior instances of the template.
| **Third column**: module + [n scope nesting] + template name. | **Third column**: module + \[n scope nesting] + template name.
| **Fourth column**: signature of the template including return type. | **Fourth column**: signature of the template including return type.
| **Docstring**: docstring if available. | **Docstring**: docstring if available.
@ -496,7 +496,7 @@ posterior instances of the template.
skType skType
------ ------
| **Third column**: module + [n scope nesting] + type name. | **Third column**: module + \[n scope nesting] + type name.
| **Fourth column**: the type. | **Fourth column**: the type.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.
@ -512,7 +512,7 @@ skType
skVar skVar
----- -----
| **Third column**: module + [n scope nesting] + var name. | **Third column**: module + \[n scope nesting] + var name.
| **Fourth column**: the type of the var. | **Fourth column**: the type of the var.
| **Docstring**: always the empty string. | **Docstring**: always the empty string.

View file

@ -328,7 +328,7 @@ Coding Guidelines
* Max line length is 100 characters. * Max line length is 100 characters.
* Provide spaces around binary operators if that enhances readability. * Provide spaces around binary operators if that enhances readability.
* Use a space after a colon, but not before it. * Use a space after a colon, but not before it.
* [deprecated] Start types with a capital `T`, unless they are * (deprecated) Start types with a capital `T`, unless they are
pointers/references which start with `P`. pointers/references which start with `P`.
* Prefer `import package`:nim: over `from package import symbol`:nim:. * Prefer `import package`:nim: over `from package import symbol`:nim:.

View file

@ -2862,11 +2862,11 @@ ref or pointer type nil
procedural type nil procedural type nil
sequence `@[]` sequence `@[]`
string `""` string `""`
tuple[x: A, y: B, ...] (default(A), default(B), ...) `tuple[x: A, y: B, ...]` (default(A), default(B), ...)
(analogous for objects) (analogous for objects)
array[0..., T] [default(T), ...] `array[0..., T]` `[default(T), ...]`
range[T] default(T); this may be out of the valid range `range[T]` default(T); this may be out of the valid range
T = enum cast[T]\(0); this may be an invalid value T = enum `cast[T](0)`; this may be an invalid value
============================ ============================================== ============================ ==============================================

View file

@ -182,7 +182,7 @@ template `^^`(n: NimNode, i: untyped): untyped =
proc `[]`*[T, U: Ordinal](n: NimNode, x: HSlice[T, U]): seq[NimNode] = proc `[]`*[T, U: Ordinal](n: NimNode, x: HSlice[T, U]): seq[NimNode] =
## Slice operation for NimNode. ## Slice operation for NimNode.
## Returns a seq of child of `n` who inclusive range [n[x.a], n[x.b]]. ## Returns a seq of child of `n` who inclusive range `[n[x.a], n[x.b]]`.
let xa = n ^^ x.a let xa = n ^^ x.a
let L = (n ^^ x.b) - xa + 1 let L = (n ^^ x.b) - xa + 1
result = newSeq[NimNode](L) result = newSeq[NimNode](L)

View file

@ -180,7 +180,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery,
## if you require **ALL** the rows. ## if you require **ALL** the rows.
## ##
## Breaking the fastRows() iterator during a loop will cause the next ## Breaking the fastRows() iterator during a loop will cause the next
## database query to raise an [EDb] exception `Commands out of sync`. ## database query to raise an `EDb` exception `Commands out of sync`.
rawExec(db, query, args) rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db) var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil: if sqlres != nil:
@ -203,7 +203,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} = {.tags: [ReadDbEffect].} =
## Same as fastRows but returns a handle that can be used to get column text ## Same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within the iterator body. ## on demand using `[]`. Returned handle is valid only within the iterator body.
rawExec(db, query, args) rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db) var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil: if sqlres != nil:
@ -283,7 +283,7 @@ proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery; iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
args: varargs[string, `$`]): InstantRow = args: varargs[string, `$`]): InstantRow =
## Same as fastRows but returns a handle that can be used to get column text ## Same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within the iterator body. ## on demand using `[]`. Returned handle is valid only within the iterator body.
rawExec(db, query, args) rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db) var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil: if sqlres != nil:

View file

@ -168,7 +168,7 @@ proc dbError*(db: var DbConn) {.
raise e raise e
proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} = proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
## Wrapper that raises [EDb] if `resVal` is neither SQL_SUCCESS or SQL_NO_DATA ## Wrapper that raises `EDb` if `resVal` is neither SQL_SUCCESS or SQL_NO_DATA
if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db) if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db)
proc sqlGetDBMS(db: var DbConn): string {. proc sqlGetDBMS(db: var DbConn): string {.
@ -304,7 +304,7 @@ iterator instantRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect, WriteDbEffect].} = {.tags: [ReadDbEffect, WriteDbEffect].} =
## Same as fastRows but returns a handle that can be used to get column text ## Same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within the iterator body. ## on demand using `[]`. Returned handle is valid only within the iterator body.
var var
rowRes: Row = @[] rowRes: Row = @[]
sz: TSqlLen = 0 sz: TSqlLen = 0

View file

@ -271,7 +271,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} = {.tags: [ReadDbEffect].} =
## same as fastRows but returns a handle that can be used to get column text ## same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within iterator body. ## on demand using `[]`. Returned handle is valid only within iterator body.
setupSingeRowQuery(db, query, args) setupSingeRowQuery(db, query, args)
fetchinstantRows(db) fetchinstantRows(db)
@ -279,7 +279,7 @@ iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): InstantRow args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} = {.tags: [ReadDbEffect].} =
## same as fastRows but returns a handle that can be used to get column text ## same as fastRows but returns a handle that can be used to get column text
## on demand using []. Returned handle is valid only within iterator body. ## on demand using `[]`. Returned handle is valid only within iterator body.
setupSingeRowQuery(db, stmtName, args) setupSingeRowQuery(db, stmtName, args)
fetchinstantRows(db) fetchinstantRows(db)

View file

@ -82,7 +82,7 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol =
## ##
## This proc should be kept in sync with the `renderTypes` proc from ## This proc should be kept in sync with the `renderTypes` proc from
## ``compiler/typesrenderer.nim``. ## ``compiler/typesrenderer.nim``.
assert linkText.kind in {rnRef, rnInner} assert linkText.kind in {rnRstRef, rnInner}
const NimDefs = ["proc", "func", "macro", "method", "iterator", const NimDefs = ["proc", "func", "macro", "method", "iterator",
"template", "converter", "const", "type", "var", "template", "converter", "const", "type", "var",

View file

@ -24,12 +24,12 @@
## using simple plaintext representation. ## using simple plaintext representation.
## ##
## This module is also embedded into Nim compiler; the compiler can output ## This module is also embedded into Nim compiler; the compiler can output
## the result to HTML [#html]_ or Latex [#latex]_. ## the result to HTML \[#html] or Latex \[#latex].
## ##
## .. [#html] commands `nim doc`:cmd: for ``*.nim`` files and ## \[#html] commands `nim doc`:cmd: for ``*.nim`` files and
## `nim rst2html`:cmd: for ``*.rst`` files ## `nim rst2html`:cmd: for ``*.rst`` files
## ##
## .. [#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and ## \[#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and
## `nim rst2tex`:cmd: for ``*.rst``. ## `nim rst2tex`:cmd: for ``*.rst``.
## ##
## If you are new to Markdown/RST please consider reading the following: ## If you are new to Markdown/RST please consider reading the following:
@ -84,8 +84,8 @@
## ##
## Additional Nim-specific features: ## Additional Nim-specific features:
## ##
## * directives: ``code-block`` [cmp:Sphinx]_, ``title``, ## * directives: ``code-block`` \[cmp:Sphinx], ``title``,
## ``index`` [cmp:Sphinx]_ ## ``index`` \[cmp:Sphinx]
## * predefined roles ## * predefined roles
## - ``:nim:`` (default), ``:c:`` (C programming language), ## - ``:nim:`` (default), ``:c:`` (C programming language),
## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). ## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#).
@ -99,9 +99,9 @@
## - ``:cmd:`` for commands and common shells syntax ## - ``:cmd:`` for commands and common shells syntax
## - ``:console:`` the same for interactive sessions ## - ``:console:`` the same for interactive sessions
## (commands should be prepended by ``$``) ## (commands should be prepended by ``$``)
## - ``:program:`` for executable names [cmp:Sphinx]_ ## - ``:program:`` for executable names \[cmp:Sphinx]
## (one can just use ``:cmd:`` on single word) ## (one can just use ``:cmd:`` on single word)
## - ``:option:`` for command line options [cmp:Sphinx]_ ## - ``:option:`` for command line options \[cmp:Sphinx]
## - ``:tok:``, a role for highlighting of programming language tokens ## - ``:tok:``, a role for highlighting of programming language tokens
## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ***triple emphasis*** (bold and italic) using \*\*\*
## * ``:idx:`` role for \`interpreted text\` to include the link to this ## * ``:idx:`` role for \`interpreted text\` to include the link to this
@ -115,7 +115,7 @@
## Here the dummy `//` will disappear, while options `compile`:option: ## Here the dummy `//` will disappear, while options `compile`:option:
## and `doc`:option: will be left in the final document. ## and `doc`:option: will be left in the final document.
## ##
## .. [cmp:Sphinx] similar but different from the directives of ## \[cmp:Sphinx] similar but different from the directives of
## Python `Sphinx directives`_ and `Sphinx roles`_ extensions ## Python `Sphinx directives`_ and `Sphinx roles`_ extensions
## ##
## .. _`extra features`: ## .. _`extra features`:
@ -1458,7 +1458,7 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
newSons = n.sons newSons = n.sons
result = newRstNode(newKind, newSons) result = newRstNode(newKind, newSons)
else: # some link that will be resolved in `resolveSubs` else: # some link that will be resolved in `resolveSubs`
newKind = rnRef newKind = rnRstRef
result = newRstNode(newKind, sons=newSons, info=n.info) result = newRstNode(newKind, sons=newSons, info=n.info)
elif match(p, p.idx, ":w:"): elif match(p, p.idx, ":w:"):
# a role: # a role:
@ -1552,7 +1552,7 @@ proc parseWordOrRef(p: var RstParser, father: PRstNode) =
while currentTok(p).kind in {tkWord, tkPunct}: while currentTok(p).kind in {tkWord, tkPunct}:
if currentTok(p).kind == tkPunct: if currentTok(p).kind == tkPunct:
if isInlineMarkupEnd(p, "_", exact=true): if isInlineMarkupEnd(p, "_", exact=true):
reference = newRstNode(rnRef, info=lineInfo(p, saveIdx)) reference = newRstNode(rnRstRef, info=lineInfo(p, saveIdx))
break break
if not validRefnamePunct(currentTok(p).symbol): if not validRefnamePunct(currentTok(p).symbol):
break break
@ -1746,7 +1746,9 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
defaultCodeLangNim(p, result) defaultCodeLangNim(p, result)
proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
var desc, link = "" # Parses Markdown link. If it's Pandoc auto-link then its second
# son (target) will be in tokenized format (rnInner with leafs).
var desc = newRstNode(rnInner)
var i = p.idx var i = p.idx
var parensStack: seq[char] var parensStack: seq[char]
@ -1754,16 +1756,21 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
parensStack.setLen 0 parensStack.setLen 0
inc i # skip begin token inc i # skip begin token
while true: while true:
if p.tok[i].kind in {tkEof, tkIndent}: return false if p.tok[i].kind == tkEof: return false
if p.tok[i].kind == tkIndent and p.tok[i+1].kind == tkIndent:
return false
let isClosing = checkParen(p.tok[i], parensStack) let isClosing = checkParen(p.tok[i], parensStack)
if p.tok[i].symbol == endToken and not isClosing: if p.tok[i].symbol == endToken and not isClosing:
break break
dest.add p.tok[i].symbol let symbol = if p.tok[i].kind == tkIndent: " " else: p.tok[i].symbol
when dest is string: dest.add symbol
else: dest.add newLeaf(symbol)
inc i inc i
inc i # skip end token inc i # skip end token
parse("]", desc) parse("]", desc)
if p.tok[i].symbol != "(": return false if p.tok[i].symbol == "(":
var link = ""
let linkIdx = i + 1 let linkIdx = i + 1
parse(")", link) parse(")", link)
# only commit if we detected no syntax error: # only commit if we detected no syntax error:
@ -1774,11 +1781,34 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
p.tok[linkIdx].line, p.tok[linkIdx].col) p.tok[linkIdx].line, p.tok[linkIdx].col)
else: else:
let child = newRstNode(rnHyperlink) let child = newRstNode(rnHyperlink)
child.add desc child.add newLeaf(desc.addNodes)
child.add link child.add link
father.add child father.add child
p.idx = i p.idx = i
result = true result = true
elif roPreferMarkdown in p.s.options:
# Use Pandoc's implicit_header_references extension
var n = newRstNode(rnPandocRef)
if p.tok[i].symbol == "[":
var link = newRstNode(rnInner)
let targetIdx = i + 1
parse("]", link)
n.add desc
if link.len != 0: # [description][target]
n.add link
n.info = lineInfo(p, targetIdx)
else: # [description=target][]
n.add desc
n.info = lineInfo(p, p.idx + 1)
else: # [description=target]
n.add desc
n.add desc # target is the same as description
n.info = lineInfo(p, p.idx + 1)
father.add n
p.idx = i
result = true
else:
result = false
proc getFootnoteType(label: PRstNode): (FootnoteType, int) = proc getFootnoteType(label: PRstNode): (FootnoteType, int) =
if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and
@ -3510,6 +3540,13 @@ proc preparePass2*(s: PRstSharedState, mainNode: PRstNode) =
proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode =
# Associate this link alias with its target and change node kind to # Associate this link alias with its target and change node kind to
# rnHyperlink or rnInternalRef appropriately. # rnHyperlink or rnInternalRef appropriately.
var desc, alias: PRstNode
if n.kind == rnPandocRef: # link like [desc][alias]
desc = n.sons[0]
alias = n.sons[1]
else: # n.kind == rnRstRef, link like `desc=alias`_
desc = n
alias = n
type LinkDef = object type LinkDef = object
ar: AnchorRule ar: AnchorRule
priority: int priority: int
@ -3521,14 +3558,13 @@ proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode =
if result == 0: if result == 0:
result = cmp(x.target, y.target) result = cmp(x.target, y.target)
var foundLinks: seq[LinkDef] var foundLinks: seq[LinkDef]
let text = newRstNode(rnInner, n.sons) let refn = rstnodeToRefname(alias)
let refn = rstnodeToRefname(n)
var hyperlinks = findRef(s, refn) var hyperlinks = findRef(s, refn)
for y in hyperlinks: for y in hyperlinks:
foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind), foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind),
target: y.value, info: y.info, target: y.value, info: y.info,
tooltip: "(" & $y.kind & ")") tooltip: "(" & $y.kind & ")")
let substRst = findMainAnchorRst(s, text.addNodes, n.info) let substRst = findMainAnchorRst(s, alias.addNodes, n.info)
for subst in substRst: for subst in substRst:
foundLinks.add LinkDef(ar: arInternalRst, priority: subst.priority, foundLinks.add LinkDef(ar: arInternalRst, priority: subst.priority,
target: newLeaf(subst.target.anchor), target: newLeaf(subst.target.anchor),
@ -3536,19 +3572,19 @@ proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode =
tooltip: "(" & $subst.anchorType & ")") tooltip: "(" & $subst.anchorType & ")")
# find anchors automatically generated from Nim symbols # find anchors automatically generated from Nim symbols
if roNimFile in s.options: if roNimFile in s.options:
let substNim = findMainAnchorNim(s, signature=text, n.info) let substNim = findMainAnchorNim(s, signature=alias, n.info)
for subst in substNim: for subst in substNim:
foundLinks.add LinkDef(ar: arNim, priority: subst.priority, foundLinks.add LinkDef(ar: arNim, priority: subst.priority,
target: newLeaf(subst.refname), target: newLeaf(subst.refname),
info: subst.info, tooltip: subst.tooltip) info: subst.info, tooltip: subst.tooltip)
foundLinks.sort(cmp = cmp, order = Descending) foundLinks.sort(cmp = cmp, order = Descending)
let linkText = addNodes(n) let linkText = addNodes(desc)
if foundLinks.len >= 1: if foundLinks.len >= 1:
let kind = if foundLinks[0].ar == arHyperlink: rnHyperlink let kind = if foundLinks[0].ar == arHyperlink: rnHyperlink
elif foundLinks[0].ar == arNim: rnNimdocRef elif foundLinks[0].ar == arNim: rnNimdocRef
else: rnInternalRef else: rnInternalRef
result = newRstNode(kind) result = newRstNode(kind)
result.sons = @[text, foundLinks[0].target] result.sons = @[newRstNode(rnInner, desc.sons), foundLinks[0].target]
if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip
if foundLinks.len > 1: # report ambiguous link if foundLinks.len > 1: # report ambiguous link
var targets = newSeq[string]() var targets = newSeq[string]()
@ -3585,7 +3621,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode =
if e != "": result = newLeaf(e) if e != "": result = newLeaf(e)
else: rstMessage(s.filenames, s.msgHandler, n.info, else: rstMessage(s.filenames, s.msgHandler, n.info,
mwUnknownSubstitution, key) mwUnknownSubstitution, key)
of rnRef: of rnRstRef, rnPandocRef:
result = resolveLink(s, n) result = resolveLink(s, n)
of rnFootnote: of rnFootnote:
var (fnType, num) = getFootnoteType(n.sons[0]) var (fnType, num) = getFootnoteType(n.sons[0])

View file

@ -49,7 +49,10 @@ type
rnCitation, # similar to footnote, so use rnFootnote instead rnCitation, # similar to footnote, so use rnFootnote instead
rnFootnoteGroup, # footnote group - exists for a purely stylistic rnFootnoteGroup, # footnote group - exists for a purely stylistic
# reason: to display a few footnotes as 1 block # reason: to display a few footnotes as 1 block
rnStandaloneHyperlink, rnHyperlink, rnRef, rnInternalRef, rnFootnoteRef, rnStandaloneHyperlink, rnHyperlink,
rnRstRef, # RST reference like `section name`_
rnPandocRef, # Pandoc Markdown reference like [section name]
rnInternalRef, rnFootnoteRef,
rnNimdocRef, # reference to automatically generated Nim symbol rnNimdocRef, # reference to automatically generated Nim symbol
rnDirective, # a general directive rnDirective, # a general directive
rnDirArg, # a directive argument (for some directives). rnDirArg, # a directive argument (for some directives).
@ -110,7 +113,7 @@ type
## auto-numbered ones without a label) ## auto-numbered ones without a label)
of rnMarkdownBlockQuoteItem: of rnMarkdownBlockQuoteItem:
quotationDepth*: int ## number of characters in line prefix quotationDepth*: int ## number of characters in line prefix
of rnRef, rnSubstitutionReferences, of rnRstRef, rnPandocRef, rnSubstitutionReferences,
rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef: rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef:
info*: TLineInfo ## To have line/column info for warnings at info*: TLineInfo ## To have line/column info for warnings at
## nodes that are post-processed after parsing ## nodes that are post-processed after parsing
@ -281,7 +284,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
inc(d.indent, 2) inc(d.indent, 2)
renderRstSons(d, n, result) renderRstSons(d, n, result)
dec(d.indent, 2) dec(d.indent, 2)
of rnRef: of rnRstRef:
result.add("`") result.add("`")
renderRstSons(d, n, result) renderRstSons(d, n, result)
result.add("`_") result.add("`_")

View file

@ -37,7 +37,7 @@
## ##
## * The same goes for footnotes/citations links: they point to themselves. ## * The same goes for footnotes/citations links: they point to themselves.
## No backreferences are generated since finding all references of a footnote ## No backreferences are generated since finding all references of a footnote
## can be done by simply searching for [footnoteName]. ## can be done by simply searching for ``[footnoteName]``.
import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
algorithm, parseutils, std/strbasics algorithm, parseutils, std/strbasics
@ -1372,7 +1372,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
"</div> &ensp; $1\n</div>\n", "</div> &ensp; $1\n</div>\n",
"\\item[\\textsuperscript{[$3]}]$2 $1\n", "\\item[\\textsuperscript{[$3]}]$2 $1\n",
[body, n.anchor.idS, mark, n.anchor]) [body, n.anchor.idS, mark, n.anchor])
of rnRef: of rnPandocRef:
renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false)
of rnRstRef:
renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=false) renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=false)
of rnStandaloneHyperlink: of rnStandaloneHyperlink:
renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true) renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true)

View file

@ -377,7 +377,7 @@ proc `$`*(ms: MemSlice): string {.inline.} =
copyMem(addr(result[0]), ms.data, ms.size) copyMem(addr(result[0]), ms.data, ms.size)
iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline.} = iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline.} =
## Iterates over [optional `eat`] `delim`-delimited slices in MemFile `mfile`. ## Iterates over \[optional `eat`] `delim`-delimited slices in MemFile `mfile`.
## ##
## Default parameters parse lines ending in either Unix(\\l) or Windows(\\r\\l) ## Default parameters parse lines ending in either Unix(\\l) or Windows(\\r\\l)
## style on on a line-by-line basis. I.e., not every line needs the same ending. ## style on on a line-by-line basis. I.e., not every line needs the same ending.

View file

@ -6,7 +6,7 @@
# -------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------
## This file contains an implementation of the Schubfach algorithm as described in ## This file contains an implementation of the Schubfach algorithm as described in
## ##
## [1] Raffaello Giulietti, "The Schubfach way to render doubles", ## \[1] Raffaello Giulietti, "The Schubfach way to render doubles",
## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN ## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN
# -------------------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------------------

View file

@ -250,7 +250,7 @@ proc max*[T](x: openArray[T]): T =
proc clamp*[T](x, a, b: T): T = proc clamp*[T](x, a, b: T): T =
## Limits the value `x` within the interval [a, b]. ## Limits the value `x` within the interval \[a, b].
## This proc is equivalent to but faster than `max(a, min(b, x))`. ## This proc is equivalent to but faster than `max(a, min(b, x))`.
## ##
## .. warning:: `a <= b` is assumed and will not be checked (currently). ## .. warning:: `a <= b` is assumed and will not be checked (currently).

View file

@ -56,6 +56,9 @@
<ul class="simple"><li><a class="reference" id="next-header-and-so-on_toc" href="#next-header-and-so-on">And so on</a></li> <ul class="simple"><li><a class="reference" id="next-header-and-so-on_toc" href="#next-header-and-so-on">And so on</a></li>
</ul></ul><li><a class="reference" id="more-headers_toc" href="#more-headers">More headers</a></li> </ul></ul><li><a class="reference" id="more-headers_toc" href="#more-headers">More headers</a></li>
<ul class="simple"><li><a class="reference" id="more-headers-up-to-level-6_toc" href="#more-headers-up-to-level-6">Up to level 6</a></li> <ul class="simple"><li><a class="reference" id="more-headers-up-to-level-6_toc" href="#more-headers-up-to-level-6">Up to level 6</a></li>
</ul><li><a class="reference" id="pandoc-markdown_toc" href="#pandoc-markdown">Pandoc Markdown</a></li>
<ul class="simple"><li><a class="reference" id="pandoc-markdown-link-name-syntax_toc" href="#pandoc-markdown-link-name-syntax">Link name syntax</a></li>
<li><a class="reference" id="pandoc-markdown-symbols-documentation_toc" href="#pandoc-markdown-symbols-documentation">Symbols documentation</a></li>
</ul><li> </ul><li>
<details open> <details open>
<summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary> <summary><a class="reference reference-toplevel" href="#7" id="57">Types</a></summary>
@ -229,7 +232,27 @@
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p> cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p>
<p>Ref. type like <a class="reference internal nimdoc" title="type G" href="#G">G</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G</a> and <a class="reference internal nimdoc" title="type G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G*[T]</a>.</p> <p>Ref. type like <a class="reference internal nimdoc" title="type G" href="#G">G</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G</a> and <a class="reference internal nimdoc" title="type G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G*[T]</a>.</p>
<p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a> </p> <p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a> </p>
Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">[]</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a>Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">[]=</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>.Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>.Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>.Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>.Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>.Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.</p> Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">[]</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a>Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">[]=</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>.Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>.Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>.Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>.Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>.Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.
<h1><a class="toc-backref" id="pandoc-markdown" href="#pandoc-markdown">Pandoc Markdown</a></h1><p>Now repeat all the auto links of above in Pandoc Markdown Syntax.</p>
<p>Ref group <a class="reference internal nimdoc" title="proc fn2 (3 overloads)" href="#fn2-procs-all">fn2</a> or specific function like <a class="reference internal nimdoc" title="proc fn2()" href="#fn2">fn2()</a> or <a class="reference internal nimdoc" title="proc fn2(x: int)" href="#fn2,int">fn2( int )</a> or <a class="reference internal nimdoc" title="proc fn2(x: int; y: float)" href="#fn2,int,float">fn2(int, float)</a>.</p>
<p>Ref generics like this: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch(openArray[T], K, proc (T, K))</a> or <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">proc binarySearch(openArray[T], K, proc (T, K))</a> or in different style: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">proc binarysearch(openarray[T], K, proc(T, K))</a>. Can be combined with export symbols and type parameters: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarysearch*[T, K](openArray[T], K, proc (T, K))</a>. With spaces <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binary search</a>.</p>
<p>Note that <tt class="docutils literal"><span class="pre"><span class="Keyword">proc</span></span></tt> can be used in postfix form: <a class="reference internal nimdoc" title="proc binarySearch[T, K](a: openArray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.}): int" href="#binarySearch,openArray[T],K,proc(T,K)">binarySearch proc</a>.</p>
<p>Ref. type like <a class="reference internal nimdoc" title="type G" href="#G">G</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G</a> and <a class="reference internal nimdoc" title="type G" href="#G">G[T]</a> and <a class="reference internal nimdoc" title="type G" href="#G">type G*[T]</a>.</p>
<p>Group ref. with capital letters works: <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fN11</a> or <a class="reference internal nimdoc" title="proc fN11 (2 overloads)" href="#fN11-procs-all">fn11</a></p>
<p>Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">`[]`</a> is the same as <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`(G[T])</a> because there are no overloads. The full form: <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">proc `[]`*[T](x: G[T]): T</a> Ref. <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`</a> aka <a class="reference internal nimdoc" title="proc `[]=`[T](a: var G[T]; index: int; value: T)" href="#[]=,G[T],int,T">`[]=`(G[T], int, T)</a>. Ref. <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">$</a> aka <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc $</a> or <a class="reference internal nimdoc" title="proc $ (2 overloads)" href="#$-procs-all">proc `$`</a>. Ref. <a class="reference internal nimdoc" title="proc `$`[T](a: ref SomeType): string" href="#$,ref.SomeType">$(a: ref SomeType)</a>. Ref. <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">foo_bar</a> aka <a class="reference internal nimdoc" title="iterator fooBar(a: seq[SomeType]): int" href="#fooBar.i,seq[SomeType]">iterator foo_bar_</a>. Ref. <a class="reference internal nimdoc" title="proc fn[T; U, V: SomeFloat]()" href="#fn">fn[T; U,V: SomeFloat]()</a>. Ref. <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">'big</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">func `'big`</a> or <a class="reference internal nimdoc" title="proc `'big`(a: string): SomeType" href="#'big,string">`'big`(string)</a>.</p>
<h2><a class="toc-backref" id="pandoc-markdown-link-name-syntax" href="#pandoc-markdown-link-name-syntax">Link name syntax</a></h2><p>Pandoc Markdown has synax for changing text of links: Ref. <a class="reference internal nimdoc" title="proc `[]`[T](x: G[T]): T" href="#[],G[T]">this proc</a> or <a class="reference internal nimdoc" title="type G" href="#G">another symbol</a>.</p>
<h2><a class="toc-backref" id="pandoc-markdown-symbols-documentation" href="#pandoc-markdown-symbols-documentation">Symbols documentation</a></h2><p>Let us repeat auto links from symbols section below:</p>
<p>There is also variant <a class="reference internal nimdoc" title="proc f(x: G[string])" href="#f,G[string]">f(G[string])</a>. See also <a class="reference internal nimdoc" title="proc f(x: G[int])" href="#f,G[int]">f(G[int])</a>.</p>
</p>
<div class="section" id="7"> <div class="section" id="7">
<h1><a class="toc-backref" href="#7">Types</a></h1> <h1><a class="toc-backref" href="#7">Types</a></h1>
<dl class="item"> <dl class="item">

View file

@ -36,3 +36,6 @@ Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next h
And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on
More headers subdir/subdir_b/utils.html#more-headers More headers More headers subdir/subdir_b/utils.html#more-headers More headers
Up to level 6 subdir/subdir_b/utils.html#more-headers-up-to-level-6 Up to level 6 Up to level 6 subdir/subdir_b/utils.html#more-headers-up-to-level-6 Up to level 6
Pandoc Markdown subdir/subdir_b/utils.html#pandoc-markdown Pandoc Markdown
Link name syntax subdir/subdir_b/utils.html#pandoc-markdown-link-name-syntax Link name syntax
Symbols documentation subdir/subdir_b/utils.html#pandoc-markdown-symbols-documentation Symbols documentation

View file

@ -79,7 +79,9 @@ func fn9*(a: int): int = 42 ## comment
func fn10*(a: int): int = a ## comment func fn10*(a: int): int = a ## comment
# Note capital letter N will be handled correctly in # Note capital letter N will be handled correctly in
# group references like fN11_ or fn11_: # group references like fN11_ or fn11_
# (or [fN11] or [fn11] in Markdown Syntax):
func fN11*() = discard func fN11*() = discard
func fN11*(x: int) = discard func fN11*(x: int) = discard
@ -160,3 +162,52 @@ proc fn*[T; U, V: SomeFloat]() = discard
## Ref. `'big`_ or `func \`'big\``_ or `\`'big\`(string)`_. ## Ref. `'big`_ or `func \`'big\``_ or `\`'big\`(string)`_.
func `'big`*(a: string): SomeType = discard func `'big`*(a: string): SomeType = discard
##[
Pandoc Markdown
===============
Now repeat all the auto links of above in Pandoc Markdown Syntax.
Ref group [fn2] or specific function like [fn2()]
or [fn2( int )] or [fn2(int,
float)].
Ref generics like this: [binarySearch] or [binarySearch(openArray[T], K,
proc (T, K))] or [proc binarySearch(openArray[T], K, proc (T, K))] or
in different style: [proc binarysearch(openarray[T], K, proc(T, K))].
Can be combined with export symbols and type parameters:
[binarysearch*[T, K](openArray[T], K, proc (T, K))].
With spaces [binary search].
Note that `proc` can be used in postfix form: [binarySearch proc].
Ref. type like [G] and [type G] and [G[T]] and [type G*[T]].
Group ref. with capital letters works: [fN11] or [fn11]
Ref. [`[]`] is the same as [proc `[]`(G[T])] because there are no
overloads. The full form: [proc `[]`*[T](x: G[T]): T]
Ref. [`[]=`] aka [`[]=`(G[T], int, T)].
Ref. [$] aka [proc $] or [proc `$`].
Ref. [$(a: ref SomeType)].
Ref. [foo_bar] aka [iterator foo_bar_].
Ref. [fn[T; U,V: SomeFloat]()].
Ref. ['big] or [func `'big`] or [`'big`(string)].
Link name syntax
----------------
Pandoc Markdown has synax for changing text of links:
Ref. [this proc][`[]`] or [another symbol][G[T]].
Symbols documentation
---------------------
Let us repeat auto links from symbols section below:
There is also variant [f(G[string])].
See also [f(G[int])].
]##

View file

@ -10,74 +10,101 @@ discard """
import ../../lib/packages/docutils/[rstast, rst, dochelpers] import ../../lib/packages/docutils/[rstast, rst, dochelpers]
import unittest import unittest
proc rstParseTest(text: string): PRstNode = proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind,
arg: string) = arg: string) =
doAssert msgkind == mwBrokenLink doAssert msgkind == mwBrokenLink
proc fromRst(text: string): LangSymbol =
let r = rstParse(text, "-input-", LineRstInit, ColRstInit,
{roNimFile},
msgHandler=testMsgHandler)
assert r.node.kind == rnRstRef
result = toLangSymbol(r.node)
proc fromMd(text: string): LangSymbol =
let r = rstParse(text, "-input-", LineRstInit, ColRstInit, let r = rstParse(text, "-input-", LineRstInit, ColRstInit,
{roPreferMarkdown, roSupportMarkdown, roNimFile}, {roPreferMarkdown, roSupportMarkdown, roNimFile},
msgHandler=testMsgHandler) msgHandler=testMsgHandler)
result = r.node assert r.node.kind == rnPandocRef
assert r.node.len == 2
# this son is the target:
assert r.node.sons[1].kind == rnInner
result = toLangSymbol(r.node.sons[1])
suite "Integration with Nim": suite "Integration with Nim":
test "simple symbol parsing (shortest form)": test "simple symbol parsing (shortest form)":
let input1 = "g_".rstParseTest let expected = LangSymbol(symKind: "", name: "g")
check input1.toLangSymbol == LangSymbol(symKind: "", name: "g") check "g_".fromRst == expected
check "[g]".fromMd == expected
# test also alternative syntax variants of Pandoc Markdown:
check "[g][]".fromMd == expected
check "[this symbol][g]".fromMd == expected
test "simple symbol parsing (group of words)": test "simple symbol parsing (group of words)":
let input1 = "`Y`_".rstParseTest #let input1 = "`Y`_".rstParseTest
check input1.toLangSymbol == LangSymbol(symKind: "", name: "Y") let expected1 = LangSymbol(symKind: "", name: "Y")
check "`Y`_".fromRst == expected1
check "[Y]".fromMd == expected1
# this means not a statement 'type', it's a backticked identifier `type`: # this means not a statement 'type', it's a backticked identifier `type`:
let input2 = "`type`_".rstParseTest let expected2 = LangSymbol(symKind: "", name: "type")
check input2.toLangSymbol == LangSymbol(symKind: "", name: "type") check "`type`_".fromRst == expected2
check "[type]".fromMd == expected2
let input3 = "`[]`_".rstParseTest let expected3 = LangSymbol(symKind: "", name: "[]")
check input3.toLangSymbol == LangSymbol(symKind: "", name: "[]") check "`[]`_".fromRst == expected3
# Markdown syntax for this case is NOT [[]]
check "[`[]`]".fromMd == expected3
let input4 = "`X Y Z`_".rstParseTest let expected4 = LangSymbol(symKind: "", name: "Xyz")
check input4.toLangSymbol == LangSymbol(symKind: "", name: "Xyz") check "`X Y Z`_".fromRst == expected4
check "[X Y Z]".fromMd == expected4
test "simple proc parsing": test "simple proc parsing":
let input1 = "proc f".rstParseTest let expected = LangSymbol(symKind: "proc", name: "f")
check input1.toLangSymbol == LangSymbol(symKind: "proc", name: "f") check "`proc f`_".fromRst == expected
check "[proc f]".fromMd == expected
test "another backticked name": test "another backticked name":
let input1 = """`template \`type\``_""".rstParseTest let expected = LangSymbol(symKind: "template", name: "type")
check input1.toLangSymbol == LangSymbol(symKind: "template", name: "type") check """`template \`type\``_""".fromRst == expected
# no backslash in Markdown:
check """[template `type`]""".fromMd == expected
test "simple proc parsing with parameters": test "simple proc parsing with parameters":
let input1 = "`proc f*()`_".rstParseTest
let input2 = "`proc f()`_".rstParseTest
let expected = LangSymbol(symKind: "proc", name: "f", let expected = LangSymbol(symKind: "proc", name: "f",
parametersProvided: true) parametersProvided: true)
check input1.toLangSymbol == expected check "`proc f*()`_".fromRst == expected
check input2.toLangSymbol == expected check "`proc f()`_".fromRst == expected
check "[proc f*()]".fromMd == expected
check "[proc f()]".fromMd == expected
test "symbol parsing with 1 parameter": test "symbol parsing with 1 parameter":
let input = "`f(G[int])`_".rstParseTest
let expected = LangSymbol(symKind: "", name: "f", let expected = LangSymbol(symKind: "", name: "f",
parameters: @[("G[int]", "")], parameters: @[("G[int]", "")],
parametersProvided: true) parametersProvided: true)
check input.toLangSymbol == expected check "`f(G[int])`_".fromRst == expected
check "[f(G[int])]".fromMd == expected
test "more proc parsing": test "more proc parsing":
let input1 = "`proc f[T](x:G[T]):M[T]`_".rstParseTest let input1 = "`proc f[T](x:G[T]):M[T]`_".fromRst
let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".rstParseTest let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".fromRst
let input3 = "`proc f*[T](x: G[T]): M[T]`_".rstParseTest let input3 = "`proc f*[T](x: G[T]): M[T]`_".fromRst
let expected = LangSymbol(symKind: "proc", let expected = LangSymbol(symKind: "proc",
name: "f", name: "f",
generics: "[T]", generics: "[T]",
parameters: @[("x", "G[T]")], parameters: @[("x", "G[T]")],
parametersProvided: true, parametersProvided: true,
outType: "M[T]") outType: "M[T]")
check(input1.toLangSymbol == expected) check(input1 == expected)
check(input2.toLangSymbol == expected) check(input2 == expected)
check(input3.toLangSymbol == expected) check(input3 == expected)
test "advanced proc parsing with Nim identifier normalization": test "advanced proc parsing with Nim identifier normalization":
let input = """`proc binarySearch*[T, K](a: openarray[T]; key: K; let inputRst = """`proc binarySearch*[T, K](a: openarray[T]; key: K;
cmp: proc (x: T; y: K): int)`_""".rstParseTest cmp: proc (x: T; y: K): int)`_"""
let inputMd = """[proc binarySearch*[T, K](a: openarray[T]; key: K;
cmp: proc (x: T; y: K): int)]"""
let expected = LangSymbol(symKind: "proc", let expected = LangSymbol(symKind: "proc",
name: "binarysearch", name: "binarysearch",
generics: "[T,K]", generics: "[T,K]",
@ -87,11 +114,12 @@ suite "Integration with Nim":
("cmp", "proc(x:T;y:K):int")], ("cmp", "proc(x:T;y:K):int")],
parametersProvided: true, parametersProvided: true,
outType: "") outType: "")
check(input.toLangSymbol == expected) check(inputRst.fromRst == expected)
check(inputMd.fromMd == expected)
test "the same without proc": test "the same without proc":
let input = """`binarySearch*[T, K](a: openarray[T]; key: K; let input = """`binarySearch*[T, K](a: openarray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.})`_""".rstParseTest cmp: proc (x: T; y: K): int {.closure.})`_"""
let expected = LangSymbol(symKind: "", let expected = LangSymbol(symKind: "",
name: "binarysearch", name: "binarysearch",
generics: "[T,K]", generics: "[T,K]",
@ -101,27 +129,32 @@ suite "Integration with Nim":
("cmp", "proc(x:T;y:K):int")], ("cmp", "proc(x:T;y:K):int")],
parametersProvided: true, parametersProvided: true,
outType: "") outType: "")
check(input.toLangSymbol == expected) check(input.fromRst == expected)
let inputMd = """[binarySearch*[T, K](a: openarray[T]; key: K;
cmp: proc (x: T; y: K): int {.closure.})]"""
check(inputMd.fromMd == expected)
test "operator $ with and without backticks": test "operator $ with and without backticks":
let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_""". let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_"""
rstParseTest let input1md = "[func `$`*[T](a: `open Array`[T]): string]"
let input2 = """`func $*[T](a: \`open Array\`[T]): string`_""". let input2 = """`func $*[T](a: \`open Array\`[T]): string`_"""
rstParseTest let input2md = "[func $*[T](a: `open Array`[T]): string]"
let expected = LangSymbol(symKind: "func", let expected = LangSymbol(symKind: "func",
name: "$", name: "$",
generics: "[T]", generics: "[T]",
parameters: @[("a", "openarray[T]")], parameters: @[("a", "openarray[T]")],
parametersProvided: true, parametersProvided: true,
outType: "string") outType: "string")
check(input1.toLangSymbol == expected) check input1.fromRst == expected
check(input2.toLangSymbol == expected) check input2.fromRst == expected
check input1md.fromMd == expected
check input2md.fromMd == expected
test "operator [] with and without backticks": test "operator [] with and without backticks":
let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_""". let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_"""
rstParseTest let input1md = "[func `[]`[T](a: `open Array`[T], idx: int): T]"
let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_""". let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_"""
rstParseTest let input2md = "[func [][T](a: `open Array`[T], idx: int): T]"
let expected = LangSymbol(symKind: "func", let expected = LangSymbol(symKind: "func",
name: "[]", name: "[]",
generics: "[T]", generics: "[T]",
@ -129,21 +162,25 @@ suite "Integration with Nim":
("idx", "int")], ("idx", "int")],
parametersProvided: true, parametersProvided: true,
outType: "T") outType: "T")
check(input1.toLangSymbol == expected) check input1.fromRst == expected
check(input2.toLangSymbol == expected) check input2.fromRst == expected
check input1md.fromMd == expected
check input2md.fromMd == expected
test "postfix symbol specifier #1": test "postfix symbol specifier #1":
let input = """`walkDir iterator`_""". let input = "`walkDir iterator`_"
rstParseTest let inputMd = "[walkDir iterator]"
let expected = LangSymbol(symKind: "iterator", let expected = LangSymbol(symKind: "iterator",
name: "walkdir") name: "walkdir")
check(input.toLangSymbol == expected) check input.fromRst == expected
check inputMd.fromMd == expected
test "postfix symbol specifier #2": test "postfix symbol specifier #2":
let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_""". let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_"""
rstParseTest let input1md = "[`[]`[T](a: `open Array`[T], idx: int): T func]"
let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_""". let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_"""
rstParseTest # note again that ` is needed between 1st and second [
let input2md = "[`[]`[T](a: `open Array`[T], idx: int): T func]"
let expected = LangSymbol(symKind: "func", let expected = LangSymbol(symKind: "func",
name: "[]", name: "[]",
generics: "[T]", generics: "[T]",
@ -151,11 +188,16 @@ suite "Integration with Nim":
("idx", "int")], ("idx", "int")],
parametersProvided: true, parametersProvided: true,
outType: "T") outType: "T")
check(input1.toLangSymbol == expected) check input1.fromRst == expected
check(input2.toLangSymbol == expected) check input2.fromRst == expected
check input1md.fromMd == expected
check input2md.fromMd == expected
test "type of type": test "type of type":
check ("`CopyFlag enum`_".rstParseTest.toLangSymbol == let inputRst = "`CopyFlag enum`_"
LangSymbol(symKind: "type", let inputMd = "[CopyFlag enum]"
let expected = LangSymbol(symKind: "type",
symTypeKind: "enum", symTypeKind: "enum",
name: "Copyflag")) name: "Copyflag")
check inputRst.fromRst == expected
check inputMd.fromMd == expected

View file

@ -1173,7 +1173,7 @@ suite "Warnings":
lastParagraph lastParagraph
""" """
var warnings = new seq[string] var warnings = new seq[string]
let output = input.toAst(warnings=warnings) let output = input.toAst(rstOptions=preferRst, warnings=warnings)
check(warnings[] == @[ check(warnings[] == @[
"input(3, 14) Warning: broken link 'citation-som'", "input(3, 14) Warning: broken link 'citation-som'",
"input(5, 7) Warning: broken link 'a broken Link'", "input(5, 7) Warning: broken link 'a broken Link'",
@ -1199,7 +1199,7 @@ suite "Warnings":
rnParagraph rnParagraph
rnLeaf 'here' rnLeaf 'here'
rnLeaf ' ' rnLeaf ' '
rnRef rnRstRef
rnLeaf 'brokenLink' rnLeaf 'brokenLink'
""") """)
removeFile("other.rst") removeFile("other.rst")
@ -1558,7 +1558,7 @@ suite "RST inline markup":
test "no punctuation in the end of a standalone URI is allowed": test "no punctuation in the end of a standalone URI is allowed":
check(dedent""" check(dedent"""
[see (http://no.org)], end""".toAst == [see (http://no.org)], end""".toAst(rstOptions = preferRst) ==
dedent""" dedent"""
rnInner rnInner
rnLeaf '[' rnLeaf '['
@ -1606,6 +1606,19 @@ suite "RST inline markup":
rnLeaf 'end' rnLeaf 'end'
""") """)
test "Markdown-style link can be split to a few lines":
check(dedent"""
is [term-rewriting
macros](manual.html#term-rewriting-macros)""".toAst ==
dedent"""
rnInner
rnLeaf 'is'
rnLeaf ' '
rnHyperlink
rnLeaf 'term-rewriting macros'
rnLeaf 'manual.html#term-rewriting-macros'
""")
test "URL with balanced parentheses (Markdown rule)": test "URL with balanced parentheses (Markdown rule)":
# 2 balanced parens, 1 unbalanced: # 2 balanced parens, 1 unbalanced:
check(dedent""" check(dedent"""

View file

@ -983,7 +983,7 @@ Test1
Ref. [#note]_ Ref. [#note]_
""" """
let output1 = input1.toHtml let output1 = input1.toHtml(preferRst)
doAssert output1.count(">[1]</a>") == 1 doAssert output1.count(">[1]</a>") == 1
doAssert output1.count(">[2]</a>") == 2 doAssert output1.count(">[2]</a>") == 2
doAssert "href=\"#footnote-note\"" in output1 doAssert "href=\"#footnote-note\"" in output1
@ -1001,7 +1001,7 @@ Test1
Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_. Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_.
""" """
let output2 = input2.toHtml let output2 = input2.toHtml(preferRst)
doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&amp;allowed]_." doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&amp;allowed]_."
# check that auto-symbol footnotes work: # check that auto-symbol footnotes work:
@ -1017,7 +1017,7 @@ Test1
And [*]_. And [*]_.
""" """
let output3 = input3.toHtml let output3 = input3.toHtml(preferRst)
# both references and footnotes. Footnotes have link to themselves. # both references and footnotes. Footnotes have link to themselves.
doAssert output3.count("href=\"#footnotesym-1\">[*]</a>") == 2 doAssert output3.count("href=\"#footnotesym-1\">[*]</a>") == 2
doAssert output3.count("href=\"#footnotesym-2\">[**]</a>") == 2 doAssert output3.count("href=\"#footnotesym-2\">[**]</a>") == 2
@ -1047,7 +1047,7 @@ Test1
Ref. [#note]_ and [#]_ and [#]_. Ref. [#note]_ and [#]_ and [#]_.
""" """
let output4 = input4.toHtml let output4 = input4.toHtml(preferRst)
doAssert ">[-1]" notin output1 doAssert ">[-1]" notin output1
let order = @[ let order = @[
"footnote-3", "[3]", "Manual1.", "footnote-3", "[3]", "Manual1.",
@ -1072,7 +1072,7 @@ Test1
Ref. [#note]_ Ref. [#note]_
""" """
var error5 = new string var error5 = new string
let output5 = input5.toHtml(error=error5) let output5 = input5.toHtml(preferRst, error=error5)
check(error5[] == "input(1, 1) Error: mismatch in number of footnotes " & check(error5[] == "input(1, 1) Error: mismatch in number of footnotes " &
"and their refs: 1 (lines 2) != 0 (lines ) for auto-numbered " & "and their refs: 1 (lines 2) != 0 (lines ) for auto-numbered " &
"footnotes") "footnotes")
@ -1086,7 +1086,7 @@ Test1
Ref. [*]_ Ref. [*]_
""" """
var error6 = new string var error6 = new string
let output6 = input6.toHtml(error=error6) let output6 = input6.toHtml(preferRst, error=error6)
check(error6[] == "input(1, 1) Error: mismatch in number of footnotes " & check(error6[] == "input(1, 1) Error: mismatch in number of footnotes " &
"and their refs: 1 (lines 3) != 2 (lines 2, 6) for auto-symbol " & "and their refs: 1 (lines 3) != 2 (lines 2, 6) for auto-symbol " &
"footnotes") "footnotes")
@ -1096,7 +1096,7 @@ Test1
Ref. [some:citation-2020]_. Ref. [some:citation-2020]_.
""" """
let output7 = input7.toHtml let output7 = input7.toHtml(preferRst)
doAssert output7.count("href=\"#citation-somecoloncitationminus2020\"") == 2 doAssert output7.count("href=\"#citation-somecoloncitationminus2020\"") == 2
doAssert output7.count("[Some:CITATION-2020]") == 1 doAssert output7.count("[Some:CITATION-2020]") == 1
doAssert output7.count("[some:citation-2020]") == 1 doAssert output7.count("[some:citation-2020]") == 1
@ -1109,7 +1109,7 @@ Test1
Ref. [som]_. Ref. [som]_.
""" """
var warnings8 = new seq[string] var warnings8 = new seq[string]
let output8 = input8.toHtml(warnings=warnings8) let output8 = input8.toHtml(preferRst, warnings=warnings8)
check(warnings8[] == @["input(3, 7) Warning: broken link 'citation-som'"]) check(warnings8[] == @["input(3, 7) Warning: broken link 'citation-som'"])
# check that footnote group does not break parsing of other directives: # check that footnote group does not break parsing of other directives:
@ -1145,7 +1145,7 @@ Test1
.. [Third] Citation. .. [Third] Citation.
""" """
let output10 = input10.toHtml let output10 = input10.toHtml(preferRst)
doAssert output10.count("<hr class=\"footnote\">" & doAssert output10.count("<hr class=\"footnote\">" &
"<div class=\"footnote-group\">") == 3 "<div class=\"footnote-group\">") == 3
doAssert output10.count("<div class=\"footnote-label\">") == 3 doAssert output10.count("<div class=\"footnote-label\">") == 3
@ -1165,7 +1165,7 @@ Test1
.. [#] Body3 .. [#] Body3
.. [2] Body2. .. [2] Body2.
""" """
let output12 = input12.toHtml let output12 = input12.toHtml(preferRst)
let orderAuto = @[ let orderAuto = @[
"#footnoteauto-1", "[1]", "#footnoteauto-1", "[1]",
"#footnoteauto-2", "[3]", "#footnoteauto-2", "[3]",