From ce7ecaf58dfa7624f29668c7761186be4a448dc4 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 23 Aug 2022 21:49:53 +0300 Subject: [PATCH] Add `doctype: RST|Markdown|RstMarkdown` pragma (#20252) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add `doctype: RST|Markdown|RstMarkdown` pragma Implements https://github.com/nim-lang/RFCs/issues/68 , see also discussion in https://github.com/nim-lang/Nim/issues/17987 The permitted values: * `markdown`, which is default. It still contains nearly all of the RST supported but it is assumed that in time we will give up most or all RST features in this mode * `rst`, without any extensions * `RstMarkdown` — compatibility with Nim 1.x. It's basically RST with those Markdown features enabled that don't conflict with RST. * Apply suggestions from code review Co-authored-by: Clay Sweetser * Additional fix in spirit of review * Fix test after #20188 Co-authored-by: Clay Sweetser --- compiler/docgen.nim | 44 ++++++++++ compiler/pragmas.nim | 5 +- compiler/wordrecg.nim | 2 +- lib/packages/docutils/rst.nim | 2 +- .../test_doctype/expected/test_doctype.html | 83 +++++++++++++++++++ nimdoc/test_doctype/test_doctype.nim | 15 ++++ nimdoc/tester.nim | 11 ++- 7 files changed, 158 insertions(+), 4 deletions(-) create mode 100644 nimdoc/test_doctype/expected/test_doctype.html create mode 100644 nimdoc/test_doctype/test_doctype.nim diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 1c03762fddddb..875089c490cb3 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -293,6 +293,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if preferMarkdown: options.incl roPreferMarkdown if not standaloneDoc: options.incl roNimFile + # (options can be changed dynamically in `setDoctype` by `{.doctype.}`) result.sharedState = newRstSharedState( options, filename.string, docgenFindFile, compilerMsgHandler) @@ -1121,6 +1122,44 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = param["types"].add %($kind) result.json["signature"]["genericParams"].add param +proc setDoctype(d: PDoc, n: PNode) = + ## Processes `{.doctype.}` pragma changing Markdown/RST parsing options. + if n == nil: + return + if n.len != 2: + localError(d.conf, n.info, errUser, + "doctype pragma takes exactly 1 argument" + ) + return + var dt = "" + case n[1].kind + of nkStrLit: + dt = toLowerAscii(n[1].strVal) + of nkIdent: + dt = toLowerAscii(n[1].ident.s) + else: + localError(d.conf, n.info, errUser, + "unknown argument type $1 provided to doctype" % [$n[1].kind] + ) + return + case dt + of "markdown": + d.sharedState.options.incl roSupportMarkdown + d.sharedState.options.incl roPreferMarkdown + of "rstmarkdown": + d.sharedState.options.incl roSupportMarkdown + d.sharedState.options.excl roPreferMarkdown + of "rst": + d.sharedState.options.excl roSupportMarkdown + d.sharedState.options.excl roPreferMarkdown + else: + localError(d.conf, n.info, errUser, + ( + "unknown doctype value \"$1\", should be from " & + "\"RST\", \"Markdown\", \"RstMarkdown\"" + ) % [dt] + ) + proc checkForFalse(n: PNode): bool = result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0 @@ -1238,6 +1277,8 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = of nkPragma: let pragmaNode = findPragma(n, wDeprecated) d.modDeprecationMsg.add(genDeprecationMsg(d, pragmaNode)) + let doctypeNode = findPragma(n, wDoctype) + setDoctype(d, doctypeNode) of nkCommentStmt: d.modDescPre.add(genComment(d, n)) of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) @@ -1373,6 +1414,9 @@ proc add(d: PDoc; j: JsonItem) = proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = case n.kind + of nkPragma: + let doctypeNode = findPragma(n, wDoctype) + setDoctype(d, doctypeNode) of nkCommentStmt: if includeComments: d.add JsonItem(rst: genComment(d, n), rstField: "comment", diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 2e86b6c44af30..d5c7284180cf3 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -55,7 +55,7 @@ const wDeprecated, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, - wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert} + wExperimental, wDoctype, wThis, wUsed, wInvariant, wAssume, wAssert} stmtPragmasTopLevel* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, wStyleChecks, wAssertions, @@ -1216,6 +1216,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if not isTopLevel(c): localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment") processExperimental(c, it) + of wDoctype: + if not isTopLevel(c): + localError(c.config, n.info, "\"doctype\" pragma only valid as top-level statement") of wNoRewrite: noVal(c, it) of wBase: diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index ded02abe8c98b..71b2e6384b067 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -80,7 +80,7 @@ type wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic", wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd", - wComputedGoto = "computedGoto", wExperimental = "experimental", + wComputedGoto = "computedGoto", wExperimental = "experimental", wDoctype = "doctype", wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit", wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index b3adbf9de31c4..9bc3c604a81c3 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -586,7 +586,7 @@ type filenameToIdx*: Table[string, FileIndex] idxToFilename*: seq[string] RstSharedState = object - options: RstParseOptions # parsing options + options*: RstParseOptions # parsing options hLevels: LevelMap # hierarchy of heading styles hTitleCnt: int # =0 if no title, =1 if only main title, # =2 if both title and subtitle are present diff --git a/nimdoc/test_doctype/expected/test_doctype.html b/nimdoc/test_doctype/expected/test_doctype.html new file mode 100644 index 0000000000000..416541d4da66a --- /dev/null +++ b/nimdoc/test_doctype/expected/test_doctype.html @@ -0,0 +1,83 @@ + + + + + + + +nimdoc/test_doctype/test_doctype + + + + + + + + + + + + + + + + +
+
+

nimdoc/test_doctype/test_doctype

+
+
+
+ + +
+ +
+ Search: +
+
+ Group by: + +
+ + +
+
+ +
+ +

Check

+

+text

+ +

Check

+

text

+ +
+
+ + +
+
+ + + diff --git a/nimdoc/test_doctype/test_doctype.nim b/nimdoc/test_doctype/test_doctype.nim new file mode 100644 index 0000000000000..28d35fc1e8ce4 --- /dev/null +++ b/nimdoc/test_doctype/test_doctype.nim @@ -0,0 +1,15 @@ +# Markdown (default) interpretes this as text + a Markdown code block: + +## Check +## ~~~~~ +## text +## ~~~~~ + +{.doctype: RST.} + +# Now RST interpretes this as 2 headings: + +## Check +## ~~~~~ +## text +## ~~~~~ diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index 32dce07099cf1..ef82ae1b99e0c 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -36,7 +36,7 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = if nimBuildIndexSwitches != "": exec("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches]) - for expected in walkDirRec(prjDir / "expected/"): + for expected in walkDirRec(prjDir / "expected/", checkDir=true): let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir]) if not fileExists(produced): echo "FAILURE: files not found: ", produced @@ -79,5 +79,14 @@ let "$1/$2" % [test2Dir, test2DocsDir]]) testNimDoc(test2Dir, test2DocsDir, test2Switches, fixup) +# Test `nim doc` on file with `{.doctype.}` pragma +let + test3PrjDir = "test_doctype" + test3PrjName = "test_doctype" + test3Dir = baseDir / test3PrjDir + test3DocsDir = "htmldocs" + test3Switches = NimSwitches(doc: @["$1/$2.nim" % [test3Dir, test3PrjName]]) +testNimDoc(test3Dir, test3DocsDir, test3Switches, fixup) + if failures > 0: quit "$# failures occurred; see note in nimdoc/tester.nim regarding -d:nimTestsNimdocFixup" % $failures