From 7b84a7a4ed65e74314a9012a271bd81212c55853 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 17 Jan 2020 12:19:14 -0800 Subject: [PATCH] fix #13150 `nim doc --project` works with duplicate names and with imports below main project file --- compiler/ast.nim | 2 ++ compiler/commands.nim | 3 +++ compiler/docgen.nim | 33 ++++++++++++++++++++++++++------- compiler/docgen2.nim | 4 ++-- compiler/main.nim | 3 +++ compiler/modules.nim | 2 ++ compiler/options.nim | 32 ++++++++++++++++++++++++++++++++ compiler/packagehandling.nim | 13 ++++++++----- 8 files changed, 78 insertions(+), 14 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 3d05fd2ca780b..5a31a2f283fdf 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -823,6 +823,7 @@ type # check for the owner when touching 'usedGenerics'. usedGenerics*: seq[PInstantiation] tab*: TStrTable # interface table for modules + nimblePkg*: PSym of skLet, skVar, skField, skForVar: guard*: PSym bitsize*: int @@ -1391,6 +1392,7 @@ proc createModuleAlias*(s: PSym, newIdent: PIdent, info: TLineInfo; result.ast = s.ast result.id = s.id result.flags = s.flags + result.nimblePkg = s.nimblePkg system.shallowCopy(result.tab, s.tab) result.options = s.options result.position = s.position diff --git a/compiler/commands.nim b/compiler/commands.nim index 6633c1aa27027..ffb7ff7a0804d 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -413,6 +413,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "docseesrcurl": expectArg(conf, switch, arg, pass, info) conf.docSeeSrcUrl = arg + of "docroot": + expectArg(conf, switch, arg, pass, info) + conf.docRoot = processPath(conf, arg, info, notRelativeToProj=true) of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e2d516dfe6017..3e64b086757ce 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -47,6 +47,20 @@ type PDoc* = ref TDocumentor ## Alias to type less. +proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = + let file2 = $file + let docRoot = conf.getDocRoot() + case $docRoot: + # of "$mixed": relativeToPkg(conf, file) + of "$pkg": # TODO: check this works; right now failing at command line step + let dir = getNimbleFile(conf, file2).parentDir.AbsoluteDir + result = relativeTo(file, dir) + of "$path": + result = getRelativePathFromConfigPath(conf, file) + else: + result = relativeTo(file, docRoot) + result = result.string.replace("..", "@@").RelativeFile + proc whichType(d: PDoc; n: PNode): PSym = if n.kind == nkSym: if d.types.strTableContains(n.sym): @@ -174,7 +188,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if execShellCmd(c2) != status: rawMessage(conf, errGenerated, "executing of external program failed: " & c2) result.emitted = initIntSet() - result.destFile = getOutFile2(conf, relativeTo(filename, conf.projectPath), + result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, RelativeDir"htmldocs", false) result.thisDir = result.destFile.splitFile.dir @@ -294,13 +308,13 @@ proc getPlainDocstring(n: PNode): string = if result.len > 0: return proc belongsToPackage(conf: ConfigRef; module: PSym): bool = - result = module.kind == skModule and module.owner != nil and - module.owner.id == conf.mainPackageId + result = module.kind == skModule and module.nimblePkg != nil and + module.nimblePkg.id == conf.mainPackageId proc externalDep(d: PDoc; module: PSym): string = if optWholeProject in d.conf.globalOptions: let full = AbsoluteFile toFullPath(d.conf, FileIndex module.position) - let tmp = getOutFile2(d.conf, full.relativeTo(d.conf.projectPath), HtmlExt, + let tmp = getOutFile2(d.conf, presentationPath(d.conf, full), HtmlExt, RelativeDir"htmldocs", sfMainModule notin module.flags) result = relativeTo(tmp, d.thisDir, '/').string else: @@ -1012,6 +1026,12 @@ proc genSection(d: PDoc, kind: TSymKind) = proc cssHref(outDir: AbsoluteDir, destFile: AbsoluteFile): Rope = rope($relativeTo(outDir / RelativeFile"nimdoc.out.css", destFile.splitFile().dir, '/')) +proc nativeToUnix(path: string): string = + doAssert not path.isAbsolute # absolute files need more care for the drive + when DirSep == '\\': + result = replace(path, '\\', '/') + else: result = path + proc genOutFile(d: PDoc): Rope = var code, content: Rope @@ -1030,7 +1050,7 @@ 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] - let external = AbsoluteFile(d.filename).relativeTo(d.conf.projectPath, '/').changeFileExt(HtmlExt).string + let external = presentationPath(d.conf, AbsoluteFile d.filename).changeFileExt(HtmlExt).string.nativeToUnix setIndexTerm(d[], external, "", title) else: # Modules get an automatic title for the HTML, but no entry in the index. @@ -1059,8 +1079,7 @@ proc generateIndex*(d: PDoc) = let dir = if not d.conf.outDir.isEmpty: d.conf.outDir else: d.conf.projectPath / RelativeDir"htmldocs" createDir(dir) - let dest = dir / changeFileExt(relativeTo(AbsoluteFile d.filename, - d.conf.projectPath), IndexExt) + let dest = dir / changeFileExt(presentationPath(d.conf, AbsoluteFile d.filename), IndexExt) writeIndexFile(d[], dest.string) proc writeOutput*(d: PDoc, useWarning = false) = diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index aa4d9cbef497c..4aeaaf35053dd 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -22,8 +22,8 @@ type config: ConfigRef PGen = ref TGen -template shouldProcess(g): bool = - (g.module.owner.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or +proc shouldProcess(g: PGen): bool = + (g.module.nimblePkg.id == g.doc.conf.mainPackageId and optWholeProject in g.doc.conf.globalOptions) or sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex template closeImpl(body: untyped) {.dirty.} = diff --git a/compiler/main.nim b/compiler/main.nim index 5efdd87af91e9..c8bdc94475660 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -293,7 +293,9 @@ proc mainCommand*(graph: ModuleGraph) = for s in definedSymbolNames(conf.symbols): definedSymbols.elems.add(%s) var libpaths = newJArray() + var lazyPaths = newJArray() for dir in conf.searchPaths: libpaths.elems.add(%dir.string) + for dir in conf.lazyPaths: lazyPaths.elems.add(%dir.string) var hints = newJObject() # consider factoring with `listHints` for a in hintMin..hintMax: @@ -311,6 +313,7 @@ proc mainCommand*(graph: ModuleGraph) = (key: "project_path", val: %conf.projectFull.string), (key: "defined_symbols", val: definedSymbols), (key: "lib_paths", val: %libpaths), + (key: "lazyPaths", val: %lazyPaths), (key: "outdir", val: %conf.outDir.string), (key: "out", val: %conf.outFile.string), (key: "nimcache", val: %getNimcacheDir(conf).string), diff --git a/compiler/modules.nim b/compiler/modules.nim index 9ef9dfead47fa..4525b8c5c27db 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -27,7 +27,9 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) + result.nimblePkg = packSym else: + result.nimblePkg = packSym let existing = strTableGet(packSym.tab, result.name) if existing != nil and existing.info.fileIndex != result.info.fileIndex: when false: diff --git a/compiler/options.nim b/compiler/options.nim index d8f6fd0b9d0f9..35e87ceb40dfd 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -263,6 +263,9 @@ type implicitIncludes*: seq[string] # modules that are to be implicitly included docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ # The string uses the formatting variables `path` and `line`. + docRoot*: AbsoluteDir + # `nim doc --docRoot:foo --project --outdir:docs foo/sub/main.nim` + # genrates: docs/sub/main.html # the used compiler cIncludes*: seq[AbsoluteDir] # directories to search for included files @@ -500,6 +503,9 @@ proc absOutFile*(conf: ConfigRef): AbsoluteFile = if dirExists(result.string): result.string.add ".out" +proc getDocRoot*(conf: ConfigRef): AbsoluteDir = + if conf.docRoot.isEmpty: conf.projectPath else: conf.docRoot + proc prepareToWriteOutput*(conf: ConfigRef): AbsoluteFile = ## Create the output directory and returns a full path to the output file createDir conf.outDir @@ -656,6 +662,32 @@ template patchModule(conf: ConfigRef) {.dirty.} = let ov = conf.moduleOverrides[key] if ov.len > 0: result = AbsoluteFile(ov) +proc isRelativeTo(path: string, base: string): bool= + # PENDING #13212 + let path = path.normalizedPath + let base = base.normalizedPath + let ret = relativePath(path, base) + result = path.len > 0 and not ret.startsWith ".." + +proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile): RelativeFile = + let f = $f + template search(paths) = + for it in paths: + let it = $it + if f.isRelativeTo(it): + return relativePath(f, it).RelativeFile + search(conf.searchPaths) + search(conf.lazyPaths) + +proc relativeToPkg*(conf: ConfigRef, file: AbsoluteFile): string = + let file2 = $file + let dir = getNimbleFile(conf, file2).parentDir + result = relativePath(file2, dir) + # take care of things like in stdlib with multiple `--path:lib/pure` etc + let path2 = getRelativePathFromConfigPath(conf, file).string + if path2.len > result.len: result = path2 + if result.len == 0: result = file2 + proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile = if f.isAbsolute: result = if f.existsFile: AbsoluteFile(f) else: AbsoluteFile"" diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index e807ae3774b11..2243e70634797 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -15,10 +15,8 @@ iterator myParentDirs(p: string): string = if current.len == 0: break yield current -proc resetPackageCache*(conf: ConfigRef) = - conf.packageCache = newPackageCache() - -proc getPackageName*(conf: ConfigRef; path: string): string = +proc getNimbleFile*(conf: ConfigRef; path: string): string = + ## returns absolute path to nimble file, eg: /pathto/cligen.nimble var parents = 0 block packageSearch: for d in myParentDirs(path): @@ -27,7 +25,7 @@ proc getPackageName*(conf: ConfigRef; path: string): string = return conf.packageCache[d] inc parents for file in walkFiles(d / "*.nimble"): - result = file.splitFile.name + result = file break packageSearch # we also store if we didn't find anything: when not defined(nimNoNilSeqs): @@ -38,6 +36,11 @@ proc getPackageName*(conf: ConfigRef; path: string): string = dec parents if parents <= 0: break +proc getPackageName*(conf: ConfigRef; path: string): string = + ## returns nimble package name, eg: `cligen` + let path = getNimbleFile(conf, path) + result = path.splitFile.name + proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string = # Convert `path` so that 2 modules with same name # in different directory get different name and they can be