1313import
1414 ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents,
1515 wordrecg, syntaxes, renderer, lexer,
16- packages/ docutils/ rst, packages / docutils / rstgen,
16+ packages/ docutils/ [ rst, rstgen, dochelpers] ,
1717 json, xmltree, trees, types,
1818 typesrenderer, astalgo, lineinfos, intsets,
1919 pathutils, tables, nimpaths, renderverbatim, osproc
4444 # # runnableExamples).
4545 substitutions: seq [string ] # # Variable names in `doc.item`...
4646 sortName: string # # The string used for sorting in output
47+ info: rstast.TLineInfo # # place where symbol was defined (for messages)
48+ anchor: string # # e.g. HTML anchor
49+ name: string # # short name of the symbol, not unique
50+ # # (includes backticks ` if present)
51+ detailedName: string # # longer name like `proc search(x: int): int`
4752 ModSection = object # # Section like Procs, Types, etc.
48- secItems: seq [Item ] # # Pre-processed items.
53+ secItems: Table [string , seq [Item ]]
54+ # # Map basic name -> pre-processed items.
4955 finalMarkup: string # # The items, after RST pass 2 and rendering.
5056 ModSections = array [TSymKind , ModSection ]
5157 TocItem = object # # HTML TOC item
9197 thisDir* : AbsoluteDir
9298 exampleGroups: OrderedTable [string , ExampleGroup ]
9399 wroteSupportFiles* : bool
100+ nimToRstFid: Table [lineinfos.FileIndex , rstast.FileIndex ]
101+ # # map Nim FileIndex -> RST one, it's needed because we keep them separate
94102
95103 PDoc * = ref TDocumentor # # Alias to type less.
96104
97105proc add (dest: var ItemPre , rst: PRstNode ) = dest.add ItemFragment (isRst: true , rst: rst)
98106proc add (dest: var ItemPre , str: string ) = dest.add ItemFragment (isRst: false , str: str)
99107
108+ proc addRstFileIndex (d: PDoc , info: lineinfos.TLineInfo ): rstast.FileIndex =
109+ let invalid = rstast.FileIndex (- 1 )
110+ result = d.nimToRstFid.getOrDefault (info.fileIndex, default = invalid)
111+ if result == invalid:
112+ let fname = toFullPath (d.conf, info)
113+ result = addFilename (d.sharedState, fname)
114+ d.nimToRstFid[info.fileIndex] = result
115+
100116proc cmpDecimalsIgnoreCase (a, b: string ): int =
101117 # # For sorting with correct handling of cases like 'uint8' and 'uint16'.
102118 # # Also handles leading zeros well (however note that leading zeros are
@@ -223,6 +239,7 @@ template declareClosures =
223239 of meFootnoteMismatch: k = errRstFootnoteMismatch
224240 of mwRedefinitionOfLabel: k = warnRstRedefinitionOfLabel
225241 of mwUnknownSubstitution: k = warnRstUnknownSubstitutionX
242+ of mwAmbiguousLink: k = warnRstAmbiguousLink
226243 of mwBrokenLink: k = warnRstBrokenLink
227244 of mwUnsupportedLanguage: k = warnRstLanguageXNotSupported
228245 of mwUnsupportedField: k = warnRstFieldXNotSupported
@@ -236,7 +253,7 @@ template declareClosures =
236253 result = getCurrentDir () / s
237254 if not fileExists (result ): result = " "
238255
239- proc parseRst (text, filename : string ,
256+ proc parseRst (text: string ,
240257 line, column: int ,
241258 conf: ConfigRef , sharedState: PRstSharedState ): PRstNode =
242259 declareClosures ()
@@ -352,7 +369,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int =
352369
353370proc genComment (d: PDoc , n: PNode ): PRstNode =
354371 if n.comment.len > 0 :
355- result = parseRst (n.comment, toFullPath (d.conf, n.info),
372+ d.sharedState.currFileIdx = addRstFileIndex (d, n.info)
373+ result = parseRst (n.comment,
356374 toLinenumber (n.info),
357375 toColumn (n.info) + DocColOffset ,
358376 d.conf, d.sharedState)
@@ -885,6 +903,57 @@ proc genSeeSrc(d: PDoc, path: string, line: int): string =
885903 " path" , path.string , " line" , $ line, " url" , gitUrl,
886904 " commit" , commit, " devel" , develBranch]])
887905
906+ proc symbolPriority (k: TSymKind ): int =
907+ result = case k
908+ of skMacro: - 3
909+ of skTemplate: - 2
910+ of skIterator: - 1
911+ else : 0 # including skProc which have higher priority
912+ # documentation itself has even higher priority 1
913+
914+ proc toLangSymbol (k: TSymKind , n: PNode , baseName: string ): LangSymbol =
915+ # # Converts symbol info (names/types/parameters) in `n` into format
916+ # # `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``.
917+ result .name = baseName.nimIdentNormalize
918+ result .symKind = k.toHumanStr
919+ if k in routineKinds:
920+ var
921+ paramTypes: seq [string ]
922+ renderParamTypes (paramTypes, n[paramsPos], toNormalize= true )
923+ let paramNames = renderParamNames (n[paramsPos], toNormalize= true )
924+ # In some rare cases (system.typeof) parameter type is not set for default:
925+ doAssert paramTypes.len <= paramNames.len
926+ for i in 0 ..< paramNames.len:
927+ if i < paramTypes.len:
928+ result .parameters.add (paramNames[i], paramTypes[i])
929+ else :
930+ result .parameters.add (paramNames[i], " " )
931+ result .parametersProvided = true
932+
933+ result .outType = renderOutType (n[paramsPos], toNormalize= true )
934+
935+ if k in {skProc, skFunc, skType, skIterator}:
936+ # Obtain `result.generics`
937+ # Use `n[miscPos]` since n[genericParamsPos] does not contain constraints
938+ var genNode: PNode = nil
939+ if k == skType:
940+ genNode = n[1 ] # FIXME: what is index 1?
941+ else :
942+ if n[miscPos].kind != nkEmpty:
943+ genNode = n[miscPos][1 ] # FIXME: what is index 1?
944+ if genNode != nil :
945+ var literal = " "
946+ var r: TSrcGen
947+ initTokRender (r, genNode, {renderNoBody, renderNoComments,
948+ renderNoPragmas, renderNoProcDefs})
949+ var kind = tkEof
950+ while true :
951+ getNextTok (r, kind, literal)
952+ if kind == tkEof:
953+ break
954+ if kind != tkSpaces:
955+ result .generics.add (literal.nimIdentNormalize)
956+
888957proc genItem (d: PDoc , n, nameNode: PNode , k: TSymKind , docFlags: DocFlags ) =
889958 if (docFlags != kForceExport) and not isVisible (d, nameNode): return
890959 let
@@ -915,6 +984,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
915984 inc (d.id)
916985 let
917986 plainNameEsc = esc (d.target, plainName.strip)
987+ detailedName = k.toHumanStr & " " & (
988+ if k in routineKinds: plainName else : name)
918989 uniqueName = if k in routineKinds: plainNameEsc else : name
919990 sortName = if k in routineKinds: plainName.strip else : name
920991 cleanPlainSymbol = renderPlainSymbolName (nameNode)
@@ -923,20 +994,32 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) =
923994 symbolOrId = d.newUniquePlainSymbol (complexSymbol)
924995 symbolOrIdEnc = encodeUrl (symbolOrId, usePlus = false )
925996 deprecationMsg = genDeprecationMsg (d, pragmaNode)
997+ rstLangSymbol = toLangSymbol (k, n, cleanPlainSymbol)
998+
999+ # we generate anchors automatically for subsequent use in doc comments
1000+ let lineinfo = rstast.TLineInfo (
1001+ line: nameNode.info.line, col: nameNode.info.col,
1002+ fileIndex: addRstFileIndex (d, nameNode.info))
1003+ addAnchorNim (d.sharedState, refn = symbolOrId, tooltip = detailedName,
1004+ rstLangSymbol, priority = symbolPriority (k), info = lineinfo)
9261005
9271006 nodeToHighlightedHtml (d, n, result , {renderNoBody, renderNoComments,
9281007 renderDocComments, renderSyms}, symbolOrIdEnc)
9291008
9301009 let seeSrc = genSeeSrc (d, toFullPath (d.conf, n.info), n.info.line.int )
9311010
932- d.section[k].secItems.add Item (
1011+ d.section [k].secItems.mgetOrPut (cleanPlainSymbol, newSeq [ Item ]()). add Item (
9331012 descRst: comm,
9341013 sortName: sortName,
1014+ info: lineinfo,
1015+ anchor: symbolOrId,
1016+ detailedName: detailedName,
1017+ name: name,
9351018 substitutions: @ [
936- " name " , name, " uniqueName" , uniqueName,
1019+ " uniqueName" , uniqueName,
9371020 " header" , result , " itemID" , $ d.id,
9381021 " header_plain" , plainNameEsc, " itemSym" , cleanPlainSymbol,
939- " itemSymOrID " , symbolOrId, " itemSymEnc" , plainSymbolEnc,
1022+ " itemSymEnc" , plainSymbolEnc,
9401023 " itemSymOrIDEnc" , symbolOrIdEnc, " seeSrc" , seeSrc,
9411024 " deprecationMsg" , deprecationMsg])
9421025
@@ -1184,6 +1267,11 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) =
11841267 if comm.len != 0 : d.modDescPre.add (comm)
11851268 else : discard
11861269
1270+ proc overloadGroupName (s: string , k: TSymKind ): string =
1271+ # # Turns a name like `f` into anchor `f-procs-all`
1272+ # s & " " & k.toHumanStr & "s all"
1273+ s & " -" & k.toHumanStr & " s-all"
1274+
11871275proc finishGenerateDoc * (d: var PDoc ) =
11881276 # # Perform 2nd RST pass for resolution of links/footnotes/headings...
11891277 # copy file map `filenames` to ``rstgen.nim`` for its warnings
@@ -1197,6 +1285,21 @@ proc finishGenerateDoc*(d: var PDoc) =
11971285 break
11981286 preparePass2 (d.sharedState, firstRst)
11991287
1288+ # add anchors to overload groups before RST resolution
1289+ for k in TSymKind :
1290+ if k in routineKinds:
1291+ for plainName, overloadChoices in d.section[k].secItems:
1292+ if overloadChoices.len > 1 :
1293+ let refn = overloadGroupName (plainName, k)
1294+ let tooltip = " $1 ($2 overloads)" % [
1295+ k.toHumanStr & " " & plainName, $ overloadChoices.len]
1296+ addAnchorNim (d.sharedState, refn, tooltip,
1297+ LangSymbol (symKind: k.toHumanStr, name: plainName,
1298+ isGroup: true ),
1299+ priority = symbolPriority (k),
1300+ # select index `0` just to have any meaningful warning:
1301+ info = overloadChoices[0 ].info)
1302+
12001303 # Finalize fragments of ``.nim`` or ``.rst`` file
12011304 proc renderItemPre (d: PDoc , fragments: ItemPre , result: var string ) =
12021305 for f in fragments:
@@ -1207,14 +1310,33 @@ proc finishGenerateDoc*(d: var PDoc) =
12071310 of false : result &= f.str
12081311 proc cmp (x, y: Item ): int = cmpDecimalsIgnoreCase (x.sortName, y.sortName)
12091312 for k in TSymKind :
1210- for item in d.section[k].secItems.sorted (cmp):
1211- var itemDesc: string
1212- renderItemPre (d, item.descRst, itemDesc)
1213- d.section[k].finalMarkup.add (
1214- getConfigVar (d.conf, " doc.item" ) % (
1215- item.substitutions & @ [" desc" , itemDesc]))
1216- itemDesc = " "
1217- d.section[k].secItems.setLen 0
1313+ # add symbols to section for each `k`, while optionally wrapping
1314+ # overloadable items with the same basic name by ``doc.item2``
1315+ let overloadableNames = toSeq (keys (d.section[k].secItems))
1316+ for plainName in overloadableNames.sorted (cmpDecimalsIgnoreCase):
1317+ var overloadChoices = d.section[k].secItems[plainName]
1318+ overloadChoices.sort (cmp)
1319+ var nameContent = " "
1320+ for item in overloadChoices:
1321+ var itemDesc: string
1322+ renderItemPre (d, item.descRst, itemDesc)
1323+ nameContent.add (
1324+ getConfigVar (d.conf, " doc.item" ) % (
1325+ item.substitutions & @ [
1326+ " desc" , itemDesc,
1327+ " name" , item.name,
1328+ " itemSymOrID" , item.anchor]))
1329+ if k in routineKinds:
1330+ let plainNameEsc1 = esc (d.target, plainName.strip)
1331+ let plainNameEsc2 = esc (d.target, plainName.strip, escMode= emUrl)
1332+ d.section[k].finalMarkup.add (
1333+ getConfigVar (d.conf, " doc.item2" ) % (
1334+ @ [" header_plain" , plainNameEsc1,
1335+ " overloadGroupName" , overloadGroupName (plainNameEsc2, k),
1336+ " content" , nameContent]))
1337+ else :
1338+ d.section[k].finalMarkup.add (nameContent)
1339+ d.section[k].secItems.clear
12181340 renderItemPre (d, d.modDescPre, d.modDescFinal)
12191341 d.modDescPre.setLen 0
12201342 d.hasToc = d.hasToc or d.sharedState.hasToc
@@ -1493,7 +1615,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef;
14931615 filename: AbsoluteFile , outExt: string ) =
14941616 var filen = addFileExt (filename, " txt" )
14951617 var d = newDocumentor (filen, cache, conf, outExt, isPureRst = true )
1496- let rst = parseRst (readFile (filen.string ), filen. string ,
1618+ let rst = parseRst (readFile (filen.string ),
14971619 line= LineRstInit , column= ColRstInit ,
14981620 conf, d.sharedState)
14991621 d.modDescPre = @ [ItemFragment (isRst: true , rst: rst)]
0 commit comments