summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/docgen.nim1
-rw-r--r--compiler/msgs.nim7
-rw-r--r--doc/tut2.txt102
-rw-r--r--lib/packages/docutils/rst.nim86
-rw-r--r--lib/packages/docutils/rstgen.nim117
-rw-r--r--lib/system.nim49
6 files changed, 267 insertions, 95 deletions
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 8bd0bb490..35acf1379 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -41,6 +41,7 @@ proc compilerMsgHandler(filename: string, line, col: int,
   of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel
   of mwUnknownSubstitution: k = warnUnknownSubstitutionX
   of mwUnsupportedLanguage: k = warnLanguageXNotSupported
+  of mwUnsupportedField: k = warnFieldXNotSupported
   globalError(newLineInfo(filename, line, col), k, arg)
 
 proc docgenFindFile(s: string): string {.procvar.} =
diff --git a/compiler/msgs.nim b/compiler/msgs.nim
index 51d2836f8..d40d94a71 100644
--- a/compiler/msgs.nim
+++ b/compiler/msgs.nim
@@ -114,7 +114,8 @@ type
     warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, 
     warnDeprecated, warnConfigDeprecated,
     warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, 
-    warnUnknownSubstitutionX, warnLanguageXNotSupported, warnCommentXIgnored, 
+    warnUnknownSubstitutionX, warnLanguageXNotSupported,
+    warnFieldXNotSupported, warnCommentXIgnored, 
     warnNilStatement, warnAnalysisLoophole,
     warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode,
     warnEachIdentIsTuple, warnShadowIdent, 
@@ -375,6 +376,7 @@ const
     warnRedefinitionOfLabel: "redefinition of label \'$1\' [RedefinitionOfLabel]", 
     warnUnknownSubstitutionX: "unknown substitution \'$1\' [UnknownSubstitutionX]", 
     warnLanguageXNotSupported: "language \'$1\' not supported [LanguageXNotSupported]", 
+    warnFieldXNotSupported: "field \'$1\' not supported [FieldXNotSupported]", 
     warnCommentXIgnored: "comment \'$1\' ignored [CommentXIgnored]", 
     warnNilStatement: "'nil' statement is deprecated; use an empty 'discard' statement instead [NilStmt]", 
     warnAnalysisLoophole: "thread analysis incomplete due to unknown call '$1' [AnalysisLoophole]",
@@ -415,7 +417,8 @@ const
     "XIsNeverRead", "XmightNotBeenInit",
     "Deprecated", "ConfigDeprecated",
     "SmallLshouldNotBeUsed", "UnknownMagic", 
-    "RedefinitionOfLabel", "UnknownSubstitutionX", "LanguageXNotSupported", 
+    "RedefinitionOfLabel", "UnknownSubstitutionX",
+    "LanguageXNotSupported", "FieldXNotSupported",
     "CommentXIgnored", "NilStmt",
     "AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
     "UnsafeCode", "EachIdentIsTuple", "ShadowIdent", 
diff --git a/doc/tut2.txt b/doc/tut2.txt
index 34f59b1cb..d8f9071a9 100644
--- a/doc/tut2.txt
+++ b/doc/tut2.txt
@@ -791,9 +791,9 @@ to be included along the program containing the license information::
 The ``readCfgAtRuntime`` proc will open the given filename and return a
 ``Table`` from the `tables module <tables.html>`_. The parsing of the file is
 done (without much care for handling invalid data or corner cases) using the
-``splitLines`` proc from the `strutils module <strutils.html>`_. There are many
-things which can fail; mind the purpose is explaining how to make this run at
-compile time, not how to properly implement a DRM scheme.
+`splitLines proc from the strutils module <strutils.html#splitLines>`_. There
+are many things which can fail; mind the purpose is explaining how to make
+this run at compile time, not how to properly implement a DRM scheme.
 
 The reimplementation of this code as a compile time proc will allow us to get
 rid of the ``data.cfg`` file we would need to distribute along the binary, plus
@@ -816,6 +816,8 @@ time string with the *generated source code*, which we then pass to the
 modified source code implementing the macro:
 
 .. code-block:: nim
+   :number-lines:
+
   import macros, strutils
 
   macro readCfgAndBuildSource(cfgFilename: string): stmt =
@@ -842,29 +844,31 @@ modified source code implementing the macro:
     echo cfglicenseKey
     echo cfgversion
 
-The good news is not much has changed! First, we need to change the handling of
-the input parameter. In the dynamic version the ``readCfgAtRuntime`` proc
-receives a string parameter. However, in the macro version it is also declared
-as string, but this is the *outside* interface of the macro.  When the macro is
-run, it actually gets a ``PNimNode`` object instead of a string, and we have
-to call the ``strVal`` proc from the `macros module <macros.html>`_ to obtain
-the string being passed in to the macro.
-
-Second, we cannot use the ``readFile`` proc from the `system module
-<system.html>`_ due to FFI restriction at compile time. If we try to use this
-proc, or any other which depends on FFI, the compiler will error with the
-message ``cannot evaluate`` and a dump of the macro's source code, along with a
-stack trace where the compiler reached before bailing out. We can get around
-this limitation by using the ``slurp`` proc from the `system module
-<system.html>`_, which was precisely made for compilation time (just like
-``gorge`` which executes an external program and captures its output).
-
-The interesting thing is that our macro does not return a runtime ``Table``
-object. Instead, it builds up Nim source code into the ``source`` variable.
-For each line of the configuration file a ``const`` variable will be generated.
-To avoid conflicts we prefix these variables with ``cfg``. In essence, what the
-compiler is doing is replacing the line calling the macro with the following
-snippet of code:
+The good news is not much has changed! First, we need to change the handling
+of the input parameter (line 3). In the dynamic version the
+``readCfgAtRuntime`` proc receives a string parameter. However, in the macro
+version it is also declared as string, but this is the *outside* interface of
+the macro.  When the macro is run, it actually gets a ``PNimNode`` object
+instead of a string, and we have to call the `strVal proc
+<macros.html#strVal>`_ (line 5) from the `macros module <macros.html>`_ to
+obtain the string being passed in to the macro.
+
+Second, we cannot use the `readFile proc <system.html#readFile>`_ from the
+`system module <system.html>`_ due to FFI restriction at compile time. If we
+try to use this proc, or any other which depends on FFI, the compiler will
+error with the message ``cannot evaluate`` and a dump of the macro's source
+code, along with a stack trace where the compiler reached before bailing out.
+We can get around this limitation by using the `slurp proc
+<system.html#slurp>`_ from the `system module <system.html>`_, which was
+precisely made for compilation time (just like `gorge <system.html#gorge>`_
+which executes an external program and captures its output).
+
+The interesting thing is that our macro does not return a runtime `Table
+<tables.html#TTable>`_ object. Instead, it builds up Nim source code into
+the ``source`` variable.  For each line of the configuration file a ``const``
+variable will be generated (line 15).  To avoid conflicts we prefix these
+variables with ``cfg``. In essence, what the compiler is doing is replacing
+the line calling the macro with the following snippet of code:
 
 .. code-block:: nim
   const cfgversion= "1.1"
@@ -873,13 +877,14 @@ snippet of code:
 
 You can verify this yourself adding the line ``echo source`` somewhere at the
 end of the macro and compiling the program. Another difference is that instead
-of calling the usual ``quit`` proc to abort (which we could still call) this
-version calls the ``error`` proc. The ``error`` proc has the same behavior as
-``quit`` but will dump also the source and file line information where the
-error happened, making it easier for the programmer to find where compilation
-failed. In this situation it would point to the line invoking the macro, but
-**not** the line of ``data.cfg`` we are processing, that's something the macro
-itself would need to control.
+of calling the usual `quit proc <system.html#quit>`_ to abort (which we could
+still call) this version calls the `error proc <macros.html#error>`_ (line
+14). The ``error`` proc has the same behavior as ``quit`` but will dump also
+the source and file line information where the error happened, making it
+easier for the programmer to find where compilation failed. In this situation
+it would point to the line invoking the macro, but **not** the line of
+``data.cfg`` we are processing, that's something the macro itself would need
+to control.
 
 
 Generating AST by hand
@@ -887,11 +892,11 @@ Generating AST by hand
 
 To generate an AST we would need to intimately know the structures used by the
 Nim compiler exposed in the `macros module <macros.html>`_, which at first
-look seems a daunting task. But we can use as helper shortcut the ``dumpTree``
-macro, which is used as a statement macro instead of an expression macro.
-Since we know that we want to generate a bunch of ``const`` symbols we can
-create the following source file and compile it to see what the compiler
-*expects* from us:
+look seems a daunting task. But we can use as helper shortcut the `dumpTree
+macro <macros.html#dumpTree>`_, which is used as a statement macro instead of
+an expression macro.  Since we know that we want to generate a bunch of
+``const`` symbols we can create the following source file and compile it to
+see what the compiler *expects* from us:
 
 .. code-block:: nim
   import macros
@@ -937,6 +942,8 @@ with this knowledge, let's look at the finished version of the AST building
 macro:
 
 .. code-block:: nim
+   :number-lines:
+
   import macros, strutils
 
   macro readCfgAndBuildAST(cfgFilename: string): stmt =
@@ -972,19 +979,20 @@ Since we are building on the previous example generating source code, we will
 only mention the differences to it. Instead of creating a temporary ``string``
 variable and writing into it source code as if it were written *by hand*, we
 use the ``result`` variable directly and create a statement list node
-(``nnkStmtList``) which will hold our children.
+(``nnkStmtList``) which will hold our children (line 7).
 
 For each input line we have to create a constant definition (``nnkConstDef``)
 and wrap it inside a constant section (``nnkConstSection``). Once these
-variables are created, we fill them hierarchichally like the previous AST dump
-tree showed: the constant definition is a child of the section definition, and
-the constant definition has an identifier node, an empty node (we let the
-compiler figure out the type), and a string literal with the value.
+variables are created, we fill them hierarchichally (line 17) like the
+previous AST dump tree showed: the constant definition is a child of the
+section definition, and the constant definition has an identifier node, an
+empty node (we let the compiler figure out the type), and a string literal
+with the value.
 
 A last tip when writing a macro: if you are not sure the AST you are building
 looks ok, you may be tempted to use the ``dumpTree`` macro. But you can't use
 it *inside* the macro you are writting/debugging. Instead ``echo`` the string
-generated by ``treeRepr``. If at the end of the this example you add ``echo
-treeRepr(result)`` you should get the same output as using the ``dumpTree``
-macro, but of course you can call that at any point of the macro where you
-might be having troubles.
+generated by `treeRepr <macros.html#treeRepr>`_. If at the end of the this
+example you add ``echo treeRepr(result)`` you should get the same output as
+using the ``dumpTree`` macro, but of course you can call that at any point of
+the macro where you might be having troubles.
diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim
index b21d51c93..07014c754 100644
--- a/lib/packages/docutils/rst.nim
+++ b/lib/packages/docutils/rst.nim
@@ -39,7 +39,8 @@ type
     meInvalidDirective,
     mwRedefinitionOfLabel,
     mwUnknownSubstitution,
-    mwUnsupportedLanguage
+    mwUnsupportedLanguage,
+    mwUnsupportedField
   
   TMsgHandler* = proc (filename: string, line, col: int, msgKind: TMsgKind,
                        arg: string) {.nimcall.} ## what to do in case of an error
@@ -55,7 +56,8 @@ const
     meInvalidDirective: "invalid directive: '$1'",
     mwRedefinitionOfLabel: "redefinition of label '$1'", 
     mwUnknownSubstitution: "unknown substitution '$1'",
-    mwUnsupportedLanguage: "language '$1' not supported"
+    mwUnsupportedLanguage: "language '$1' not supported",
+    mwUnsupportedField: "field '$1' not supported"
   ]
 
 proc rstnodeToRefname*(n: PRstNode): string
