diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 049d78b138..a7745b2a58 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -208,6 +208,9 @@ proc dispA(target: TOutputTarget, dest: var string, if target != outLatex: addf(dest, xml, args) else: addf(dest, tex, args) +proc `or`(x, y: string): string {.inline.} = + result = if x.isNil: y else: x + proc renderRstToOut*(d: var TRstGenerator, n: PRstNode, result: var string) ## Writes into ``result`` the rst ast ``n`` using the ``d`` configuration. ## @@ -307,12 +310,32 @@ type linkTitle: string ## If not nil, contains a prettier text for the href linkDesc: string ## If not nil, the title attribute of the final href + TIndexedDocs {.pure, final.} = TTable[TIndexEntry, seq[TIndexEntry]] ## \ + ## Contains the index sequences for doc types. + ## + ## The key is a *fake* TIndexEntry 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. The + ## value indexed by this TIndexEntry is a sequence with the real index + ## entries found in the ``.idx`` file. + + proc cmp(a, b: TIndexEntry): int = ## Sorts two ``TIndexEntry`` first by `keyword` field, then by `link`. result = cmpIgnoreStyle(a.keyword, b.keyword) if result == 0: result = cmpIgnoreStyle(a.link, b.link) +proc hash(x: TIndexEntry): int = + ## Returns the hash for the combined fields of the type. + ## + ## The hash is computed as the concatenated string of all available fields. + assert(not x.keyword.isNil) + assert(not x.link.isNil) + let value = x.keyword & x.link & (x.linkTitle or "") & (x.linkDesc or "") + result = value.hash + + proc `<-`(a: var TIndexEntry, b: TIndexEntry) = shallowCopy a.keyword, b.keyword shallowCopy a.link, b.link @@ -371,9 +394,6 @@ proc isDocumentationTitle(hyperlink: string): bool = ## for a more detailed explanation. result = hyperlink.find('#') < 0 -proc `or`(x, y: string): string {.inline.} = - result = if x.isNil: y else: x - 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 .. ") level = newLevel -proc generateDocumentationTOC(entries: seq[TIndexEntry]): - tuple[toc, titleRef: string] = +proc generateDocumentationTOC(entries: seq[TIndexEntry]): string = ## Returns the sequence of index entries in an HTML hierarchical list. - result.toc = "" + result = "" # Build a list of levels and extracted titles to make processing easier. var + titleRef: string levels: seq[tuple[level: int, text: string]] L = 0 level = 1 @@ -420,34 +440,83 @@ proc generateDocumentationTOC(entries: seq[TIndexEntry]): inc L # Now generate hierarchical lists based on the precalculated levels. - result.toc = "\n") + assert(not titleRef.isNil, + "Can't use this proc on an API index, docs always have a title entry") -proc generateDocumentationIndex( - docs: TTable[string, seq[TIndexEntry]]): string = +proc generateDocumentationIndex(docs: TIndexedDocs): string = ## Returns all the documentation TOCs in an HTML hierarchical list. result = "" # Sort the titles to generate their toc in alphabetical order. - var titles = toSeq(keys[string, seq[TIndexEntry]](docs)) - sort(titles, system.cmp) + var titles = toSeq(keys[TIndexEntry, seq[TIndexEntry]](docs)) + sort(titles, cmp) for title in titles: - let (list, titleRef) = generateDocumentationTOC(docs[title]) - result.add("\n") + let tocList = generateDocumentationTOC(docs[title]) + result.add("\n") + +proc readIndexDir(dir: string): + tuple[symbols: seq[TIndexEntry], docs: TIndexedDocs] = + ## Walks `dir` reading ``.idx`` files converting them in TIndexEntry items. + ## + ## Returns the list of free symbol entries and the separate documentation + ## indexes found. See the documentation of ``mergeIndexes`` for details. + result.docs = initTable[TIndexEntry, seq[TIndexEntry]](32) + newSeq(result.symbols, 15_000) + 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): + var + fileEntries: seq[TIndexEntry] + title: TIndexEntry + F = 0 + newSeq(fileEntries, 500) + setLen(fileEntries, 0) + for line in lines(path): + let s = line.find('\t') + if s < 0: continue + setLen(fileEntries, F+1) + fileEntries[F].keyword = line.substr(0, s-1) + fileEntries[F].link = line.substr(s+1) + # See if we detect a title, a link without a `#foobar` trailing part. + if title.keyword.isNil and fileEntries[F].link.isDocumentationTitle: + title.keyword = fileEntries[F].keyword + title.link = fileEntries[F].link + + if fileEntries[F].link.find('\t') > 0: + let extraCols = fileEntries[F].link.split('\t') + fileEntries[F].link = extraCols[0] + assert extraCols.len == 3 + fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn + fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn + else: + fileEntries[F].linkTitle = nil + fileEntries[F].linkDesc = nil + inc F + # Depending on type add this to the list of symbols or table of APIs. + if title.keyword.isNil: + setLen(result.symbols, L + F) + for i in 0 .. 0: - let extraCols = fileEntries[F].link.split('\t') - fileEntries[F].link = extraCols[0] - assert extraCols.len == 3 - fileEntries[F].linkTitle = extraCols[1].unquoteIndexColumn - fileEntries[F].linkDesc = extraCols[2].unquoteIndexColumn - else: - fileEntries[F].linkTitle = nil - fileEntries[F].linkDesc = nil - inc F - # Depending on type add this to the list of symbols or table of APIs. - if foundTitle.len > 0: - docs[foundTitle] = fileEntries - else: - setLen(symbols, L + F) - for i in 0 .. 0: + if symbols.len > 0: sortIndex(symbols) result.add("

API symbols

\n") result.add(generateSymbolIndex(symbols))