From 325064435fc0903e011489edbfe604099fdf32a8 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 30 Mar 2021 21:36:42 +0300 Subject: [PATCH 1/8] enable syntax highlighting for inline code --- doc/contributing.rst | 16 ++++++ lib/packages/docutils/rst.nim | 56 ++++++++++++++++---- lib/packages/docutils/rstast.nim | 7 ++- lib/packages/docutils/rstgen.nim | 31 +++++++---- nimdoc/rst2html/expected/rst_examples.html | 6 +-- nimdoc/testproject/expected/testproject.html | 8 +-- tests/stdlib/trstgen.nim | 35 ++++++++---- 7 files changed, 122 insertions(+), 37 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 279a4ee941e59..576283b80e851 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -285,6 +285,22 @@ or the first is preferred. +Inline mono spaced text can be input using \`single backticks\` or +\`\`double backticks\`\`. The former are syntactically highlighted, +the latter are not. To avoid accidental highlighting follow the rule: + +* use single backticks for fragments of code in Nim and other + programming languages, including identifiers + +* prefer double backticks otherwise: + + * for file names: \`\`os.nim\`\` + * for fragments of strings **not** enclosed by `"` and `"` and not + related to code, e.g. text of compiler messages + * for command line options: \`\`--docInternal\`\` + * also when code ends with a standalone ``\`` (otherwise a combination of + ``\`` and a final \` would get escaped) + When you specify an *RST role* (highlighting/interpretation marker) do it in the postfix form for uniformity, that is after \`text in backticks\`. For example an ``:idx:`` role for referencing a topic ("SQLite" in the diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index d331c2c12d365..b3a9ba131ded5 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -70,6 +70,9 @@ ## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, ## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:`` ## (see `RST roles list`_ for description). +## +## .. Note:: default role is non-standard ``:nim:`` (see below) +## ## + inline internal targets ## ## .. _`Nim-specific features`: @@ -78,7 +81,10 @@ ## ## * directives: ``code-block`` [cmp:Sphinx]_, ``title``, ## ``index`` [cmp:Sphinx]_ -## +## * predefined roles ``:nim:`` (default), ``:c:`` (C programming language), +## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). +## That is every language that `highlite `_ supports. +## They turn on appropriate syntax highlighting in inline code. ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this ## text into an index (example: `Nim index`_). @@ -1019,14 +1025,36 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) proc whichRole(sym: string): RstNodeKind = - case sym + case sym.toLowerAscii of "idx": result = rnIdx of "literal": result = rnInlineLiteral of "strong": result = rnStrongEmphasis of "emphasis": result = rnEmphasis of "sub", "subscript": result = rnSub of "sup", "superscript": result = rnSup - else: result = rnGeneralRole + # c++ currently can be spelled only as cpp, c# only as csharp + of "nim", "yaml", "python", "java", "c", "c++", "cpp", "c#", "csharp": + result = rnInlineCode + else: # unknown role + result = rnGeneralRole + +proc toInlineCode(n: PRstNode, language: string): PRstNode = + ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). + result = newRstNode(rnInlineCode) + var args = newRstNode(rnDirArg) + var lang = language + if language == "cpp": lang = "c++" + elif language == "csharp": lang = "c#" + args.add newLeaf(lang) + result.add args + result.add PRstNode(nil) + var lb = newRstNode(rnLiteralBlock) + var s: string + for i in n.sons: + assert i.kind == rnLeaf + s.add i.text + lb.add newLeaf(s) + result.add lb proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind @@ -1052,14 +1080,19 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = result = newRstNode(newKind, newSons) elif match(p, p.idx, ":w:"): # a role: - newKind = whichRole(nextTok(p).symbol) + let roleName = nextTok(p).symbol + newKind = whichRole(roleName) if newKind == rnGeneralRole: let newN = newRstNode(rnInner, n.sons) - newSons = @[newN, newLeaf(nextTok(p).symbol)] + newSons = @[newN, newLeaf(roleName)] + result = newRstNode(newKind, newSons) + elif newKind == rnInlineCode: + result = n.toInlineCode(language=roleName) + else: + result = newRstNode(newKind, newSons) inc p.idx, 3 - result = newRstNode(newKind, newSons) - else: # no change - result = n + else: + result = n.toInlineCode(language="nim") proc matchVerbatim(p: RstParser, start: int, expr: string): int = result = start @@ -1315,9 +1348,12 @@ proc parseInline(p: var RstParser, father: PRstNode) = parseUntil(p, n, "``", false) father.add(n) elif match(p, p.idx, ":w:") and p.tok[p.idx+3].symbol == "`": - let k = whichRole(nextTok(p).symbol) - let n = newRstNode(k) + let roleName = nextTok(p).symbol + let k = whichRole(roleName) + var n = newRstNode(k) inc p.idx, 3 + if k == rnInlineCode: + n = n.toInlineCode(language=roleName) parseUntil(p, n, "`", false) # bug #17260 father.add(n) elif isInlineMarkupStart(p, "`"): diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index ff34da2d19287..395eb0098a165 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -55,12 +55,15 @@ type # * `file#id `_ # * `file#id '_ rnSubstitutionDef, # a definition of a substitution - rnGeneralRole, # Inline markup: + # Inline markup: + rnInlineCode, + rnGeneralRole, # interpreted text with an unknown role rnSub, rnSup, rnIdx, rnEmphasis, # "*" rnStrongEmphasis, # "**" rnTripleEmphasis, # "***" - rnInterpretedText, # "`" + rnInterpretedText, # "`" an auxiliary role for parsing that will + # be converted into other kinds like rnInlineCode rnInlineLiteral, # "``" rnInlineTarget, # "_`target`" rnSubstitutionReferences, # "|" diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 30c7f3080f410..00f44f1cd2c93 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -942,7 +942,7 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = result.init if n.isNil: return - assert n.kind == rnCodeBlock + assert n.kind in {rnCodeBlock, rnInlineCode} assert(not n.sons[2].isNil) # Parse the field list for rendering parameters if there are any. @@ -987,8 +987,8 @@ proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string, "" & ( d.config.getOrDefault"doc.listing_button" % id) -proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = - ## Renders a code block, appending it to `result`. +proc renderCode(d: PDoc, n: PRstNode, result: var string) = + ## Renders a code (code block or inline code), appending it to `result`. ## ## If the code block uses the ``number-lines`` option, a table will be ## generated with two columns, the first being a list of numbers and the @@ -997,7 +997,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = ## may also come from the parser through the internal ``default-language`` ## option to differentiate between a plain code block and Nim's code block ## extension. - assert n.kind == rnCodeBlock + assert n.kind in {rnCodeBlock, rnInlineCode} if n.sons[2] == nil: return var params = d.parseCodeBlockParams(n) var m = n.sons[2].sons[0] @@ -1006,10 +1006,23 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = if params.testCmd.len > 0 and d.onTestSnippet != nil: d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text) - let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text, + var blockStart, blockEnd: string + case d.target + of outHtml: + if n.kind == rnCodeBlock: + (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text, n.anchor.idS) - dispA(d.target, result, blockStart, - "\\begin{rstpre}\n" & n.anchor.idS & "\n", []) + else: # rnInlineCode + blockStart = "" + blockEnd = "" + of outLatex: + if n.kind == rnCodeBlock: + blockStart = "\n\n\\begin{rstpre}" & n.anchor.idS & "\n" + blockEnd = "\n\\end{rstpre}\n" + else: # rnInlineCode + blockStart = "\\texttt{" + blockEnd = "}" + dispA(d.target, result, blockStart, blockStart, []) if params.lang == langNone: if len(params.langStr) > 0: d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) @@ -1028,7 +1041,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = esc(d.target, substr(m.text, g.start, g.length+g.start-1)), tokenClassToStr[g.kind]]) deinitGeneralTokenizer(g) - dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n") + dispA(d.target, result, blockEnd, blockEnd) proc renderContainer(d: PDoc, n: PRstNode, result: var string) = var tmp = "" @@ -1294,7 +1307,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = result.add addNodes(lastSon(n)) of rnImage, rnFigure: renderImage(d, n, result) - of rnCodeBlock: renderCodeBlock(d, n, result) + of rnCodeBlock, rnInlineCode: renderCode(d, n, result) of rnContainer: renderContainer(d, n, result) of rnSubstitutionReferences, rnSubstitutionDef: renderAux(d, n, "|$1|", "|$1|", result) diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 8253289a44464..32abc0f80d3a7 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -213,7 +213,7 @@

