mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
index generation for docgen knows about subdirectories; index knows about enum values; fixes import statement for runnableExamples
This commit is contained in:
@@ -343,6 +343,7 @@ proc testExample(d: PDoc; ex: PNode) =
|
||||
elif isDefined(d.conf, "objc"): "objc"
|
||||
else: "c"
|
||||
if os.execShellCmd(os.getAppFilename() & " " & backend &
|
||||
" --path:" & quoteShell(d.conf.projectPath) &
|
||||
" --nimcache:" & quoteShell(outputDir) &
|
||||
" -r " & quoteShell(outp)) != 0:
|
||||
quit "[Examples] failed: see " & outp.string
|
||||
@@ -622,11 +623,22 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
||||
[nameRope, result, comm, itemIDRope, plainNameRope, plainSymbolRope,
|
||||
symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope]))
|
||||
|
||||
let external = AbsoluteFile(d.filename).relativeTo(d.conf.projectPath, '/').changeFileExt(HtmlExt).string
|
||||
|
||||
var attype: Rope
|
||||
if k in routineKinds and nameNode.kind == nkSym:
|
||||
let att = attachToType(d, nameNode.sym)
|
||||
if att != nil:
|
||||
attype = rope esc(d.target, att.name.s)
|
||||
elif k == skType and nameNode.kind == nkSym and nameNode.sym.typ.kind in {tyEnum, tyBool}:
|
||||
let etyp = nameNode.sym.typ
|
||||
for e in etyp.n:
|
||||
if e.sym.kind != skEnumField: continue
|
||||
let plain = renderPlainSymbolName(e)
|
||||
let symbolOrId = d.newUniquePlainSymbol(plain)
|
||||
setIndexTerm(d[], external, symbolOrId, plain, nameNode.sym.name.s & '.' & plain,
|
||||
xmltree.escape(getPlainDocstring(e).docstringSummary))
|
||||
|
||||
add(d.toc[k], ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"),
|
||||
["name", "header", "desc", "itemID", "header_plain", "itemSym",
|
||||
"itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"],
|
||||
@@ -637,11 +649,11 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind) =
|
||||
# Ironically for types the complexSymbol is *cleaner* than the plainName
|
||||
# because it doesn't include object fields or documentation comments. So we
|
||||
# use the plain one for callable elements, and the complex for the rest.
|
||||
var linkTitle = changeFileExt(extractFilename(d.filename), "") & " : "
|
||||
var linkTitle = changeFileExt(extractFilename(d.filename), "") & ": "
|
||||
if n.isCallable: linkTitle.add(xmltree.escape(plainName.strip))
|
||||
else: linkTitle.add(xmltree.escape(complexSymbol.strip))
|
||||
|
||||
setIndexTerm(d[], symbolOrId, name, linkTitle,
|
||||
setIndexTerm(d[], external, symbolOrId, name, linkTitle,
|
||||
xmltree.escape(plainDocstring.docstringSummary))
|
||||
if k == skType and nameNode.kind == nkSym:
|
||||
d.types.strTableAdd nameNode.sym
|
||||
@@ -847,7 +859,8 @@ proc genOutFile(d: PDoc): Rope =
|
||||
# Extract the title. Non API modules generate an entry in the index table.
|
||||
if d.meta[metaTitle].len != 0:
|
||||
title = d.meta[metaTitle]
|
||||
setIndexTerm(d[], "", title)
|
||||
let external = AbsoluteFile(d.filename).relativeTo(d.conf.projectPath, '/').changeFileExt(HtmlExt).string
|
||||
setIndexTerm(d[], external, "", title)
|
||||
else:
|
||||
# Modules get an automatic title for the HTML, but no entry in the index.
|
||||
title = "Module " & extractFilename(changeFileExt(d.filename, ""))
|
||||
|
||||
@@ -85,8 +85,8 @@ proc init(p: var CodeBlockParams) =
|
||||
proc initRstGenerator*(g: var RstGenerator, target: OutputTarget,
|
||||
config: StringTableRef, filename: string,
|
||||
options: RstParseOptions,
|
||||
findFile: FindFileHandler=nil,
|
||||
msgHandler: MsgHandler=nil) =
|
||||
findFile: FindFileHandler = nil,
|
||||
msgHandler: MsgHandler = nil) =
|
||||
## Initializes a ``RstGenerator``.
|
||||
##
|
||||
## You need to call this before using a ``RstGenerator`` with any other
|
||||
@@ -255,9 +255,9 @@ proc renderRstToOut*(d: var RstGenerator, n: PRstNode, result: var string)
|
||||
## .. code-block:: nim
|
||||
##
|
||||
## # ...configure gen and rst vars...
|
||||
## var generatedHTML = ""
|
||||
## renderRstToOut(gen, rst, generatedHTML)
|
||||
## echo generatedHTML
|
||||
## var generatedHtml = ""
|
||||
## renderRstToOut(gen, rst, generatedHtml)
|
||||
## echo generatedHtml
|
||||
|
||||
proc renderAux(d: PDoc, n: PRstNode, result: var string) =
|
||||
for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], result)
|
||||
@@ -282,20 +282,26 @@ proc quoteIndexColumn(text: string): string =
|
||||
## * ``"\\"`` => ``"\\\\"``
|
||||
## * ``"\n"`` => ``"\\n"``
|
||||
## * ``"\t"`` => ``"\\t"``
|
||||
result = text.replace("\\", "\\\\").replace("\n", "\\n").replace("\t", "\\t")
|
||||
result = newStringOfCap(text.len + 3)
|
||||
for c in text:
|
||||
case c
|
||||
of '\\': result.add "\\"
|
||||
of '\L': result.add "\\n"
|
||||
of '\C': discard
|
||||
of '\t': result.add "\\t"
|
||||
else: result.add c
|
||||
|
||||
proc unquoteIndexColumn(text: string): string =
|
||||
## Returns the unquoted version generated by ``quoteIndexColumn``.
|
||||
result = text.replace("\\t", "\t").replace("\\n", "\n").replace("\\\\", "\\")
|
||||
result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\"))
|
||||
|
||||
proc setIndexTerm*(d: var RstGenerator, id, term: string,
|
||||
proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string,
|
||||
linkTitle, linkDesc = "") =
|
||||
## Adds a `term` to the index using the specified hyperlink identifier.
|
||||
##
|
||||
## A new entry will be added to the index using the format
|
||||
## ``term<tab>file#id``. The file part will come from the `filename`
|
||||
## parameter used in a previous call to the `initRstGenerator()
|
||||
## <#initRstGenerator>`_ proc.
|
||||
## ``term<tab>file#id``. The file part will come from the `htmlFile`
|
||||
## parameter.
|
||||
##
|
||||
## The `id` will be appended with a hash character only if its length is not
|
||||
## zero, otherwise no specific anchor will be generated. In general you
|
||||
@@ -316,7 +322,6 @@ proc setIndexTerm*(d: var RstGenerator, id, term: string,
|
||||
entry = term
|
||||
isTitle = false
|
||||
entry.add('\t')
|
||||
let htmlFile = changeFileExt(extractFilename(d.filename), HtmlExt)
|
||||
entry.add(htmlFile)
|
||||
if id.len > 0:
|
||||
entry.add('#')
|
||||
@@ -356,7 +361,7 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) =
|
||||
|
||||
var term = ""
|
||||
renderAux(d, n, term)
|
||||
setIndexTerm(d, id, term, d.currentSection)
|
||||
setIndexTerm(d, "", id, term, d.currentSection)
|
||||
dispA(d.target, result, "<span id=\"$1\">$2</span>", "$2\\label{$1}",
|
||||
[id, term])
|
||||
|
||||
@@ -364,15 +369,15 @@ type
|
||||
IndexEntry = object
|
||||
keyword: string
|
||||
link: string
|
||||
linkTitle: string ## If not nil, contains a prettier text for the href
|
||||
linkDesc: string ## If not nil, the title attribute of the final href
|
||||
linkTitle: string ## contains a prettier text for the href
|
||||
linkDesc: string ## the title attribute of the final href
|
||||
|
||||
IndexedDocs = Table[IndexEntry, seq[IndexEntry]] ## \
|
||||
## Contains the index sequences for doc types.
|
||||
##
|
||||
## The key is a *fake* IndexEntry which will contain the title of the
|
||||
## document in the `keyword` field and `link` will contain the html
|
||||
## filename for the document. `linkTitle` and `linkDesc` will be nil.
|
||||
## filename for the document. `linkTitle` and `linkDesc` will be empty.
|
||||
##
|
||||
## The value indexed by this IndexEntry is a sequence with the real index
|
||||
## entries found in the ``.idx`` file.
|
||||
@@ -433,13 +438,13 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string =
|
||||
var i = 0
|
||||
while i < symbols.len:
|
||||
let keyword = symbols[i].keyword
|
||||
let cleaned_keyword = keyword.escapeLink
|
||||
let cleanedKeyword = keyword.escapeLink
|
||||
result.addf("<dt><a name=\"$2\" href=\"#$2\"><span>$1:</span></a></dt><dd><ul class=\"simple\">\n",
|
||||
[keyword, cleaned_keyword])
|
||||
[keyword, cleanedKeyword])
|
||||
var j = i
|
||||
while j < symbols.len and keyword == symbols[j].keyword:
|
||||
let
|
||||
url = symbols[j].link.escapeLink
|
||||
url = symbols[j].link #.escapeLink
|
||||
text = if symbols[j].linkTitle.len > 0: symbols[j].linkTitle else: url
|
||||
desc = if symbols[j].linkDesc.len > 0: symbols[j].linkDesc else: ""
|
||||
if desc.len > 0:
|
||||
@@ -462,12 +467,12 @@ proc isDocumentationTitle(hyperlink: string): bool =
|
||||
## for a more detailed explanation.
|
||||
result = hyperlink.find('#') < 0
|
||||
|
||||
proc stripTOCLevel(s: string): tuple[level: int, text: string] =
|
||||
proc stripTocLevel(s: string): tuple[level: int, text: string] =
|
||||
## Returns the *level* of the toc along with the text without it.
|
||||
for c in 0 .. <s.len:
|
||||
for c in 0 ..< s.len:
|
||||
result.level = c
|
||||
if s[c] != ' ': break
|
||||
result.text = s[result.level .. <s.len]
|
||||
result.text = s[result.level ..< s.len]
|
||||
|
||||
proc indentToLevel(level: var int, newLevel: int): string =
|
||||
## Returns the sequence of <ul>|</ul> characters to switch to `newLevel`.
|
||||
@@ -483,7 +488,7 @@ proc indentToLevel(level: var int, newLevel: int): string =
|
||||
result = repeat("</ul></li>", level - newLevel)
|
||||
level = newLevel
|
||||
|
||||
proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
|
||||
proc generateDocumentationToc(entries: seq[IndexEntry]): string =
|
||||
## Returns the sequence of index entries in an HTML hierarchical list.
|
||||
result = ""
|
||||
# Build a list of levels and extracted titles to make processing easier.
|
||||
@@ -495,7 +500,7 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
|
||||
level = 1
|
||||
levels.newSeq(entries.len)
|
||||
for entry in entries:
|
||||
let (rawLevel, rawText) = stripTOCLevel(entry.linkTitle or entry.keyword)
|
||||
let (rawLevel, rawText) = stripTocLevel(entry.linkTitle or entry.keyword)
|
||||
if rawLevel < 1:
|
||||
# This is a normal symbol, push it *inside* one level from the last one.
|
||||
levels[L].level = level + 1
|
||||
@@ -519,9 +524,9 @@ proc generateDocumentationTOC(entries: seq[IndexEntry]): string =
|
||||
titleTag = levels[L].text
|
||||
else:
|
||||
result.add(level.indentToLevel(levels[L].level))
|
||||
result.addf("""<li><a class="reference" data-doc-search-tag="$1" href="$2">
|
||||
result.addf("""<li><a class="reference" data-doc-search-tag="$1: $2" href="$3">
|
||||
$3</a></li>
|
||||
""", [titleTag & " : " & levels[L].text, link, levels[L].text])
|
||||
""", [titleTag, levels[L].text, link, levels[L].text])
|
||||
inc L
|
||||
result.add(level.indentToLevel(1) & "</ul>\n")
|
||||
|
||||
@@ -534,7 +539,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string =
|
||||
sort(titles, cmp)
|
||||
|
||||
for title in titles:
|
||||
let tocList = generateDocumentationTOC(docs.getOrDefault(title))
|
||||
let tocList = generateDocumentationToc(docs.getOrDefault(title))
|
||||
result.add("<ul><li><a href=\"" &
|
||||
title.link & "\">" & title.keyword & "</a>\n" & tocList & "</li></ul>\n")
|
||||
|
||||
@@ -575,8 +580,8 @@ proc readIndexDir(dir: string):
|
||||
setLen(result.symbols, 0)
|
||||
var L = 0
|
||||
# Scan index files and build the list of symbols.
|
||||
for kind, path in walkDir(dir):
|
||||
if kind == pcFile and path.endsWith(IndexExt):
|
||||
for path in walkDirRec(dir):
|
||||
if path.endsWith(IndexExt):
|
||||
var
|
||||
fileEntries: seq[IndexEntry]
|
||||
title: IndexEntry
|
||||
@@ -606,7 +611,7 @@ proc readIndexDir(dir: string):
|
||||
inc F
|
||||
# Depending on type add this to the list of symbols or table of APIs.
|
||||
if title.keyword.len == 0:
|
||||
for i in 0 .. <F:
|
||||
for i in 0 ..< F:
|
||||
# Don't add to symbols TOC entries (they start with a whitespace).
|
||||
let toc = fileEntries[i].linkTitle
|
||||
if toc.len > 0 and toc[0] == ' ':
|
||||
@@ -615,7 +620,12 @@ proc readIndexDir(dir: string):
|
||||
setLen(result.symbols, L + 1)
|
||||
result.symbols[L] = fileEntries[i]
|
||||
inc L
|
||||
result.modules.add(path.splitFile.name)
|
||||
if fileEntries.len > 0:
|
||||
var x = fileEntries[0].link
|
||||
let i = find(x, '#')
|
||||
if i > 0:
|
||||
x = x.substr(0, i-1)
|
||||
result.modules.add(x.changeFileExt(""))
|
||||
else:
|
||||
# Generate the symbolic anchor for index quickjumps.
|
||||
title.linkTitle = "doc_toc_" & $result.docs.len
|
||||
@@ -676,7 +686,7 @@ proc mergeIndexes*(dir: string): string =
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
proc stripTOCHTML(s: string): string =
|
||||
proc stripTocHtml(s: string): string =
|
||||
## Ugly quick hack to remove HTML tags from TOC titles.
|
||||
##
|
||||
## A TocEntry.header field already contains rendered HTML tags. Instead of
|
||||
@@ -724,7 +734,7 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) =
|
||||
|
||||
# Generate index entry using spaces to indicate TOC level for the output HTML.
|
||||
assert n.level >= 0
|
||||
setIndexTerm(d, refname, tmp.stripTOCHTML,
|
||||
setIndexTerm(d, "", refname, tmp.stripTocHtml,
|
||||
spaces(max(0, n.level)) & tmp)
|
||||
|
||||
proc renderOverline(d: PDoc, n: PRstNode, result: var string) =
|
||||
@@ -872,7 +882,7 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams =
|
||||
if result.langStr != "":
|
||||
result.lang = getSourceLanguage(result.langStr)
|
||||
|
||||
proc buildLinesHTMLTable(d: PDoc; params: CodeBlockParams, code: string):
|
||||
proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string):
|
||||
tuple[beginTable, endTable: string] =
|
||||
## Returns the necessary tags to start/end a code block in HTML.
|
||||
##
|
||||
@@ -922,7 +932,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) =
|
||||
if params.testCmd.len > 0 and d.onTestSnippet != nil:
|
||||
d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text)
|
||||
|
||||
let (blockStart, blockEnd) = buildLinesHTMLTable(d, params, m.text)
|
||||
let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text)
|
||||
|
||||
dispA(d.target, result, blockStart, "\\begin{rstpre}\n", [])
|
||||
if params.lang == langNone:
|
||||
|
||||
@@ -7,9 +7,12 @@ var
|
||||
|
||||
proc test(dir: string; fixup = false) =
|
||||
putEnv("SOURCE_DATE_EPOCH", "100000")
|
||||
if execShellCmd("nim doc --project -o:$1/htmldocs $1/testproject.nim" % dir) != 0:
|
||||
if execShellCmd("nim doc --project --index:on -o:$1/htmldocs $1/testproject.nim" % dir) != 0:
|
||||
quit("FAILURE: nim doc failed")
|
||||
|
||||
if execShellCmd("nim buildIndex -o:$1/htmldocs/theindex.html $1/htmldocs" % [dir]) != 0:
|
||||
quit("FAILURE: nim buildIndex failed")
|
||||
|
||||
for expected in walkDirRec(dir / "expected/"):
|
||||
let produced = expected.replace("/expected/", "/htmldocs/")
|
||||
if not fileExists(produced):
|
||||
|
||||
1266
nimdoc/testproject/expected/theindex.html
Normal file
1266
nimdoc/testproject/expected/theindex.html
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
|
||||
type
|
||||
SomeType* = int
|
||||
SomeType* = enum
|
||||
enumValueA,
|
||||
enumValueB,
|
||||
enumValueC
|
||||
|
||||
proc someType*(): SomeType =
|
||||
## constructor.
|
||||
|
||||
@@ -3,8 +3,9 @@ import subdir / subdir_b / utils
|
||||
|
||||
## This is the top level module.
|
||||
runnableExamples:
|
||||
import subdir / subdir_b / utils
|
||||
doAssert bar(3, 4) == 7
|
||||
foo(1, 2)
|
||||
foo(enumValueA, enumValueB)
|
||||
|
||||
|
||||
template foo*(a, b: SomeType) =
|
||||
|
||||
Reference in New Issue
Block a user