From 926ae2ec6451a2c37a6e7f9c0f0a5c8f428fd5f3 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 8 May 2020 18:48:44 -0600 Subject: [PATCH] Add ability for rst parser to continue on errors --- compiler/commands.nim | 2 ++ compiler/docgen.nim | 14 ++++++++-- compiler/options.nim | 1 + doc/advopt.txt | 1 + lib/packages/docutils/rst.nim | 51 ++++++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 949d6cae1..9a7052009 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -437,6 +437,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.docSeeSrcUrl = arg of "docroot": conf.docRoot = if arg.len == 0: "@default" else: arg + of "docignoreparseerrors": + conf.docIgnoreParseErrors = true of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a42be9a9c..b1cde838d 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -146,8 +146,13 @@ proc parseRst(text, filename: string, rstOptions: RstParseOptions; conf: ConfigRef): PRstNode = declareClosures() + var msgHandler: MsgHandler + if conf.docIgnoreParseErrors: + msgHandler = rst.noExceptionMsgHandler + else: + msgHandler = compilerMsgHandler result = rstParse(text, filename, line, column, hasToc, rstOptions, - docgenFindFile, compilerMsgHandler) + docgenFindFile, msgHandler) proc getOutFile2(conf: ConfigRef; filename: RelativeFile, ext: string, dir: RelativeDir; guessTarget: bool): AbsoluteFile = @@ -170,9 +175,14 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.module = module result.conf = conf result.cache = cache + var msgHandler: MsgHandler + if conf.docIgnoreParseErrors: + msgHandler = rst.noExceptionMsgHandler + else: + msgHandler = defaultMsgHandler initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown}, - docgenFindFile, compilerMsgHandler) + docgenFindFile, msgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): result.analytics = """ diff --git a/compiler/options.nim b/compiler/options.nim index 5475bc07b..a9b3be519 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -274,6 +274,7 @@ type docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ # The string uses the formatting variables `path` and `line`. docRoot*: string ## see nim --fullhelp for --docRoot + docIgnoreParseErrors*: bool ## continue parsing docs if errors occur # the used compiler cIncludes*: seq[AbsoluteDir] # directories to search for included files diff --git a/doc/advopt.txt b/doc/advopt.txt index c877b02e9..42ff18cd2 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -79,6 +79,7 @@ Advanced options: --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands (see doc.item.seesrc in config/nimdoc.cfg) --docInternal also generate documentation for non-exported symbols + --docIgnoreParseErrors continue parsing docs if rst errors occur --lineDir:on|off generation of #line directive on|off --embedsrc:on|off embeds the original source code as comments in the generated output diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 50b855662..56772ac89 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -312,6 +312,14 @@ proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, if mc == mcError: raise newException(EParseError, message) else: writeLine(stdout, message) +proc noExceptionMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, + arg: string) {.procvar.} = + let mc = msgkind.whichMsgClass + let a = messages[msgkind] % arg + let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a] + if mc == mcError: writeLine(stderr, message) + else: writeLine(stdout, message) + proc defaultFindFile*(filename: string): string {.procvar.} = if existsFile(filename): result = filename else: result = "" @@ -755,13 +763,23 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, inc(p.idx) if p.tok[p.idx].kind == tkIndent: rstMessage(p, meExpected, postfix, line, col) + add(father, newLeaf(p)) + inc(p.idx) break of tkWhite: add(father, newRstNode(rnLeaf, " ")) inc(p.idx) - else: rstMessage(p, meExpected, postfix, line, col) + else: + rstMessage(p, meExpected, postfix, line, col) + add(father, newLeaf(p)) + inc(p.idx) + break proc parseMarkdownCodeblock(p: var RstParser): PRstNode = + let backupIndex = p.idx + + # skip ``` + inc(p.idx) var args = newRstNode(rnDirArg) if p.tok[p.idx].kind == tkWord: add(args, newLeaf(p)) @@ -773,7 +791,10 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = case p.tok[p.idx].kind of tkEof: rstMessage(p, meExpected, "```") - break + p.idx = backupIndex + result = newLeaf(p) + inc(p.idx) + return of tkPunct: if p.tok[p.idx].symbol == "```": inc(p.idx) @@ -817,36 +838,47 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = result = true proc parseInline(p: var RstParser, father: PRstNode) = + template addOrBackup(endTok) = + let prevTok = if (p.idx-1) < len(p.tok): p.tok[p.idx-1].symbol else: "" + + if p.idx >= len(p.tok) - 1 and prevTok != endTok: + # we failed, so + p.idx = backupIndex + add(father, newLeaf(p)) + inc(p.idx) + else: + add(father, n) + + let backupIndex = p.idx case p.tok[p.idx].kind of tkPunct: if isInlineMarkupStart(p, "***"): var n = newRstNode(rnTripleEmphasis) parseUntil(p, n, "***", true) - add(father, n) + addOrBackup("***") elif isInlineMarkupStart(p, "**"): var n = newRstNode(rnStrongEmphasis) parseUntil(p, n, "**", true) - add(father, n) + addOrBackup("**") elif isInlineMarkupStart(p, "*"): var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) - add(father, n) + addOrBackup("*") elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "```": - inc(p.idx) add(father, parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) - add(father, n) + addOrBackup("``") elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) parseUntil(p, n, "`", true) n = parsePostfix(p, n) - add(father, n) + addOrBackup("`") elif isInlineMarkupStart(p, "|"): var n = newRstNode(rnSubstitutionReferences) parseUntil(p, n, "|", false) - add(father, n) + addOrBackup("|") elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "[" and p.tok[p.idx+1].symbol != "[" and parseMarkdownLink(p, father): @@ -1773,6 +1805,7 @@ proc parseDotDot(p: var RstParser): PRstNode = b = dirImage(p) else: rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol) + inc(p.idx) setSub(p, addNodes(a), b) elif match(p, p.idx, " ["): # footnotes, citations