T's be p's return type. NRVO applies for T if sizeof(T) >= N (where N is implementation dependent), in other words, it applies for "big" structures.

Apart from built-in operations like array indexing, memory allocation, etc. the raise statement is the only way to raise an exception.

-

typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

+

typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

The !=, >, >=, in, notin, isnot operators are in fact templates:

a > b is transformed into b < a.
a in b is transformed into contains(b, a).
notin and isnot have the obvious meanings.

A template where every parameter is untyped is called an immediate template. For historical reasons templates can be explicitly annotated with an immediate pragma and then these templates do not take part in overloading resolution and the parameters' types are ignored by the compiler. Explicit immediate templates are now deprecated.

@@ -297,10 +297,10 @@

Introduction

"Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir."

This document is a tutorial for the programming language Nim. This tutorial assumes that you are familiar with basic programming concepts like variables, types, or statements but is kept very basic. The manual contains many more examples of the advanced language features. All code examples in this tutorial, as well as the ones found in the rest of Nim's documentation, follow the Nim style guide.

-

However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has finished. This return and continue is called a yield statement. Now the only thing left to do is to replace the proc keyword by iterator and here it is - our first iterator:

+

However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has finished. This return and continue is called a yield statement. Now the only thing left to do is to replace the proc keyword by iterator and here it is - our first iterator:

