Skip to content

Commit 5828254

Browse files
authored
fix nim-lang#6583, fix nim-lang#14376, index+search now generated for all projects, many bug fixes with nim doc (nim-lang#14324)
* refs nim-lang#6583 fix nim doc output * changelog * change default for outDir when unspecified * cleanups * --project implies --index
1 parent cbfe932 commit 5828254

File tree

22 files changed

+294
-161
lines changed

22 files changed

+294
-161
lines changed

.gitignore

+3-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ megatest.nim
9292
# ignore debug dirs generated by dsymutil on OSX
9393
*.dSYM
9494

95-
nimdoc.out.css
96-
9795
# for `nim c -r nimdoc/tester` etc; this can be in multiple places
9896
htmldocs
97+
98+
## these are not needed anymore unless checkout old older versions
99+
nimdoc.out.css

changelog.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@
9494
- The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`.
9595
- The callback that is assigned to `system.onUnhandledException` must now be `.gcsafe`.
9696

97-
- `osproc.execCmdEx` now takes an optional `input` for stdin.
9897
- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env`
9998
parameters.
10099

@@ -188,6 +187,9 @@ proc mydiv(a, b): int {.raises: [].} =
188187
and avoids polluting both $pwd and $projectdir. It can be used with any command.
189188
- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override how an example is compiled and run,
190189
for example to change backend or compile only.
190+
- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified (with or without `--project`);
191+
passing `--project` now automatically generates an index and enables search.
192+
See [docgen](docgen.html#introduction-quick-start) for details.
191193

192194
## Tool changes
193195

compiler/commands.nim

+2-2
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
440440
expectArg(conf, switch, arg, pass, info)
441441
conf.docSeeSrcUrl = arg
442442
of "docroot":
443-
conf.docRoot = if arg.len == 0: "@default" else: arg
443+
conf.docRoot = if arg.len == 0: docRootDefault else: arg
444444
of "backend", "b":
445445
let backend = parseEnum(arg.normalize, TBackend.default)
446446
if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg)
@@ -485,7 +485,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
485485
of "forcebuild", "f":
486486
processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info)
487487
of "project":
488-
processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info)
488+
processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info)
489489
of "gc":
490490
expectArg(conf, switch, arg, pass, info)
491491
if pass in {passCmd2, passPP}:

compiler/docgen.nim

+36-28
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,10 @@ import
1717
packages/docutils/rst, packages/docutils/rstgen,
1818
json, xmltree, cgi, trees, types,
1919
typesrenderer, astalgo, lineinfos, intsets,
20-
pathutils, trees, tables
20+
pathutils, trees, tables, nimpaths
2121

2222
const
2323
exportSection = skField
24-
htmldocsDir = RelativeDir"htmldocs"
2524
docCmdSkip = "skip"
2625

2726
type
@@ -51,7 +50,7 @@ type
5150
destFile*: AbsoluteFile
5251
thisDir*: AbsoluteDir
5352
exampleGroups: OrderedTable[string, ExampleGroup]
54-
wroteCss*: bool
53+
wroteSupportFiles*: bool
5554

5655
PDoc* = ref TDocumentor ## Alias to type less.
5756

@@ -72,7 +71,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re
7271
proc nimbleDir(): AbsoluteDir =
7372
getNimbleFile(conf, file2).parentDir.AbsoluteDir
7473
case conf.docRoot:
75-
of "@default": # using `@` instead of `$` to avoid shell quoting complications
74+
of docRootDefault:
7675
result = getRelativePathFromConfigPath(conf, file)
7776
let dir = nimbleDir()
7877
if not dir.isEmpty:
@@ -88,9 +87,12 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re
8887
result = getRelativePathFromConfigPath(conf, file)
8988
if result.isEmpty: bail()
9089
elif conf.docRoot.len > 0:
91-
doAssert conf.docRoot.isAbsolute, conf.docRoot # or globalError
92-
doAssert conf.docRoot.existsDir, conf.docRoot
93-
result = relativeTo(file, conf.docRoot.AbsoluteDir)
90+
# we're (currently) requiring `isAbsolute` to avoid confusion when passing
91+
# a relative path (would it be relative wrt $PWD or to projectfile)
92+
conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot
93+
conf.globalAssert conf.docRoot.existsDir, arg=conf.docRoot
94+
# needed because `canonicalizePath` called on `file`
95+
result = file.relativeTo conf.docRoot.expandFilename.AbsoluteDir
9496
else:
9597
bail()
9698
if isAbsolute(result.string):
@@ -1125,11 +1127,8 @@ proc genSection(d: PDoc, kind: TSymKind) =
11251127
"sectionid", "sectionTitle", "sectionTitleID", "content"], [
11261128
ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]])
11271129