@@ -850,13 +852,13 @@ proc parseComment(p: var TRstParser): PRstNode =
 
 type 
   TDirKind = enum             # must be ordered alphabetically!
-    dkNone, dkAuthor, dkAuthors, dkCodeBlock, dkContainer, dkContents,
+    dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
     dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
 
 const 
-  DirIds: array[0..11, string] = ["", "author", "authors", "code-block", 
-    "container", "contents", "figure", "image", "include", "index", "raw", 
-    "title"]
+  DirIds: array[0..12, string] = ["", "author", "authors", "code",
+    "code-block", "container", "contents", "figure", "image", "include",
+    "index", "raw", "title"]
 
 proc getDirKind(s: string): TDirKind = 
   let i = find(DirIds, s)
@@ -876,7 +878,10 @@ proc parseUntilNewline(p: var TRstParser, father: PRstNode) =
     of tkEof, tkIndent: break
   
 proc parseSection(p: var TRstParser, result: PRstNode)
-proc parseField(p: var TRstParser): PRstNode = 
+proc parseField(p: var TRstParser): PRstNode =
+  ## Returns a parsed rnField node.
+  ##
+  ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
   result = newRstNode(rnField)
   var col = p.tok[p.idx].col
   var fieldname = newRstNode(rnFieldName)
