summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAndrey Makarov <ph.makarov@gmail.com>2023-06-28 14:38:54 -0600
committerGitHub <noreply@github.com>2023-06-28 22:38:54 +0200
commit57de460437924a951d393ced8b7c88418fe2541a (patch)
treec46cd5150aaab8962c117de00faae2486b389bd8
parentb35942ef8386d082b8564b2aa62843a4949cdb60 (diff)
downloadNim-57de460437924a951d393ced8b7c88418fe2541a.tar.gz
Don't throw errors on RST tables in Markdown and RstMarkdown modes (#22165)
* Don't throw errors on RST tables in Markdown and RstMarkdown modes

Additions to RST simple tables (#19859) made their parsing more
restrictive, which can introduce problems with of some old
nimforum posts, which have tables with sloppily aligned columns
(like this one:
https://github.com/nim-lang/nimforum/issues/330#issuecomment-1376039966).
Also this strictness contradicts to Markdown style of not getting
in the way (ignoring errors).

So this PR proposes a new strategy of dealing with errors:
* In Markdown and legacy (old default) RstMarkdown we try to
  continue parsing, emitting only warnings
* And only in pure RST mode we throw a error

I expect that this strategy will be applied to more parts of markup code
in the future.

* Don't return anything in `checkColumns`
-rw-r--r--lib/packages/docutils/rst.nim44
-rw-r--r--tests/stdlib/trst.nim33
2 files changed, 62 insertions, 15 deletions
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index ae564b643..2894010ef 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -585,6 +585,29 @@ proc rstMessage(p: RstParser, msgKind: MsgKind) =
                              p.col + currentTok(p).col, msgKind,
                              currentTok(p).symbol)
 
+# Functions `isPureRst` & `stopOrWarn` address differences between
+# Markdown and RST:
+# * Markdown always tries to continue working. If it is really impossible
+#   to parse a markup element, its proc just returns `nil` and parsing
+#   continues for it as for normal text paragraph.
+#   The downside is that real mistakes/typos are often silently ignored.
+#   The same applies to legacy `RstMarkdown` mode for nimforum.
+# * RST really signals errors. The downside is that it's more intrusive -
+#   the user must escape special syntax with \ explicitly.
+#
+# TODO: we need to apply this strategy to all markup elements eventually.
+
+func isPureRst(p: RstParser): bool =
+  roSupportMarkdown notin p.s.options
+
+proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string) =
+  let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
+  rstMessage(p, realMsgKind, arg)
+
+proc stopOrWarn(p: RstParser, errorType: MsgKind, arg: string, line, col: int) =
+  let realMsgKind = if isPureRst(p): errorType else: mwRstStyle
+  rstMessage(p, realMsgKind, arg, line, col)
+
 proc currInd(p: RstParser): int =
   result = p.indentStack[high(p.indentStack)]
 
@@ -2596,11 +2619,11 @@ proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int =
 proc checkColumns(p: RstParser, cols: RstCols) =
   var i = p.idx
   if p.tok[i].symbol[0] != '=':