1128-
const nimdocOutCss = "nimdoc.out.css"
1129-
# `out` to make it easier to use with gitignore in user's repos
1130-
1131-
proc cssHref(outDir: AbsoluteDir, destFile: AbsoluteFile): Rope =
1132-
rope($relativeTo(outDir / nimdocOutCss.RelativeFile, destFile.splitFile().dir, '/'))
1130+
proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): Rope =
1131+
rope($relativeTo(outDir / linkto, destFile.splitFile().dir, '/'))
11331132

11341133
proc genOutFile(d: PDoc): Rope =
11351134
var
@@ -1160,15 +1159,17 @@ proc genOutFile(d: PDoc): Rope =
11601159
elif d.hasToc: "doc.body_toc"
11611160
else: "doc.body_no_toc"
11621161
content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title",
1163-
"tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg"],
1162+
"tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref"],
11641163
[title.rope, toc, d.modDesc, rope(getDateStr()),
1165-
rope(getClockStr()), code, d.modDeprecationMsg])
1164+
rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile, theindexFname.RelativeFile)])
11661165
if optCompileOnly notin d.conf.globalOptions:
11671166
# XXX what is this hack doing here? 'optCompileOnly' means raw output!?
11681167
code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [
1169-
"nimdoccss", "title", "tableofcontents", "moduledesc", "date", "time",
1168+
"nimdoccss", "dochackjs", "title", "tableofcontents", "moduledesc", "date", "time",
11701169
"content", "author", "version", "analytics", "deprecationMsg"],
1171-
[cssHref(d.conf.outDir, d.destFile), title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()),
1170+
[relLink(d.conf.outDir, d.destFile, nimdocOutCss.RelativeFile),
1171+
relLink(d.conf.outDir, d.destFile, docHackJsFname.RelativeFile),
1172+
title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()),
11721173
content, d.meta[metaAuthor].rope, d.meta[metaVersion].rope, d.analytics.rope, d.modDeprecationMsg])
11731174
else:
11741175
code = content
@@ -1203,11 +1204,13 @@ proc writeOutput*(d: PDoc, useWarning = false) =
12031204
if not writeRope(content, outfile):
12041205
rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile,
12051206
outfile.string)
1206-
elif not d.wroteCss:
1207-
let cssSource = $d.conf.getPrefixDir() / "doc" / "nimdoc.css"
1208-
let cssDest = $dir / nimdocOutCss
1209-
copyFile(cssSource, cssDest)
1210-
d.wroteCss = true
1207+
elif not d.wroteSupportFiles: # nimdoc.css + dochack.js
1208+
let nimr = $d.conf.getPrefixDir()
1209+
copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss)
1210+
if optGenIndex in d.conf.globalOptions:
1211+
let docHackJs2 = getDocHacksJs(nimr, nim = getAppFilename())
1212+
copyFile(docHackJs2, $d.conf.outDir / docHackJs2.lastPathPart)
1213+
d.wroteSupportFiles = true
12111214

12121215
proc writeOutputJson*(d: PDoc, useWarning = false) =
12131216
runAllExamples(d)
@@ -1234,6 +1237,8 @@ proc writeOutputJson*(d: PDoc, useWarning = false) =
12341237
proc handleDocOutputOptions*(conf: ConfigRef) =
12351238
if optWholeProject in conf.globalOptions:
12361239
# Backward compatibility with previous versions
1240+
# xxx this is buggy when user provides `nim doc --project -o:sub/bar.html main`,
1241+
# it'd write to `sub/bar.html/main.html`
12371242
conf.outDir = AbsoluteDir(conf.outDir / conf.outFile)
12381243

12391244
proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
@@ -1308,20 +1313,23 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) =
13081313
if not writeRope(content, filename):
13091314
rawMessage(conf, errCannotOpenFile, filename.string)
13101315

1311-
proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) =
1312-
var content = mergeIndexes(conf.projectFull.string).rope
1316+
proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") =
1317+
var content = mergeIndexes(dir).rope
13131318

1314-
var outFile = RelativeFile"theindex"
1315-
if conf.outFile != RelativeFile"":
1316-
outFile = conf.outFile
1319+
var outFile = outFile
1320+
if outFile.isEmpty: outFile = theindexFname.RelativeFile.changeFileExt("")
13171321
let filename = getOutFile(conf, outFile, HtmlExt)
13181322