@@ -892,7 +897,11 @@ proc parseField(p: var TRstParser): PRstNode =
   add(result, fieldname)
   add(result, fieldbody)
 
-proc parseFields(p: var TRstParser): PRstNode = 
+proc parseFields(p: var TRstParser): PRstNode =
+  ## Parses fields for a section or directive block.
+  ##
+  ## This proc may return nil if the parsing doesn't find anything of value,
+  ## otherwise it will return a node of rnFieldList type with children.
   result = nil
   var atStart = p.idx == 0 and p.tok[0].symbol == ":"
   if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx + 1].symbol == ":") or
@@ -908,6 +917,18 @@ proc parseFields(p: var TRstParser): PRstNode =
       else: 
         break 
   
+proc getFieldValue*(n: PRstNode): string =
+  ## Returns the value of a specific ``rnField`` node.
+  ##
+  ## This proc will assert if the node is not of the expected type. The empty
+  ## string will be returned as a minimum. Any value in the rst will be
+  ## stripped form leading/trailing whitespace.
+  assert n.kind == rnField
+  assert n.len == 2
+  assert n.sons[0].kind == rnFieldName
+  assert n.sons[1].kind == rnFieldBody
+  result = addNodes(n.sons[1]).strip
+
 proc getFieldValue(n: PRstNode, fieldname: string): string = 
   result = ""
   if n.sons[1] == nil: return 
