summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMiran <narimiran@disroot.org>2020-11-10 09:41:26 +0100
committerGitHub <noreply@github.com>2020-11-10 09:41:26 +0100
commitee78d7610853f4866c9b3d1e81d523b7ef339da0 (patch)
tree48fb70888df2e0dde545e9b40a25d39ad302d1a0
parentd8e7caf5dd4ab70b7429896c1587cde992e7855a (diff)
downloadNim-ee78d7610853f4866c9b3d1e81d523b7ef339da0.tar.gz
rst: add support for markdown tables (#15854)
* rst: add support for markdown tables

* change template into proc

* don't create unnecessary `seq[string]`
-rw-r--r--compiler/docgen.nim1
-rw-r--r--compiler/lineinfos.nim30
-rw-r--r--lib/packages/docutils/rst.nim73
-rw-r--r--lib/packages/docutils/rstast.nim2
-rw-r--r--lib/packages/docutils/rstgen.nim2
-rw-r--r--tests/stdlib/trstgen.nim24
6 files changed, 116 insertions, 16 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 7609c96ee..8f6b7a560 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -127,6 +127,7 @@ template declareClosures =
     of meCannotOpenFile: k = errCannotOpenFile
     of meExpected: k = errXExpected
     of meGridTableNotImplemented: k = errGridTableNotImplemented
+    of meMarkdownIllformedTable: k = errMarkdownIllformedTable
     of meNewSectionExpected: k = errNewSectionExpected
     of meGeneralParseError: k = errGeneralParseError
     of meInvalidDirective: k = errInvalidDirectiveX
diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim
index 759002312..f9ea90caf 100644
--- a/compiler/lineinfos.nim
+++ b/compiler/lineinfos.nim
@@ -30,42 +30,43 @@ type
     errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile,
     errXExpected,
     errGridTableNotImplemented,
+    errMarkdownIllformedTable,
     errGeneralParseError,
     errNewSectionExpected,
     errInvalidDirectiveX,
     errProveInit, # deadcode
     errGenerated,
     errUser,
-    
-    warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", 
+
+    warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape",
     warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit",
     warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated",
-    warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", 
-    warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", 
-    warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported", 
+    warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic",
+    warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX",
+    warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported",
     warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam",
-    warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap", 
+    warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap",
     warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport",
     warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple",
     warnUnsafeSetLen = "UnsafeSetLen", warnUnsafeDefault = "UnsafeDefault",
     warnProveInit = "ProveInit", warnProveField = "ProveField", warnProveIndex = "ProveIndex",
     warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode",
     warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2",
-    warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor", 
-    warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed", 
-    warnInconsistentSpacing = "Spacing",  warnCaseTransition = "CaseTransition", 
-    warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores", 
+    warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor",
+    warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed",
+    warnInconsistentSpacing = "Spacing",  warnCaseTransition = "CaseTransition",
+    warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores",
     warnUser = "User",
 
     hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC",
     hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed",
     hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded",
-    hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", 
-    hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin", 
+    hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX",
+    hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin",
     hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path",
-    hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name", 
+    hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name",
     hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency",
-    hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace", 
+    hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace",
     hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro",
     hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext",
     hintMsgOrigin = "MsgOrigin", # since 1.3.5
@@ -79,6 +80,7 @@ const
     errCannotOpenFile: "cannot open '$1'",
     errXExpected: "'$1' expected",
     errGridTableNotImplemented: "grid table is not implemented",
+    errMarkdownIllformedTable: "illformed delimiter row of a markdown table",
     errGeneralParseError: "general parse error",
     errNewSectionExpected: "new section expected",
     errInvalidDirectiveX: "invalid directive: '$1'",
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index 56bfe580b..c04a6e07c 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -36,6 +36,7 @@ type
     meCannotOpenFile,
     meExpected,
     meGridTableNotImplemented,
+    meMarkdownIllformedTable,
     meNewSectionExpected,
     meGeneralParseError,
     meInvalidDirective,
@@ -53,6 +54,7 @@ const
     meCannotOpenFile: "cannot open '$1'",
     meExpected: "'$1' expected",
     meGridTableNotImplemented: "grid table is not implemented",
+    meMarkdownIllformedTable: "illformed delimiter row of a markdown table",
     meNewSectionExpected: "new section expected",
     meGeneralParseError: "general parse error",
     meInvalidDirective: "invalid directive: '$1'",
@@ -1091,6 +1093,13 @@ proc isMarkdownHeadline(p: RstParser): bool =
       if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}:
         result = true
 
+proc findPipe(p: RstParser, start: int): bool =
+  var i = start
+  while true:
+    if p.tok[i].symbol == "|": return true
+    if p.tok[i].kind in {tkIndent, tkEof}: return false
+    inc i
+
 proc whichSection(p: RstParser): RstNodeKind =
   case p.tok[p.idx].kind
   of tkAdornment:
@@ -1104,6 +1113,9 @@ proc whichSection(p: RstParser): RstNodeKind =
   of tkPunct:
     if isMarkdownHeadline(p):
       result = rnHeadline
+    elif roSupportMarkdown in p.s.options and predNL(p) and
+        match(p, p.idx, "| w") and findPipe(p, p.idx+3):
+      result = rnMarkdownTable
     elif p.tok[p.idx].symbol == "```":
       result = rnCodeBlock
     elif match(p, tokenAfterNewline(p), "ai"):
@@ -1206,6 +1218,9 @@ proc parseHeadline(p: var RstParser): PRstNode =
 
 type
   IntSeq = seq[int]
+  ColumnLimits = tuple
+    first, last: int
+  ColSeq = seq[ColumnLimits]
 
 proc tokEnd(p: RstParser): int =
   result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