13191323
let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), [
1320-
"nimdoccss", "title", "tableofcontents", "moduledesc", "date", "time",
1324+
"nimdoccss", "dochackjs",
1325+
"title", "tableofcontents", "moduledesc", "date", "time",
13211326
"content", "author", "version", "analytics"],
1322-
[cssHref(conf.outDir, filename), rope"Index", nil, nil, rope(getDateStr()),
1327+
[relLink(conf.outDir, filename, nimdocOutCss.RelativeFile),
1328+
relLink(conf.outDir, filename, docHackJsFname.RelativeFile),
1329+
rope"Index", nil, nil, rope(getDateStr()),
13231330
rope(getClockStr()), content, nil, nil, nil])
13241331
# no analytics because context is not available
13251332

13261333
if not writeRope(code, filename):
13271334
rawMessage(conf, errCannotOpenFile, filename.string)
1335+

compiler/main.nim

+43-73
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,8 @@ when not defined(leanCompiler):
6666
compileProject(graph)
6767
finishDoc2Pass(graph.config.projectName)
6868

69-
proc setOutDir(conf: ConfigRef) =
70-
if conf.outDir.isEmpty:
71-
if optUseNimcache in conf.globalOptions:
72-
conf.outDir = getNimcacheDir(conf)
73-
else:
74-
conf.outDir = conf.projectPath
75-
7669
proc commandCompileToC(graph: ModuleGraph) =
7770
let conf = graph.config
78-
setOutDir(conf)
7971
if conf.outFile.isEmpty:
8072
let base = conf.projectName
8173
let targetName = if optGenDynLib in conf.globalOptions:
@@ -121,7 +113,6 @@ proc commandCompileToJS(graph: ModuleGraph) =
121113
let conf = graph.config
122114
conf.exc = excCpp
123115

124-
setOutDir(conf)
125116
if conf.outFile.isEmpty:
126117
conf.outFile = RelativeFile(conf.projectName & ".js")
127118

@@ -191,11 +182,6 @@ proc mainCommand*(graph: ModuleGraph) =
191182
conf.searchPaths.add(conf.libpath)
192183
setId(100)
193184

194-
## Calling `setOutDir(conf)` unconditionally would fix regression
195-
## https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125
196-
when false: setOutDir(conf)
197-
if optUseNimcache in conf.globalOptions: setOutDir(conf)
198-
199185
proc customizeForBackend(backend: TBackend) =
200186
## Sets backend specific options but don't compile to backend yet in
201187
## case command doesn't require it. This must be called by all commands.
@@ -234,15 +220,40 @@ proc mainCommand*(graph: ModuleGraph) =
234220
of backendJs: commandCompileToJS(graph)
235221
of backendInvalid: doAssert false
236222

223+
template docLikeCmd(body) =
224+
when defined(leanCompiler):
225+
quit "compiler wasn't built with documentation generator"
226+
else:
227+
wantMainModule(conf)
228+
conf.cmd = cmdDoc
229+
loadConfigs(DocConfig, cache, conf)
230+
defineSymbol(conf.symbols, "nimdoc")
231+
body
232+
233+
block: ## command prepass
234+
var docLikeCmd2 = false # includes what calls `docLikeCmd` + some more
235+
case conf.command.normalize
236+
of "r": conf.globalOptions.incl {optRun, optUseNimcache}
237+
of "doc0", "doc2", "doc", "rst2html", "rst2tex", "jsondoc0", "jsondoc2",
238+
"jsondoc", "ctags", "buildindex": docLikeCmd2 = true
239+
else: discard
240+
if conf.outDir.isEmpty:
241+
# doc like commands can generate a lot of files (especially with --project)
242+
# so by default should not end up in $PWD nor in $projectPath.
243+
conf.outDir = block:
244+
var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf)
245+
else: conf.projectPath
246+
doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee
247+
if docLikeCmd2: ret = ret / htmldocsDir
248+
ret
249+
237250
## process all backend commands
238251
case conf.command.normalize
239252
of "c", "cc", "compile", "compiletoc": compileToBackend(backendC) # compile means compileToC currently
240253
of "cpp", "compiletocpp": compileToBackend(backendCpp)
241254
of "objc", "compiletooc": compileToBackend(backendObjc)
242255
of "js", "compiletojs": compileToBackend(backendJs)
243-
of "r": # different from `"run"`!
244-
conf.globalOptions.incl {optRun, optUseNimcache}
245-
compileToBackend(backendC)
256+
of "r": compileToBackend(backendC) # different from `"run"`!
246257
of "run":
247258
when hasTinyCBackend:
248259
extccomp.setCC(conf, "tcc", unknownLineInfo)
@@ -254,29 +265,19 @@ proc mainCommand*(graph: ModuleGraph) =
254265
else: customizeForBackend(backendC) # fallback for other commands
255266