@@ -1387,7 +1408,16 @@ type
   TDirFlags = set[TDirFlag]
   TSectionParser = proc (p: var TRstParser): PRstNode {.nimcall.}
 
-proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode = 
+proc parseDirective(p: var TRstParser, flags: TDirFlags): PRstNode =
+  ## Parses arguments and options for a directive block.
+  ##
+  ## A directive block will always have three sons: the arguments for the
+  ## directive (rnDirArg), the options (rnFieldList) and the block
+  ## (rnLineBlock). This proc parses the two first nodes, the block is left to
+  ## the outer `parseDirective` call.
+  ##
+  ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to
+  ## check them before accessing.
   result = newRstNode(rnDirective)
   var args: PRstNode = nil
   var options: PRstNode = nil
@@ -1421,6 +1451,9 @@ proc indFollows(p: TRstParser): bool =
   
 proc parseDirective(p: var TRstParser, flags: TDirFlags, 
                     contentParser: TSectionParser): PRstNode = 
+  ## Returns a generic rnDirective tree.
+  ##
+  ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
   result = parseDirective(p, flags)
   if not isNil(contentParser) and indFollows(p): 
     pushInd(p, p.tok[p.idx].ival)
@@ -1474,7 +1507,23 @@ proc dirInclude(p: var TRstParser): PRstNode =
       #  InternalError("Too many binary zeros in include file")
       result = parseDoc(q)
 
-proc dirCodeBlock(p: var TRstParser): PRstNode = 
+proc dirCodeBlock(p: var TRstParser, nimrodExtension = false): PRstNode =
+  ## Parses a code block.
+  ##
+  ## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the
+  ## description of ``parseDirective`` for further structure information.
+  ##
+  ## Code blocks can come in two forms, the standard `code directive
+  ## <http://docutils.sourceforge.net/docs/ref/rst/directives.html#code>`_ and
+  ## the nimrod extension ``.. code-block::``. If the block is an extension, we
+  ## want the default language syntax highlighting to be Nimrod, so we create a
+  ## fake internal field to comminicate with the generator. The field is named
+  ## ``default-language``, which is unlikely to collide with a field specified
+  ## by any random rst input file.
+  ##
+  ## As an extension this proc will process the ``file`` extension field and if
+  ## present will replace the code block with the contents of the referenced
+  ## file.
   result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock)
   var filename = strip(getFieldValue(result, "file"))
   if filename != "": 
@@ -1483,6 +1532,20 @@ proc dirCodeBlock(p: var TRstParser): PRstNode =
     var n = newRstNode(rnLiteralBlock)
     add(n, newRstNode(rnLeaf, readFile(path)))
     result.sons[2] = n