@@ -1280,6 +1295,63 @@ proc parseSimpleTable(p: var RstParser): PRstNode =
       add(a, b)
     add(result, a)
 
+proc readTableRow(p: var RstParser): ColSeq =
+  if p.tok[p.idx].symbol == "|": inc p.idx
+  while p.tok[p.idx].kind notin {tkIndent, tkEof}:
+    var limits: ColumnLimits
+    limits.first = p.idx
+    while p.tok[p.idx].kind notin {tkIndent, tkEof}:
+      if p.tok[p.idx].symbol == "|" and p.tok[p.idx-1].symbol != "\\": break
+      inc p.idx
+    limits.last = p.idx
+    result.add(limits)
+    if p.tok[p.idx].kind in {tkIndent, tkEof}: break
+    inc p.idx
+  p.idx = tokenAfterNewline(p)
+
+proc getColContents(p: var RstParser, colLim: ColumnLimits): string =
+  for i in colLim.first ..< colLim.last:
+    result.add(p.tok[i].symbol)
+  result.strip
+
+proc isValidDelimiterRow(p: var RstParser, colNum: int): bool =
+  let row = readTableRow(p)
+  if row.len != colNum: return false
+  for limits in row:
+    let content = getColContents(p, limits)
+    if content.len < 3 or not (content.startsWith("--") or content.startsWith(":-")):
+      return false
+  return true
+
+proc parseMarkdownTable(p: var RstParser): PRstNode =
+  var
+    row: ColSeq
+    colNum: int
+    a, b: PRstNode
+    q: RstParser
+  result = newRstNode(rnMarkdownTable)
+
+  proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) =
+    row = readTableRow(p)
+    if colNum == 0: colNum = row.len # table header
+    elif row.len < colNum: row.setLen(colNum)
+    a = newRstNode(rnTableRow)
+    for j in 0 ..< colNum:
+      b = newRstNode(cellKind)
+      initParser(q, p.s)
+      q.col = p.col
+      q.line = p.tok[p.idx].line - 1
+      q.filename = p.filename
+      q.col += getTokens(getColContents(p, row[j]), false, q.tok)
+      b.add(parseDoc(q))
+      a.add(b)
+    result.add(a)
+
+  parseRow(p, rnTableHeaderCell, result)
+  if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable)
+  while predNL(p) and p.tok[p.idx].symbol == "|":
+    parseRow(p, rnTableDataCell, result)
+
 proc parseTransition(p: var RstParser): PRstNode =
   result = newRstNode(rnTransition)
   inc(p.idx)
@@ -1461,6 +1533,7 @@ proc parseSection(p: var RstParser, result: PRstNode) =
     of rnHeadline: a = parseHeadline(p)
     of rnOverline: a = parseOverline(p)
     of rnTable: a = parseSimpleTable(p)
+    of rnMarkdownTable: a = parseMarkdownTable(p)
     of rnOptionList: a = parseOptionList(p)
     else:
       #InternalError("rst.parseSection()")
diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim
index 044ea2c14..8b2159aeb 100644
--- a/lib/packages/docutils/rstast.nim
+++ b/lib/packages/docutils/rstast.nim
@@ -37,7 +37,7 @@ type
     rnLineBlock,              # the | thingie
     rnLineBlockItem,          # sons of the | thing
     rnBlockQuote,             # text just indented
-    rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
+    rnTable, rnGridTable, rnMarkdownTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
     rnLabel,                  # used for footnotes and other things
     rnFootnote,               # a footnote
     rnCitation,               # similar to footnote
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index c70998edb..604cc3ca9 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -1094,7 +1094,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) =
   of rnBlockQuote:
     renderAux(d, n, "<blockquote><p>$1</p></blockquote>\n",
                     "\\begin{quote}$1\\end{quote}\n", result)
-  of rnTable, rnGridTable:
+  of rnTable, rnGridTable, rnMarkdownTable:
     renderAux(d, n,
       "<table border=\"1\" class=\"docutils\">$1</table>",
       "\\begin{table}\\begin{rsttab}{" &
diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim
index 8fdbf3911..d35bc5821 100644
--- a/tests/stdlib/trstgen.nim
+++ b/tests/stdlib/trstgen.nim
@@ -153,3 +153,27 @@ suite "YAML syntax highlighting":
     assert a == """(( <a class="reference external" href="https://nim-lang.org/">Nim</a> ))"""
     assert b == """((<a class="reference external" href="https://nim-lang.org/">Nim</a>))"""
     assert c == """[<a class="reference external" href="https://nim-lang.org/">Nim</a>]"""
+
+  test "Markdown tables":
+    let input1 = """
+| A1 header    | A2 \| not fooled
+| :---         | ----:       |
+| C1           | C2 **bold** | ignored |
+| D1 `code \|` | D2          | also ignored
+| E1 \| text   |
+|              | F2 without pipe
+not in table"""
+    let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig())
+    assert output1 == """<table border="1" class="docutils"><tr><th>A1 header</th><th>A2 | not fooled</th></tr>
+<tr><td>C1</td><td>C2 <strong>bold</strong></td></tr>
+<tr><td>D1 <tt class="docutils literal"><span class="pre">code |</span></tt></td><td>D2</td></tr>
+<tr><td>E1 | text</td><td></td></tr>
+<tr><td></td><td>F2 without pipe</td></tr>
+</table><p>not in table</p>
+"""
+    let input2 = """
+| A1 header | A2 |
+| --- | --- |"""
+    let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig())
+    assert output2 == """<table border="1" class="docutils"><tr><th>A1 header</th><th>A2</th></tr>
+</table>"""