NimYAML/doc/rstPreproc.nim

135 lines
3.9 KiB
Nim
Raw Normal View History

2016-10-08 16:38:27 +00:00
## This is a tool for preprocessing rst files. Lines starting with ``%`` will
## get substituted by nicely layouted nim and yaml code included from file in
## the snippets tree.
2016-10-08 16:38:27 +00:00
##
## The syntax of substituted lines is ``'%' path '%' level``. *path* shall be
## a path relative to the *snippets* directory. *level* shall be the level depth
## of the first title that should be produced.
2016-10-08 16:38:27 +00:00
##
## Usage:
##
## rstPreproc -o:path <infile>
##
## *path* is the output path. If omitted, it will be equal to infile with its
## suffix substituted by ``.rst``. *infile* is the source rst file.
##
## The reason for this complex approach is to have all snippets used in the docs
## available as source files for automatic testing. This way, we can make sure
## that the code in the docs actually works.
2016-10-08 16:38:27 +00:00
import parseopt2, streams, tables, strutils, os
2016-10-08 16:38:27 +00:00
var
infile = ""
path: string = nil
for kind, key, val in getopt():
case kind
of cmdArgument:
if infile == "":
if key == "":
echo "invalid input file with empty name!"
quit 1
infile = key
else:
echo "Only one input file is supported!"
quit 1
of cmdLongOption, cmdShortOption:
case key
of "out", "o":
if isNil(path): path = val
else:
echo "Duplicate output path!"
quit 1
else:
echo "Unknown option: ", key
quit 1
of cmdEnd: assert(false) # cannot happen
if infile == "":
echo "Missing input file!"
quit 1
if isNil(path):
for i in countdown(infile.len - 1, 0):
if infile[i] == '.':
if infile[i..^1] == ".rst": path = infile & ".rst"
else: path = infile[0..i] & "rst"
break
if isNil(path): path = infile & ".rst"
var tmpOut = newFileStream(path, fmWrite)
proc append(s: string) =
tmpOut.writeLine(s)
const headingChars = ['=', '-', '`', ':', '\'']
2016-10-08 16:38:27 +00:00
proc outputExamples(curPath: string, level: int = 0) =
let titlePath = curPath / "title"
if fileExists(titlePath):
let titleFile = open(titlePath, fmRead)
defer: titleFile.close()
var title = ""
if titleFile.readLine(title):
2016-10-08 16:38:27 +00:00
let headingChar = if level >= headingChars.len: headingChars[^1] else:
headingChars[level]
append(title)
append(repeat(headingChar, title.len) & '\l')
# process content files under this directory
var codeFiles = newSeq[string]()
for kind, filePath in walkDir(curPath, true):
if kind == pcFile:
if filePath != "title": codeFiles.add(filePath)
case codeFiles.len
of 0: discard
of 1:
2016-10-08 19:08:12 +00:00
let (_, _, extension) = codeFiles[0].splitFile()
append(".. code:: " & extension[1..^1])
append(" :file: " & (curPath / codeFiles[0]) & '\l')
of 2:
append(".. raw:: html")
append(" <table class=\"quickstart-example\"><thead><tr>")
for codeFile in codeFiles:
2016-10-08 19:08:12 +00:00
append(" <th>" & codeFile[3..^1] & "</th>")
append(" </th></tr></thead><tbody><tr><td>\n")
var first = true
for codeFile in codeFiles:
if first: first = false
else: append(".. raw:: html\n </td>\n <td>\n")
2016-10-08 19:08:12 +00:00
let (_, _, extension) = codeFile.splitFile()
append(".. code:: " & extension[1..^1])
append(" :file: " & (curPath / codeFile) & '\l')
append(".. raw:: html")
append(" </td></tr></tbody></table>\n")
2016-10-08 16:38:27 +00:00
else:
echo "Unexpected number of files in ", curPath, ": ", codeFiles.len
# process child directories
for kind, dirPath in walkDir(curPath):
if kind == pcDir:
outputExamples(dirPath, level + 1)
2016-10-08 16:38:27 +00:00
var lineNum = 0
for line in infile.lines():
if line.len > 0 and line[0] == '%':
var
srcPath: string = nil
level = 0
2016-10-08 16:38:27 +00:00
for i in 1..<line.len:
if line[i] == '%':
srcPath = line[1 .. i - 1]
level = parseInt(line[i + 1 .. ^1])
2016-10-08 16:38:27 +00:00
break
if isNil(srcPath):
2016-10-08 16:38:27 +00:00
echo "Second % missing in line " & $lineNum & "! content:\n"
echo line
quit 1
outputExamples("snippets" / srcPath, level)
2016-10-08 16:38:27 +00:00
else:
append(line)