+
+  # Extend the field block if we are using our custom extension.
+  if nimrodExtension:
+    # Create a field block if the input block didn't have any.
+    if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList)
+    assert result.sons[1].kind == rnFieldList
+    # Hook the extra field and specify the Nimrod language as value.
+    var extraNode = newRstNode(rnField)
+    extraNode.add(newRstNode(rnFieldName))
+    extraNode.add(newRstNode(rnFieldBody))
+    extraNode.sons[0].add(newRstNode(rnLeaf, "default-language"))
+    extraNode.sons[1].add(newRstNode(rnLeaf, "Nimrod"))
+    result.sons[1].add(extraNode)
+
   result.kind = rnCodeBlock
 
 proc dirContainer(p: var TRstParser): PRstNode = 
@@ -1566,7 +1629,8 @@ proc parseDotDot(p: var TRstParser): PRstNode =
         result = dirRaw(p)
       else:
         rstMessage(p, meInvalidDirective, d)
-    of dkCodeBlock: result = dirCodeBlock(p)
+    of dkCode: result = dirCodeBlock(p)
+    of dkCodeBlock: result = dirCodeBlock(p, nimrodExtension = true)
     of dkIndex: result = dirIndex(p)
     else: rstMessage(p, meInvalidDirective, d)
     popInd(p)
diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim
index 02b0afd2f..cfbc4a708 100644
--- a/lib/packages/docutils/rstgen.nim
+++ b/lib/packages/docutils/rstgen.nim
@@ -24,7 +24,7 @@
 ## generate `LaTeX documents <https://en.wikipedia.org/wiki/LaTeX>`_ too.
 
 import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils,
-  algorithm
+  algorithm, parseutils
 
 const
   HtmlExt = "html"
@@ -63,6 +63,19 @@ type
   
   PDoc = var TRstGenerator ## Alias to type less.
 