- +
A1 headerA2 | not fooled
C1C2 bold
D1 code \|D2
D1 code \|D2
E1 | text
F2 without pipe

not in table

diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 599a904a2d835..a4f4eb7187a3f 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -565,14 +565,14 @@

Procs

func someFunc() {...}{.raises: [], tags: [].}
-My someFunc. Stuff in quotes here. Some link +My someFunc. Stuff in quotes here. Some link
proc fromUtils3() {...}{.raises: [], tags: [].}
-came form utils but should be shown where fromUtilsGen is called +came form utils but should be shown where fromUtilsGen is called

Example:

discard 1
@@ -765,7 +765,7 @@

Procs

proc low[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
-

Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

+

Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

See also:

@@ -776,7 +776,7 @@

Procs

proc low2[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
-

Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

+

Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

See also:

diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index da01c30d21926..00e55fe490c24 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -36,6 +36,11 @@ proc toHtml(input: string, except EParseError: discard +# inline code tags (for parsing originated from highlite.nim) +proc id(str: string): string = """""" & str & "" +proc op(str: string): string = """""" & str & "" +proc pu(str: string): string = """""" & str & "" + suite "YAML syntax highlighting": test "Basics": let input = """.. code-block:: yaml @@ -201,14 +206,14 @@ not in table""" `|` outside a table cell should render as `\|` consistently with markdown, see https://stackoverflow.com/a/66557930/1426932 ]# - doAssert output1 == """ + check(output1 == """ - +
A1 headerA2 | not fooled
C1C2 bold
D1 code \|D2
D1 """ & id"code" & " " & op"\|" & """D2
E1 | text
F2 without pipe

not in table

-""" +""") let input2 = """ | A1 header | A2 | | --- | --- |""" @@ -556,11 +561,22 @@ let x = 1 doAssert "foo.bar""" - check """`foo\`\`bar`""".toHtml == """foo``bar""" - check """`foo\`bar`""".toHtml == """foo`bar""" - check """`\`bar`""".toHtml == """`bar""" - check """`a\b\x\\ar`""".toHtml == """a\b\x\\ar""" + check("""`foo.bar`""".toHtml == + """""" & + id"foo" & op"." & id"bar" & "") + check("""`foo\`\`bar`""".toHtml == + """""" & + id"foo" & pu"`" & pu"`" & id"bar" & "") + check("""`foo\`bar`""".toHtml == + """""" & + id"foo" & pu"`" & id"bar" & "") + check("""`\`bar`""".toHtml == + """""" & + pu"`" & id"bar" & "") + check("""`a\b\x\\ar`""".toHtml == + """""" & + id"a" & op"""\""" & id"b" & op"""\""" & id"x" & op"""\\""" & id"ar" & + "") test "inline literal": check """``foo.bar``""".toHtml == """foo.bar""" @@ -1341,7 +1357,8 @@ Test1 test "(not) Roles: check escaping 1": let expected = """See :subscript:""" & - """some text.""" + """""" & id"some" & " " & id"text" & + "." check """See \:subscript:`some text`.""".toHtml == expected check """See :subscript\:`some text`.""".toHtml == expected From 32ea770a1a1593070e45e408acd8f7af4f7a8492 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 1 Apr 2021 02:19:12 +0300 Subject: [PATCH 2/8] finish '.. default-role' and preliminary '.. role' implementation --- lib/packages/docutils/rst.nim | 57 ++++++++++++++++++++++++++++++----- tests/stdlib/trstgen.nim | 36 ++++++++++++++++++++++ 2 files changed, 85 insertions(+), 8 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index b3a9ba131ded5..9a77852a63f1c 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -460,6 +460,8 @@ type hTitleCnt: int # =0 if no title, =1 if only main title, # =2 if both title and subtitle are present hCurLevel: int # current section level + currRole: string + currRoleKind: RstNodeKind subs: seq[Substitution] # substitutions refs: seq[Substitution] # references anchors: seq[AnchorSubst] # internal target substitutions @@ -520,10 +522,15 @@ proc defaultFindFile*(filename: string): string = if fileExists(filename): result = filename else: result = "" +const defaultRoleKind = rnInlineCode +const defaultRole = "nim" + proc newSharedState(options: RstParseOptions, findFile: FindFileHandler, msgHandler: MsgHandler): PSharedState = new(result) + result.currRoleKind = defaultRoleKind + result.currRole = defaultRole result.subs = @[] result.refs = @[] result.options = options @@ -1024,18 +1031,24 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = for i in countup(0, sep - incr): a.add(n.sons[i]) for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) -proc whichRole(sym: string): RstNodeKind = - case sym.toLowerAscii +const supportedLanguages = ["nim", "yaml", "python", "java", "c", + "c++", "cpp", "c#", "csharp"] +proc whichRole(p: RstParser, sym: string): RstNodeKind = + let r = sym.toLowerAscii + case r of "idx": result = rnIdx of "literal": result = rnInlineLiteral of "strong": result = rnStrongEmphasis of "emphasis": result = rnEmphasis of "sub", "subscript": result = rnSub of "sup", "superscript": result = rnSup + # literal and code are the same in our implementation + of "code": result = rnInlineLiteral # c++ currently can be spelled only as cpp, c# only as csharp - of "nim", "yaml", "python", "java", "c", "c++", "cpp", "c#", "csharp": + elif r in supportedLanguages: result = rnInlineCode else: # unknown role + rstMessage(p, mwUnsupportedLanguage, p.s.currRole) result = rnGeneralRole proc toInlineCode(n: PRstNode, language: string): PRstNode = @@ -1081,7 +1094,7 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = elif match(p, p.idx, ":w:"): # a role: let roleName = nextTok(p).symbol - newKind = whichRole(roleName) + newKind = whichRole(p, roleName) if newKind == rnGeneralRole: let newN = newRstNode(rnInner, n.sons) newSons = @[newN, newLeaf(roleName)] @@ -1092,7 +1105,11 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = result = newRstNode(newKind, newSons) inc p.idx, 3 else: - result = n.toInlineCode(language="nim") + if p.s.currRoleKind == rnInlineCode: + result = n.toInlineCode(language=p.s.currRole) + else: + newKind = p.s.currRoleKind + result = newRstNode(newKind, newSons) proc matchVerbatim(p: RstParser, start: int, expr: string): int = result = start @@ -1349,7 +1366,7 @@ proc parseInline(p: var RstParser, father: PRstNode) = father.add(n) elif match(p, p.idx, ":w:") and p.tok[p.idx+3].symbol == "`": let roleName = nextTok(p).symbol - let k = whichRole(roleName) + let k = whichRole(p, roleName) var n = newRstNode(k) inc p.idx, 3 if k == rnInlineCode: @@ -2457,6 +2474,28 @@ proc dirAdmonition(p: var RstParser, d: string): PRstNode = proc dirDefaultRole(p: var RstParser): PRstNode = result = parseDirective(p, rnDefaultRole, {hasArg}, nil) + if result.sons[0].len == 0: + p.s.currRoleKind = defaultRoleKind + p.s.currRole = defaultRole + else: + assert result.sons[0].sons[0].kind == rnLeaf + p.s.currRole = result.sons[0].sons[0].text + p.s.currRoleKind = whichRole(p, p.s.currRole) + +proc dirRole(p: var RstParser): PRstNode = + result = parseDirective(p, rnDirective, {hasArg, hasOptions}, nil) + # just check that language is supported, TODO: real role association + if result.sons[1] != nil: + for fld in result.sons[1].sons: + if fld.sons[0].sons[0].kind == rnLeaf and + fld.sons[0].sons[0].text == "language": + var s: string + for leaf in fld.sons[1].sons: + assert leaf.kind == rnLeaf + s.add leaf.text + s = s.strip + if s notin supportedLanguages: + rstMessage(p, mwUnsupportedLanguage, s) proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, contentParser: SectionParser) = @@ -2501,7 +2540,9 @@ proc selectDir(p: var RstParser, d: string): PRstNode = of "code-block": result = dirCodeBlock(p, nimExtension = true) of "container": result = dirContainer(p) of "contents": result = dirContents(p) - of "danger", "error": result = dirAdmonition(p, d) + of "danger": result = dirAdmonition(p, d) + of "default-role": result = dirDefaultRole(p) + of "error": result = dirAdmonition(p, d) of "figure": result = dirFigure(p) of "hint": result = dirAdmonition(p, d) of "image": result = dirImage(p) @@ -2514,10 +2555,10 @@ proc selectDir(p: var RstParser, d: string): PRstNode = result = dirRaw(p) else: rstMessage(p, meInvalidDirective, d) + of "role": result = dirRole(p) of "tip": result = dirAdmonition(p, d) of "title": result = dirTitle(p) of "warning": result = dirAdmonition(p, d) - of "default-role": result = dirDefaultRole(p) else: let tok = p.tok[p.idx-2] # report on directive in ".. directive::" rstMessage(p, meInvalidDirective, d, tok.line, tok.col) diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 00e55fe490c24..0586509c9a379 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -583,8 +583,44 @@ let x = 1 check """``foo\bar``""".toHtml == """foo\bar""" check """``f\`o\\o\b`ar``""".toHtml == """f\`o\\o\b`ar""" + test "default-role": + # nim(default) -> literal -> nim -> code(=literal) + let input = dedent""" + Par1 `value1`. + + .. default-role:: literal + + Par2 `value2`. + + .. default-role:: nim + + Par3 `value3`. + + .. default-role:: code + + Par4 `value4`.""" + let p1 = """Par1 """ & id"value1" & "." + let p2 = """

Par2 value2.

""" + let p3 = """

Par3 """ & id"value3" & ".

" + let p4 = """

Par4 value4.

""" + let expected = p1 & p2 & "\n" & p3 & "\n" & p4 & "\n" + check(input.toHtml == expected) + + test "role directive": + let input = dedent""" + .. role:: y(code) + :language: yaml + + .. role:: brainhelp(code) + :language: brainhelp + """ + var warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0]) + test "RST comments": let input1 = """ + Check that comment disappears: .. From c9c72683468e534906b7110bb10976c4b358527b Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 1 Apr 2021 21:48:53 +0300 Subject: [PATCH 3/8] more compact check in dirRole --- lib/packages/docutils/rst.nim | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 9a77852a63f1c..d0b92eb46e23d 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2485,17 +2485,9 @@ proc dirDefaultRole(p: var RstParser): PRstNode = proc dirRole(p: var RstParser): PRstNode = result = parseDirective(p, rnDirective, {hasArg, hasOptions}, nil) # just check that language is supported, TODO: real role association - if result.sons[1] != nil: - for fld in result.sons[1].sons: - if fld.sons[0].sons[0].kind == rnLeaf and - fld.sons[0].sons[0].text == "language": - var s: string - for leaf in fld.sons[1].sons: - assert leaf.kind == rnLeaf - s.add leaf.text - s = s.strip - if s notin supportedLanguages: - rstMessage(p, mwUnsupportedLanguage, s) + let lang = getFieldValue(result, "language").strip + if lang != "" and lang notin supportedLanguages: + rstMessage(p, mwUnsupportedLanguage, lang) proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, contentParser: SectionParser) = From 39c601d1d10e39f917218d1c28a14cc190037fa6 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 2 Apr 2021 02:32:25 +0300 Subject: [PATCH 4/8] set :literal: as default role for *.rst --- compiler/docgen.nim | 3 +- doc/contributing.rst | 42 +++++++++++++++++++------ lib/packages/docutils/rst.nim | 29 ++++++++++------- nimdoc/rst2html/source/rst_examples.rst | 4 +++ tests/stdlib/trstgen.nim | 2 +- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f1a9750b715cd..fdc0fa39e1326 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -192,7 +192,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.cache = cache result.outDir = conf.outDir.string initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), - conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown}, + conf.configVars, filename.string, + {roSupportRawDirective, roSupportMarkdown, roNimFile}, docgenFindFile, compilerMsgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): diff --git a/doc/contributing.rst b/doc/contributing.rst index 576283b80e851..7ee3aa4442d6d 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -285,12 +285,33 @@ or the first is preferred. -Inline mono spaced text can be input using \`single backticks\` or +When you specify an *RST role* (highlighting/interpretation marker) do it +in the postfix form for uniformity, that is after \`text in backticks\`. +For example an ``:idx:`` role for referencing a topic ("SQLite" in the +example below) from `Nim Index`_ can be used in doc comment this way: + +.. code-block:: nim + ## A higher level `SQLite`:idx: database wrapper. + +.. _`Nim Index`: https://nim-lang.org/docs/theindex.html + +Inline monospaced text can be input using \`single backticks\` or \`\`double backticks\`\`. The former are syntactically highlighted, -the latter are not. To avoid accidental highlighting follow the rule: +the latter are not. +To avoid accidental highlighting follow this rule in `*.nim` files: * use single backticks for fragments of code in Nim and other - programming languages, including identifiers + programming languages, including identifiers, in `*.nim` files. + + For languages other than Nim add a role after final backtick, + e.g. for C++ inline highlighting:: + + `#include `:cpp: + + For a currently unsupported language add the `:code:` role, + like for SQL in this example:: + + `SELECT * FROM ;`:code: * prefer double backticks otherwise: @@ -301,15 +322,16 @@ the latter are not. To avoid accidental highlighting follow the rule: * also when code ends with a standalone ``\`` (otherwise a combination of ``\`` and a final \` would get escaped) -When you specify an *RST role* (highlighting/interpretation marker) do it -in the postfix form for uniformity, that is after \`text in backticks\`. -For example an ``:idx:`` role for referencing a topic ("SQLite" in the -example below) from `Nim Index`_ can be used in doc comment this way: +.. Note:: `*.rst` files have `:literal:` as their default role. + So for them the rule above is only applicable if the `:nim:` role + is set up manually as the default:: -.. code-block:: nim - ## A higher level `SQLite`:idx: database wrapper. + .. role:: nim(code) + :language: nim + .. default-role:: nim -.. _`Nim Index`: https://nim-lang.org/docs/theindex.html + The first 2 lines are for other RST implementations, + including Github one. Best practices ============== diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index d0b92eb46e23d..9924335e44dbe 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -70,9 +70,6 @@ ## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, ## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:`` ## (see `RST roles list`_ for description). -## -## .. Note:: default role is non-standard ``:nim:`` (see below) -## ## + inline internal targets ## ## .. _`Nim-specific features`: @@ -85,6 +82,10 @@ ## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). ## That is every language that `highlite `_ supports. ## They turn on appropriate syntax highlighting in inline code. +## +## .. Note:: default role for Nim files is ``:nim:``, +## for ``*.rst`` it's currently ``:literal:``. +## ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this ## text into an index (example: `Nim index`_). @@ -167,7 +168,9 @@ type roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) - roSupportMarkdown ## support additional features of Markdown + roSupportMarkdown, ## support additional features of Markdown + roNimFile ## set for Nim files where default interpreted + ## text role should be :nim: RstParseOptions* = set[RstParseOption] @@ -460,8 +463,8 @@ type hTitleCnt: int # =0 if no title, =1 if only main title, # =2 if both title and subtitle are present hCurLevel: int # current section level - currRole: string - currRoleKind: RstNodeKind + currRole: string # current interpreted text role + currRoleKind: RstNodeKind # ... and its node kind subs: seq[Substitution] # substitutions refs: seq[Substitution] # references anchors: seq[AnchorSubst] # internal target substitutions @@ -522,15 +525,17 @@ proc defaultFindFile*(filename: string): string = if fileExists(filename): result = filename else: result = "" -const defaultRoleKind = rnInlineCode -const defaultRole = "nim" +proc defaultRoleKind(options: RstParseOptions): RstNodeKind = + if roNimFile in options: rnInlineCode else: rnInlineLiteral +proc defaultRole(options: RstParseOptions): string = + if roNimFile in options: "nim" else: "literal" proc newSharedState(options: RstParseOptions, findFile: FindFileHandler, msgHandler: MsgHandler): PSharedState = new(result) - result.currRoleKind = defaultRoleKind - result.currRole = defaultRole + result.currRoleKind = defaultRoleKind(options) + result.currRole = defaultRole(options) result.subs = @[] result.refs = @[] result.options = options @@ -2475,8 +2480,8 @@ proc dirAdmonition(p: var RstParser, d: string): PRstNode = proc dirDefaultRole(p: var RstParser): PRstNode = result = parseDirective(p, rnDefaultRole, {hasArg}, nil) if result.sons[0].len == 0: - p.s.currRoleKind = defaultRoleKind - p.s.currRole = defaultRole + p.s.currRoleKind = defaultRoleKind(p.s.options) + p.s.currRole = defaultRole(p.s.options) else: assert result.sons[0].sons[0].kind == rnLeaf p.s.currRole = result.sons[0].sons[0].text diff --git a/nimdoc/rst2html/source/rst_examples.rst b/nimdoc/rst2html/source/rst_examples.rst index 54f0124c8cd1d..7fa20de6c5ba8 100644 --- a/nimdoc/rst2html/source/rst_examples.rst +++ b/nimdoc/rst2html/source/rst_examples.rst @@ -5,6 +5,10 @@ Not a Nim Manual :Authors: Andreas Rumpf, Zahary Karadjov :Version: |nimversion| +.. role:: nim(code) + :language: nim +.. default-role:: nim + .. contents:: diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 0586509c9a379..ad0c27f059df1 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -10,7 +10,7 @@ import unittest, strutils, strtabs import std/private/miscdollars proc toHtml(input: string, - rstOptions: RstParseOptions = {roSupportMarkdown}, + rstOptions: RstParseOptions = {roSupportMarkdown, roNimFile}, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. From baa7115000be18f0f07ad3c37b1ed7e9560ffd58 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 2 Apr 2021 19:29:20 +0300 Subject: [PATCH 5/8] Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour --- lib/packages/docutils/rst.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 9924335e44dbe..a4cbf2d99f36f 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2479,13 +2479,11 @@ proc dirAdmonition(p: var RstParser, d: string): PRstNode = proc dirDefaultRole(p: var RstParser): PRstNode = result = parseDirective(p, rnDefaultRole, {hasArg}, nil) - if result.sons[0].len == 0: - p.s.currRoleKind = defaultRoleKind(p.s.options) - p.s.currRole = defaultRole(p.s.options) + if result.sons[0].len == 0: p.s.currRole = defaultRole(p.s.options) else: assert result.sons[0].sons[0].kind == rnLeaf p.s.currRole = result.sons[0].sons[0].text - p.s.currRoleKind = whichRole(p, p.s.currRole) + p.s.currRoleKind = whichRole(p, p.s.currRole) proc dirRole(p: var RstParser): PRstNode = result = parseDirective(p, rnDirective, {hasArg, hasOptions}, nil) From cd462c1a5a4bf7bc250e8a996cd89bcf2ba7de20 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 2 Apr 2021 19:52:42 +0300 Subject: [PATCH 6/8] use whichRole for setting currRoleKind --- lib/packages/docutils/rst.nim | 44 +++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index a4cbf2d99f36f..265053b609527 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -525,17 +525,36 @@ proc defaultFindFile*(filename: string): string = if fileExists(filename): result = filename else: result = "" -proc defaultRoleKind(options: RstParseOptions): RstNodeKind = - if roNimFile in options: rnInlineCode else: rnInlineLiteral proc defaultRole(options: RstParseOptions): string = if roNimFile in options: "nim" else: "literal" +# mirror highlite.nim sourceLanguageToStr with substitutions c++ cpp, c# csharp +const supportedLanguages = ["nim", "yaml", "python", "java", "c", + "cpp", "csharp"] + +proc whichRoleAux(sym: string): RstNodeKind = + let r = sym.toLowerAscii + case r + of "idx": result = rnIdx + of "literal": result = rnInlineLiteral + of "strong": result = rnStrongEmphasis + of "emphasis": result = rnEmphasis + of "sub", "subscript": result = rnSub + of "sup", "superscript": result = rnSup + # literal and code are the same in our implementation + of "code": result = rnInlineLiteral + # c++ currently can be spelled only as cpp, c# only as csharp + elif r in supportedLanguages: + result = rnInlineCode + else: # unknown role + result = rnGeneralRole + proc newSharedState(options: RstParseOptions, findFile: FindFileHandler, msgHandler: MsgHandler): PSharedState = new(result) - result.currRoleKind = defaultRoleKind(options) result.currRole = defaultRole(options) + result.currRoleKind = whichRoleAux(result.currRole) result.subs = @[] result.refs = @[] result.options = options @@ -1036,25 +1055,10 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = for i in countup(0, sep - incr): a.add(n.sons[i]) for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) -const supportedLanguages = ["nim", "yaml", "python", "java", "c", - "c++", "cpp", "c#", "csharp"] proc whichRole(p: RstParser, sym: string): RstNodeKind = - let r = sym.toLowerAscii - case r - of "idx": result = rnIdx - of "literal": result = rnInlineLiteral - of "strong": result = rnStrongEmphasis - of "emphasis": result = rnEmphasis - of "sub", "subscript": result = rnSub - of "sup", "superscript": result = rnSup - # literal and code are the same in our implementation - of "code": result = rnInlineLiteral - # c++ currently can be spelled only as cpp, c# only as csharp - elif r in supportedLanguages: - result = rnInlineCode - else: # unknown role + result = whichRoleAux(sym) + if result == rnGeneralRole: rstMessage(p, mwUnsupportedLanguage, p.s.currRole) - result = rnGeneralRole proc toInlineCode(n: PRstNode, language: string): PRstNode = ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). From 17b7cddbb03c5257e5ac2fb8c8e10d8ac9ad5756 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 2 Apr 2021 19:53:54 +0300 Subject: [PATCH 7/8] Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour --- lib/packages/docutils/rst.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 265053b609527..15c5b0ffa7b8b 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1063,7 +1063,7 @@ proc whichRole(p: RstParser, sym: string): RstNodeKind = proc toInlineCode(n: PRstNode, language: string): PRstNode = ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). result = newRstNode(rnInlineCode) - var args = newRstNode(rnDirArg) + let args = newRstNode(rnDirArg) var lang = language if language == "cpp": lang = "c++" elif language == "csharp": lang = "c#" From 32541fe1f67e6db50d2c913e4b17883a9328a903 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 2 Apr 2021 21:17:55 +0300 Subject: [PATCH 8/8] rename rnGeneralRole -> rnUnknownRole --- lib/packages/docutils/rst.nim | 6 +++--- lib/packages/docutils/rstast.nim | 4 ++-- lib/packages/docutils/rstgen.nim | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 265053b609527..189f076340cfb 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -547,7 +547,7 @@ proc whichRoleAux(sym: string): RstNodeKind = elif r in supportedLanguages: result = rnInlineCode else: # unknown role - result = rnGeneralRole + result = rnUnknownRole proc newSharedState(options: RstParseOptions, findFile: FindFileHandler, @@ -1057,7 +1057,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = proc whichRole(p: RstParser, sym: string): RstNodeKind = result = whichRoleAux(sym) - if result == rnGeneralRole: + if result == rnUnknownRole: rstMessage(p, mwUnsupportedLanguage, p.s.currRole) proc toInlineCode(n: PRstNode, language: string): PRstNode = @@ -1104,7 +1104,7 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = # a role: let roleName = nextTok(p).symbol newKind = whichRole(p, roleName) - if newKind == rnGeneralRole: + if newKind == rnUnknownRole: let newN = newRstNode(rnInner, n.sons) newSons = @[newN, newLeaf(roleName)] result = newRstNode(newKind, newSons) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 395eb0098a165..dd456b5779545 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -57,7 +57,7 @@ type rnSubstitutionDef, # a definition of a substitution # Inline markup: rnInlineCode, - rnGeneralRole, # interpreted text with an unknown role + rnUnknownRole, # interpreted text with an unknown role rnSub, rnSup, rnIdx, rnEmphasis, # "*" rnStrongEmphasis, # "**" @@ -255,7 +255,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) = result.add(" <") renderRstToRst(d, n.sons[1], result) result.add(">`_") - of rnGeneralRole: + of rnUnknownRole: result.add('`') renderRstToRst(d, n.sons[0],result) result.add("`:") diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 00f44f1cd2c93..1a16f590e1882 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1313,7 +1313,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "|$1|", "|$1|", result) of rnDirective: renderAux(d, n, "", "", result) - of rnGeneralRole: + of rnUnknownRole: var tmp0 = "" var tmp1 = "" renderRstToOut(d, n.sons[0], tmp0)