fix RST parsing when no indent after enum.item (fix #17249) (#17257)

This commit is contained in:
Andrey Makarov 2021-03-12 10:33:21 +03:00 • committed by GitHub
commit d97bf4f1c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 82 additions and 28 deletions

View file

@ -1366,8 +1366,9 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
var d = newDocumentor(filen, cache, conf, outExt) var d = newDocumentor(filen, cache, conf, outExt)
d.isPureRst = true d.isPureRst = true
var rst = parseRst(readFile(filen.string), filen.string, 0, 1, d.hasToc, var rst = parseRst(readFile(filen.string), filen.string,
{roSupportRawDirective, roSupportMarkdown}, conf) line=LineRstInit, column=ColRstInit,
d.hasToc, {roSupportRawDirective, roSupportMarkdown}, conf)
var modDesc = newStringOfCap(30_000) var modDesc = newStringOfCap(30_000)
renderRstToOut(d[], rst, modDesc) renderRstToOut(d[], rst, modDesc)
d.modDesc = rope(modDesc) d.modDesc = rope(modDesc)

View file

@ -599,14 +599,16 @@ to existing modules is acceptable. For two reasons:
Conventions Conventions
----------- -----------
1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to require 1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to
users to import via `import std/foo` instead of `import foo`, which would cause require users to import via `import std/foo` instead of `import foo`,
potential conflicts with nimble packages. Note that this still applies for new modules which would cause potential conflicts with nimble packages.
in existing logical directories, e.g.: Note that this still applies for new modules in existing logical
use `lib/std/collections/foo.nim`, not `lib/pure/collections/foo.nim`. directories, e.g.: use `lib/std/collections/foo.nim`,
not `lib/pure/collections/foo.nim`.
2. New module names should prefer plural form whenever possible, e.g.: 2. New module names should prefer plural form whenever possible, e.g.:
`std/sums.nim` instead of `std/sum.nim`. In particular, this reduces chances of conflicts `std/sums.nim` instead of `std/sum.nim`. In particular, this reduces
between module name and the symbols it defines. Furthermore, module names should chances of conflicts between module name and the symbols it defines.
use `snake_case` and not use capital letters, which cause issues when going Furthermore, module names should use `snake_case` and not use capital
from an OS without case sensitivity to an OS with it. letters, which cause issues when going from an OS without case
sensitivity to an OS with it.

View file

@ -6073,13 +6073,14 @@ avoid ambiguity when there are multiple modules with the same path.
There are two pseudo directories: There are two pseudo directories:
1. ``std``: The ``std`` pseudo directory is the abstract location of Nim's standard 1. ``std``: The ``std`` pseudo directory is the abstract location of
library. For example, the syntax ``import std / strutils`` is used to unambiguously Nim's standard library. For example, the syntax ``import std / strutils``
refer to the standard library's ``strutils`` module. is used to unambiguously refer to the standard library's ``strutils`` module.
2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to a Nimble 2. ``pkg``: The ``pkg`` pseudo directory is used to unambiguously refer to
package. However, for technical details that lie outside the scope of this document, a Nimble package. However, for technical details that lie outside the
its semantics are: *Use the search path to look for module name but ignore the standard scope of this document, its semantics are: *Use the search path to look for
library locations*. In other words, it is the opposite of ``std``. module name but ignore the standard library locations*.
In other words, it is the opposite of ``std``.
From import statement From import statement

View file

@ -154,7 +154,7 @@
## The reasoning is as follows: ## The reasoning is as follows:
## 1. it's close to what many DBs offer natively (char**) ## 1. it's close to what many DBs offer natively (char**)
## 2. it hides the number of types that the DB supports ## 2. it hides the number of types that the DB supports
## (int? int64? decimal up to 10 places? geo coords?) ## (int? int64? decimal up to 10 places? geo coords?)
## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query) ## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query)
## ##
## See also ## See also

View file

@ -467,12 +467,20 @@ type
s*: PSharedState s*: PSharedState
indentStack*: seq[int] indentStack*: seq[int]
filename*: string filename*: string
line*, col*: int line*, col*: int ## initial line/column of whole text or
## documenation fragment that will be added
## in case of error/warning reporting to
## (relative) line/column of the token.
hasToc*: bool hasToc*: bool
curAnchor*: string # variable to track latest anchor in s.anchors curAnchor*: string # variable to track latest anchor in s.anchors
EParseError* = object of ValueError EParseError* = object of ValueError
const
LineRstInit* = 1 ## Initial line number for standalone RST text
ColRstInit* = 0 ## Initial column number for standalone RST text
## (Nim global reporting adds ColOffset=1)
template currentTok(p: RstParser): Token = p.tok[p.idx] template currentTok(p: RstParser): Token = p.tok[p.idx]
template prevTok(p: RstParser): Token = p.tok[p.idx - 1] template prevTok(p: RstParser): Token = p.tok[p.idx - 1]
template nextTok(p: RstParser): Token = p.tok[p.idx + 1] template nextTok(p: RstParser): Token = p.tok[p.idx + 1]
@ -542,8 +550,8 @@ proc initParser(p: var RstParser, sharedState: PSharedState) =
p.idx = 0 p.idx = 0
p.filename = "" p.filename = ""
p.hasToc = false p.hasToc = false
p.col = 0 p.col = ColRstInit
p.line = 1 p.line = LineRstInit
p.s = sharedState p.s = sharedState
proc addNodesAux(n: PRstNode, result: var string) = proc addNodesAux(n: PRstNode, result: var string) =
@ -1439,8 +1447,8 @@ proc countTitles(p: var RstParser, n: PRstNode) =
if p.s.hTitleCnt >= 2: if p.s.hTitleCnt >= 2:
break break
proc tokenAfterNewline(p: RstParser): int = proc tokenAfterNewline(p: RstParser, start: int): int =
result = p.idx result = start
while true: while true:
case p.tok[result].kind case p.tok[result].kind
of tkEof: of tkEof:
@ -1450,6 +1458,9 @@ proc tokenAfterNewline(p: RstParser): int =
break break
else: inc result else: inc result
proc tokenAfterNewline(p: RstParser): int {.inline.} =
result = tokenAfterNewline(p, p.idx)
proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool =
## check that underline/overline length is enough for the heading. ## check that underline/overline length is enough for the heading.
## No support for Unicode. ## No support for Unicode.
@ -1937,13 +1948,34 @@ proc parseEnumList(p: var RstParser): PRstNode =
wildToken: array[0..5, int] = [4, 3, 3, 4, 3, 3] # number of tokens wildToken: array[0..5, int] = [4, 3, 3, 4, 3, 3] # number of tokens
wildIndex: array[0..5, int] = [1, 0, 0, 1, 0, 0] wildIndex: array[0..5, int] = [1, 0, 0, 1, 0, 0]
# position of enumeration sequence (number/letter) in enumerator # position of enumeration sequence (number/letter) in enumerator
result = newRstNodeA(p, rnEnumList)
let col = currentTok(p).col let col = currentTok(p).col
var w = 0 var w = 0
while w < wildcards.len: while w < wildcards.len:
if match(p, p.idx, wildcards[w]): break if match(p, p.idx, wildcards[w]): break
inc w inc w
assert w < wildcards.len assert w < wildcards.len
proc checkAfterNewline(p: RstParser, report: bool): bool =
let j = tokenAfterNewline(p, start=p.idx+1)
if p.tok[j].kind notin {tkIndent, tkEof} and
p.tok[j].col < p.tok[p.idx+wildToken[w]].col and
(p.tok[j].col > col or
(p.tok[j].col == col and not match(p, j, wildcards[w]))):
if report:
let n = p.line + p.tok[j].line
let msg = "\n" & """
not enough indentation on line $2
(if it's continuation of enumeration list),
or no blank line after line $1 (if it should be the next paragraph),
or no escaping \ at the beginning of line $1
(if lines $1..$2 are a normal paragraph, not enum. list)""".
unindent(8)
rstMessage(p, mwRstStyle, msg % [$(n-1), $n])
result = false
else:
result = true
if not checkAfterNewline(p, report = true):
return nil
result = newRstNodeA(p, rnEnumList)
let autoEnums = if roSupportMarkdown in p.s.options: @["#", "1"] else: @["#"] let autoEnums = if roSupportMarkdown in p.s.options: @["#", "1"] else: @["#"]
var prevAE = "" # so as not allow mixing auto-enumerators `1` and `#` var prevAE = "" # so as not allow mixing auto-enumerators `1` and `#`
var curEnum = 1 var curEnum = 1
@ -1963,6 +1995,10 @@ proc parseEnumList(p: var RstParser): PRstNode =
result.add(item) result.add(item)
if currentTok(p).kind == tkIndent and currentTok(p).ival == col and if currentTok(p).kind == tkIndent and currentTok(p).ival == col and
match(p, p.idx+1, wildcards[w]): match(p, p.idx+1, wildcards[w]):
# don't report to avoid duplication of warning since for
# subsequent enum. items parseEnumList will be called second time:
if not checkAfterNewline(p, report = false):
break
let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol
# check that it's in sequence: enumerator == next(prevEnum) # check that it's in sequence: enumerator == next(prevEnum)
if "n" in wildcards[w]: # arabic numeral if "n" in wildcards[w]: # arabic numeral
@ -2336,7 +2372,8 @@ proc selectDir(p: var RstParser, d: string): PRstNode =
of "warning": result = dirAdmonition(p, d) of "warning": result = dirAdmonition(p, d)
of "default-role": result = dirDefaultRole(p) of "default-role": result = dirDefaultRole(p)
else: else:
rstMessage(p, meInvalidDirective, d) let tok = p.tok[p.idx-2] # report on directive in ".. directive::"
rstMessage(p, meInvalidDirective, d, tok.line, tok.col)
proc prefix(ftnType: FootnoteType): string = proc prefix(ftnType: FootnoteType): string =
case ftnType case ftnType

View file

@ -1506,7 +1506,8 @@ proc rstToHtml*(s: string, options: RstParseOptions,
initRstGenerator(d, outHtml, config, filen, options, myFindFile, initRstGenerator(d, outHtml, config, filen, options, myFindFile,
rst.defaultMsgHandler) rst.defaultMsgHandler)
var dummyHasToc = false var dummyHasToc = false
var rst = rstParse(s, filen, 0, 1, dummyHasToc, options) var rst = rstParse(s, filen, line=LineRstInit, column=ColRstInit,
dummyHasToc, options)
result = "" result = ""
renderRstToOut(d, rst, result) renderRstToOut(d, rst, result)
@ -1518,4 +1519,7 @@ proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline,
var option: bool var option: bool
var rstGenera: RstGenerator var rstGenera: RstGenerator
rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options) rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options)
rstGenera.renderRstToOut(rstParse(rstSource, "", 1, 1, option, options), result) rstGenera.renderRstToOut(
rstParse(rstSource, "", line=LineRstInit, column=ColRstInit,
option, options),
result)

View file

@ -708,6 +708,15 @@ Test1
doAssert count(output7, "<li>") == 3 doAssert count(output7, "<li>") == 3
doAssert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 doAssert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7
# check that it's not recognized as enum.list without indentation on 2nd line
let input8 = dedent """
A. string1
string2
"""
# TODO: find out hot to catch warning here instead of throwing a defect
expect(AssertionDefect):
let output8 = input8.toHtml
test "Markdown enumerated lists": test "Markdown enumerated lists":
let input1 = dedent """ let input1 = dedent """
Below are 2 enumerated lists: Markdown-style (5 items) and RST (1 item) Below are 2 enumerated lists: Markdown-style (5 items) and RST (1 item)