+  CodeBlockParams = object ## Stores code block params.
+    numberLines: bool ## True if the renderer has to show line numbers.
+    startLine: int ## The starting line of the code block, by default 1.
+    langStr: string ## Input string used to specify the language.
+    lang: TSourceLanguage ## Type of highlighting, by default none.
+
+
+proc init(p: var CodeBlockParams) =
+  ## Default initialisation of CodeBlockParams to sane values.
+  p.startLine = 1
+  p.lang = langNone
+  p.langStr = ""
+
 proc initRstGenerator*(g: var TRstGenerator, target: TOutputTarget,
                        config: StringTableRef, filename: string,
                        options: TRstParseOptions,
@@ -761,26 +774,102 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) =
         height="17" hspace="2" vspace="2" />""",
     "\\includegraphics{$1}", [n.text])
   
+proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) =
+  ## Parses useful fields which can appear before a code block.
+  ##
+  ## This supports the special ``default-language`` internal string generated
+  ## by the ``rst`` module to communicate a specific default language.
+  case n.getArgument.toLower
+  of "number-lines":
+    params.numberLines = true
+    # See if the field has a parameter specifying a different line than 1.
+    var number: int
+    if parseInt(n.getFieldValue, number) > 0:
+      params.startLine = number
+  of "file":
+    # The ``file`` option is a Nimrod extension to the official spec, it acts
+    # like it would for other directives like ``raw`` or ``cvs-table``. This
+    # field is dealt with in ``rst.nim`` which replaces the existing block with
+    # the referenced file, so we only need to ignore it here to avoid incorrect
+    # warning messages.
+    discard
+  of "default-language":
+    params.langStr = n.getFieldValue.strip
+    params.lang = params.langStr.getSourceLanguage
+  else:
+    d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument)
+
+proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams =
+  ## Iterates over all code block fields and returns processed params.
+  ##
+  ## Also processes the argument of the directive as the default language. This
+  ## is done last so as to override any internal communication field variables.
+  result.init
+  if n.isNil:
+    return
+  assert n.kind == rnCodeBlock
+  assert(not n.sons[2].isNil)
+
+  # Parse the field list for rendering parameters if there are any.
+  if not n.sons[1].isNil:
+    for son in n.sons[1].sons: d.parseCodeBlockField(son, result)
+
+  # Parse the argument and override the language.
+  result.langStr = strip(getArgument(n))
+  if result.langStr != "":
+    result.lang = getSourceLanguage(result.langStr)
+
+proc buildLinesHTMLTable(params: CodeBlockParams, code: string):
+    tuple[beginTable, endTable: string] =
+  ## Returns the necessary tags to start/end a code block in HTML.
+  ##
+  ## If the numberLines has not been used, the tags will default to a simple
+  ## <pre> pair. Otherwise it will build a table and insert an initial column
+  ## with all the line numbers, which requires you to pass the `code` to detect
+  ## how many lines have to be generated (and starting at which point!).
+  if not params.numberLines:
+    result = ("<pre>", "</pre>")
+    return
+
+  var codeLines = 1 + code.strip.countLines
+  assert codeLines > 0
+  result.beginTable = """<table><tbody><tr><td class="blob-line-nums"><pre>"""
+  var line = params.startLine
+  while codeLines > 0:
+    result.beginTable.add($line & "\n")
+    line.inc
+    codeLines.dec
+  result.beginTable.add("</pre></td><td><pre>")
+  result.endTable = "</pre></td></tr></tbody></table>"
+
 proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
+  ## Renders a code block, appending it to `result`.
+  ##
+  ## If the code block uses the ``number-lines`` option, a table will be
+  ## generated with two columns, the first being a list of numbers and the
+  ## second the code block itself. The code block can use syntax highlighting,
+  ## which depends on the directive argument specified by the rst input, and
+  ## may also come from the parser through the internal ``default-language``
+  ## option to differentiate between a plain code block and nimrod's code block
+  ## extension.
+  assert n.kind == rnCodeBlock
   if n.sons[2] == nil: return
+  var params = d.parseCodeBlockParams(n)
   var m = n.sons[2].sons[0]
   assert m.kind == rnLeaf
-  var langstr = strip(getArgument(n))
-  var lang: TSourceLanguage
-  if langstr == "":
-    lang = langNim         # default language
-  else:
-    lang = getSourceLanguage(langstr)
-  
-  dispA(d.target, result, "<pre>", "\\begin{rstpre}\n", [])
-  if lang == langNone:
-    d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, langstr)
+
+  let (blockStart, blockEnd) = params.buildLinesHTMLTable(m.text)
+
+  dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
+  if params.lang == langNone:
+    if len(params.langStr) > 0:
+      d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr)
     for letter in m.text: escChar(d.target, result, letter)
   else:
     var g: TGeneralTokenizer
     initGeneralTokenizer(g, m.text)
     while true: 
-      getNextToken(g, lang)
+      getNextToken(g, params.lang)
       case g.kind
       of gtEof: break 
       of gtNone, gtWhitespace: 
@@ -790,8 +879,8 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
           esc(d.target, substr(m.text, g.start, g.length+g.start-1)),
           tokenClassToStr[g.kind]])
     deinitGeneralTokenizer(g)
-  dispA(d.target, result, "</pre>", "\n\\end{rstpre}\n")
-  
+  dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n")
+
 proc renderContainer(d: PDoc, n: PRstNode, result: var string) = 
   var tmp = ""
   renderRstToOut(d, n.sons[2], tmp)
diff --git a/lib/system.nim b/lib/system.nim
index a9b4da77a..7b78d1cf1 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1102,11 +1102,11 @@ else:
 
 const
   QuitSuccess* = 0
-    ## is the value that should be passed to ``quit`` to indicate
+    ## is the value that should be passed to `quit <#quit>`_ to indicate
     ## success.
 
   QuitFailure* = 1
-    ## is the value that should be passed to ``quit`` to indicate
+    ## is the value that should be passed to `quit <#quit>`_ to indicate
     ## failure.
 
 var programResult* {.exportc: "nim_program_result".}: int
@@ -1119,10 +1119,11 @@ proc quit*(errorcode: int = QuitSuccess) {.
   ## Stops the program immediately with an exit code.
   ##
   ## Before stopping the program the "quit procedures" are called in the
-  ## opposite order they were added with ``addQuitProc``. ``quit`` never
-  ## returns and ignores any exception that may have been raised by the quit
-  ## procedures.  It does *not* call the garbage collector to free all the
-  ## memory, unless a quit procedure calls ``GC_collect``.
+  ## opposite order they were added with `addQuitProc <#addQuitProc>`_.
+  ## ``quit`` never returns and ignores any exception that may have been raised
+  ## by the quit procedures.  It does *not* call the garbage collector to free
+  ## all the memory, unless a quit procedure calls `GC_fullCollect
+  ## <#GC_fullCollect>`_.
   ##
   ## The proc ``quit(QuitSuccess)`` is called implicitly when your nim
   ## program finishes without incident. A raised unhandled exception is
@@ -1130,7 +1131,8 @@ proc quit*(errorcode: int = QuitSuccess) {.
   ##
   ## Note that this is a *runtime* call and using ``quit`` inside a macro won't
   ## have any compile time effect. If you need to stop the compiler inside a
-  ## macro, use the ``error`` or ``fatal`` pragmas.
+  ## macro, use the `error <manual.html#error-pragma>`_ or `fatal
+  ## <manual.html#fatal-pragma>`_ pragmas.
 
 template sysAssert(cond: bool, msg: string) =
   when defined(useSysAssert):
@@ -1284,11 +1286,12 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.
 
 proc addQuitProc*(QuitProc: proc() {.noconv.}) {.
   importc: "atexit", header: "<stdlib.h>".}
-  ## adds/registers a quit procedure. Each call to ``addQuitProc``
-  ## registers another quit procedure. Up to 30 procedures can be
-  ## registered. They are executed on a last-in, first-out basis
-  ## (that is, the last function registered is the first to be executed).
-  ## ``addQuitProc`` raises an EOutOfIndex if ``quitProc`` cannot be
+  ## Adds/registers a quit procedure.
+  ##
+  ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30
+  ## procedures can be registered. They are executed on a last-in, first-out
+  ## basis (that is, the last function registered is the first to be executed).
+  ## ``addQuitProc`` raises an EOutOfIndex exception if ``QuitProc`` cannot be
   ## registered.
 
 # Support for addQuitProc() is done by Ansi C's facilities here.
@@ -2349,9 +2352,12 @@ when not defined(JS): #and not defined(NimrodVM):
       ## current file position is not at the beginning of the file.
     
     proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.}
-      ## Opens a file named `filename` for reading. Then calls `readAll`
-      ## and closes the file afterwards. Returns the string. 
-      ## Raises an IO exception in case of an error.
+      ## Opens a file named `filename` for reading.
+      ##
+      ## Then calls `readAll <#readAll>`_ and closes the file afterwards.
+      ## Returns the string.  Raises an IO exception in case of an error. If
+      ## you need to call this inside a compile time macro you can use
+      ## `staticRead <#staticRead>`_.
 
     proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.}
       ## Opens a file named `filename` for writing. Then writes the
@@ -2852,19 +2858,20 @@ proc `[]=`*[T](s: var seq[T], x: Slice[int], b: openArray[T]) =
     spliceImpl(s, a, L, b)
 
 proc slurp*(filename: string): string {.magic: "Slurp".}
-  ## This is an alias for ``staticRead``.
+  ## This is an alias for `staticRead <#staticRead>`_.
 
 proc staticRead*(filename: string): string {.magic: "Slurp".}
-  ## Compile-time ``readFile`` proc for easy `resource`:idx: embedding:
+  ## Compile-time `readFile <#readFile>`_ proc for easy `resource`:idx:
+  ## embedding:
   ##
   ## .. code-block:: nim
   ##     const myResource = staticRead"mydatafile.bin"
   ##
-  ## ``slurp`` is an alias for ``staticRead``.
+  ## `slurp <#slurp>`_ is an alias for ``staticRead``.
 
 proc gorge*(command: string, input = ""): string {.
   magic: "StaticExec".} = discard
-  ## This is an alias for ``staticExec``.
+  ## This is an alias for `staticExec <#staticExec>`_.
 
 proc staticExec*(command: string, input = ""): string {.
   magic: "StaticExec".} = discard
@@ -2876,8 +2883,8 @@ proc staticExec*(command: string, input = ""): string {.
   ##     const buildInfo = "Revision " & staticExec("git rev-parse HEAD") & 
   ##                       "\nCompiled on " & staticExec("uname -v")
   ##
-  ## ``gorge`` is an alias for ``staticExec``. Note that you can use this proc
-  ## inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL
+  ## `gorge <#gorge>`_ is an alias for ``staticExec``. Note that you can use
+  ## this proc inside a pragma like `passC <nimc.html#passc-pragma>`_ or `passL
   ## <nimc.html#passl-pragma>`_.
 
 proc `+=`*[T: SomeOrdinal|uint|uint64](x: var T, y: T) {.magic: "Inc", noSideEffect.}