-    rstMessage(p, mwRstStyle,
+    stopOrWarn(p, meIllformedTable,
                "only tables with `=` columns specification are allowed")
   for col in 0 ..< cols.len:
     if tokEnd(p, i) != cols[col].stop:
-      rstMessage(p, meIllformedTable,
+      stopOrWarn(p, meIllformedTable,
                  "end of table column #$1 should end at position $2" % [
                    $(col+1), $(cols[col].stop+ColRstOffset)],
                  p.tok[i].line, tokEnd(p, i))
@@ -2609,12 +2632,12 @@ proc checkColumns(p: RstParser, cols: RstCols) =
       if p.tok[i].kind == tkWhite:
         inc i
       if p.tok[i].kind notin {tkIndent, tkEof}:
-        rstMessage(p, meIllformedTable, "extraneous column specification")
+        stopOrWarn(p, meIllformedTable, "extraneous column specification")
     elif p.tok[i].kind == tkWhite:
       inc i
     else:
-      rstMessage(p, meIllformedTable, "no enough table columns",
-                 p.tok[i].line, p.tok[i].col)
+      stopOrWarn(p, meIllformedTable,
+                 "no enough table columns", p.tok[i].line, p.tok[i].col)
 
 proc getSpans(p: RstParser, nextLine: int,
               cols: RstCols, unitedCols: RstCols): seq[int] =
@@ -2669,17 +2692,18 @@ proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNo
       if tokEnd(p) <= colEnd(nCell):
         if tokStart(p) < colStart(nCell):
           if currentTok(p).kind != tkWhite:
-            rstMessage(p, meIllformedTable,
+            stopOrWarn(p, meIllformedTable,
                        "this word crosses table column from the left")
-          else:
-            inc p.idx
+            row[nCell].add(currentTok(p).symbol)
         else:
           row[nCell].add(currentTok(p).symbol)
-          inc p.idx
+        inc p.idx
       else:
         if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite:
-          rstMessage(p, meIllformedTable,
+          stopOrWarn(p, meIllformedTable,
                      "this word crosses table column from the right")
+          row[nCell].add(currentTok(p).symbol)
+          inc p.idx
         inc nCell
     if currentTok(p).kind == tkIndent: inc p.idx
     if tokEnd(p) <= colEnd(0): break
diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim
index 5da5fc360..329adc101 100644
--- a/tests/stdlib/trst.nim
+++ b/tests/stdlib/trst.nim
@@ -29,7 +29,9 @@ import os
 import std/[assertions, syncio]
 
 const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}
+# legacy nimforum / old default mode:
 const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled}
+const pureRst = {roNimFile, roSandboxDisabled}
 
 proc toAst(input: string,
             rstOptions: RstParseOptions = preferMarkdown,
@@ -917,10 +919,31 @@ suite "RST tables":
         ======   ======
          Inputs  Output
         ======   ======
-        """.toAst(error=error) == "")
+        """.toAst(rstOptions = pureRst, error = error) == "")
     check(error[] == "input(2, 2) Error: Illformed table: " &
                      "this word crosses table column from the right")
 
+    # In nimforum compatibility mode & Markdown we raise a warning instead:
+    let expected = dedent"""
+      rnTable  colCount=2
+        rnTableRow
+          rnTableDataCell
+            rnLeaf  'Inputs'
+          rnTableDataCell
+            rnLeaf  'Output'
+      """
+    for opt in [preferRst, preferMarkdown]:
+      var warnings = new seq[string]
+
+      check(
+        dedent"""
+          ======   ======
+           Inputs  Output
+          ======   ======
+          """.toAst(rstOptions = opt, warnings = warnings) == expected)
+      check(warnings[] == @[
+        "input(2, 2) Warning: RST style: this word crosses table column from the right"])
+
   test "tables with slightly overflowed cells cause an error (2)":
     var error = new string
     check("" == dedent"""
@@ -929,7 +952,7 @@ suite "RST tables":
       =====  =====  ======
       False  False  False
       =====  =====  ======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(2, 8) Error: Illformed table: " &
                      "this word crosses table column from the right")
 
@@ -941,7 +964,7 @@ suite "RST tables":
       =====  =====  ======
       False  False  False
       =====  =====  ======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(2, 7) Error: Illformed table: " &
                      "this word crosses table column from the left")
 
@@ -954,7 +977,7 @@ suite "RST tables":
       =====  ======
       False  False
       =====  =======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(5, 14) Error: Illformed table: " &
                      "end of table column #2 should end at position 13")
 
@@ -966,7 +989,7 @@ suite "RST tables":
       =====  =======
       False  False
       =====  ======
-      """.toAst(error=error))
+      """.toAst(rstOptions = pureRst, error = error))
     check(error[] == "input(3, 14) Error: Illformed table: " &
                      "end of table column #2 should end at position 13")