256267
## process all other commands
257-
case conf.command.normalize
258-
of "doc0":
259-
when defined(leanCompiler):
260-
quit "compiler wasn't built with documentation generator"
261-
else:
262-
wantMainModule(conf)
263-
conf.cmd = cmdDoc
264-
loadConfigs(DocConfig, cache, conf)
265-
commandDoc(cache, conf)
268+
case conf.command.normalize # synchronize with `cmdUsingHtmlDocs`
269+
of "doc0": docLikeCmd commandDoc(cache, conf)
266270
of "doc2", "doc":
267-
conf.setNoteDefaults(warnLockLevel, false) # issue #13218
268-
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218
269-
# because currently generates lots of false positives due to conflation
270-
# of labels links in doc comments, eg for random.rand:
271-
# ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
272-
# ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
273-
when defined(leanCompiler):
274-
quit "compiler wasn't built with documentation generator"
275-
else:
276-
conf.cmd = cmdDoc
277-
loadConfigs(DocConfig, cache, conf)
278-
defineSymbol(conf.symbols, "nimdoc")
271+
docLikeCmd():
272+
conf.setNoteDefaults(warnLockLevel, false) # issue #13218
273+
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218
274+
# because currently generates lots of false positives due to conflation
275+
# of labels links in doc comments, eg for random.rand:
276+
# ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer
277+
# ## * `rand proc<#rand,Rand,range[]>`_ that returns a float
279278
commandDoc2(graph, false)
279+
if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions:
280+
commandBuildIndex(conf, $conf.outDir)
280281
of "rst2html":
281282
conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218
282283
when defined(leanCompiler):
@@ -292,41 +293,10 @@ proc mainCommand*(graph: ModuleGraph) =
292293
conf.cmd = cmdRst2tex
293294
loadConfigs(DocTexConfig, cache, conf)
294295
commandRst2TeX(cache, conf)
295-
of "jsondoc0":
296-
when defined(leanCompiler):
297-
quit "compiler wasn't built with documentation generator"
298-
else:
299-
wantMainModule(conf)
300-
conf.cmd = cmdDoc
301-
loadConfigs(DocConfig, cache, conf)
302-
wantMainModule(conf)
303-
defineSymbol(conf.symbols, "nimdoc")
304-
commandJson(cache, conf)
305-
of "jsondoc2", "jsondoc":
306-
when defined(leanCompiler):
307-
quit "compiler wasn't built with documentation generator"
308-
else:
309-
conf.cmd = cmdDoc
310-
loadConfigs(DocConfig, cache, conf)
311-
wantMainModule(conf)
312-
defineSymbol(conf.symbols, "nimdoc")
313-
commandDoc2(graph, true)
314-
of "ctags":
315-
when defined(leanCompiler):
316-
quit "compiler wasn't built with documentation generator"
317-
else:
318-
wantMainModule(conf)
319-
conf.cmd = cmdDoc
320-
loadConfigs(DocConfig, cache, conf)
321-
defineSymbol(conf.symbols, "nimdoc")
322-
commandTags(cache, conf)
323-
of "buildindex":
324-
when defined(leanCompiler):
325-
quit "compiler wasn't built with documentation generator"
326-
else:
327-
conf.cmd = cmdDoc
328-
loadConfigs(DocConfig, cache, conf)
329-
commandBuildIndex(cache, conf)
296+
of "jsondoc0": docLikeCmd commandJson(cache, conf)
297+
of "jsondoc2", "jsondoc": docLikeCmd commandDoc2(graph, true)
298+
of "ctags": docLikeCmd commandTags(cache, conf)
299+
of "buildindex": docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile)
330300
of "gendepend":
331301
conf.cmd = cmdGenDepend
332302
commandGenDepend(graph)

compiler/msgs.nim

+9-1
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string,
510510
styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg)
511511
if conf.hasHint(hintSource) and info != unknownLineInfo:
512512
conf.writeSurroundingSrc(info)
513-
if conf.hasHint(hintMsgOrigin):
513+
if hintMsgOrigin in conf.mainPackageNotes:
514514
styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle,
515515
" compiler msg initiated here", KindColor,
516516
KindFormat % hintMsgOrigin.msgToStr,
@@ -529,6 +529,14 @@ template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
529529
conf.m.errorOutputs = {eStdOut, eStdErr}
530530
liMessage(conf, info, msg, arg, doAbort, instLoc())
531531

532+
template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") =
533+
## avoids boilerplate
534+
if not cond:
535+
const info2 = instantiationInfo(-1, fullPaths = true)
536+
var arg2 = "'$1' failed" % [astToStr(cond)]
537+
if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg
538+
liMessage(conf, info, errGenerated, arg2, doRaise, info2)
539+
532540
template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") =
533541
liMessage(conf, info, msg, arg, doRaise, instLoc())
534542

0 commit comments

Comments
 (0)