change runnableExamples implementation; fixes #8641; fixes #7135; runnableExamples works for templates and generics
This commit is contained in:
parent
4cf704bb3e
commit
1948eadc24
5 changed files with 70 additions and 42 deletions
|
|
@ -31,6 +31,7 @@ type
|
||||||
isPureRst: bool
|
isPureRst: bool
|
||||||
conf*: ConfigRef
|
conf*: ConfigRef
|
||||||
cache*: IdentCache
|
cache*: IdentCache
|
||||||
|
runnableExamples*: PNode
|
||||||
|
|
||||||
PDoc* = ref TDocumentor ## Alias to type less.
|
PDoc* = ref TDocumentor ## Alias to type less.
|
||||||
|
|
||||||
|
|
@ -284,11 +285,60 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
|
||||||
dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
|
dispA(d.conf, result, "<span class=\"Other\">$1</span>", "\\spanOther{$1}",
|
||||||
[rope(esc(d.target, literal))])
|
[rope(esc(d.target, literal))])
|
||||||
|
|
||||||
|
proc testExamples*(d: PDoc) =
|
||||||
|
if d.runnableExamples == nil: return
|
||||||
|
let outputDir = d.conf.getNimcacheDir / "runnableExamples"
|
||||||
|
createDir(outputDir)
|
||||||
|
let inp = toFullPath(d.conf, d.runnableExamples.info)
|
||||||
|
let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim")
|
||||||
|
let nimcache = outp.changeFileExt"" & "_nimcache"
|
||||||
|
renderModule(d.runnableExamples, inp, outp, conf = d.conf)
|
||||||
|
let backend = if isDefined(d.conf, "js"): "js"
|
||||||
|
elif isDefined(d.conf, "cpp"): "cpp"
|
||||||
|
elif isDefined(d.conf, "objc"): "objc"
|
||||||
|
else: "c"
|
||||||
|
if os.execShellCmd(os.getAppFilename() & " " & backend &
|
||||||
|
" --nimcache:" & nimcache & " -r " & outp) != 0:
|
||||||
|
quit "[Examples] failed: see " & outp
|
||||||
|
else:
|
||||||
|
# keep generated source file `outp` to allow inspection.
|
||||||
|
rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp])
|
||||||
|
removeFile(outp.changeFileExt(ExeExt))
|
||||||
|
try:
|
||||||
|
removeDir(nimcache)
|
||||||
|
except OSError:
|
||||||
|
discard
|
||||||
|
|
||||||
|
proc extractImports(n: PNode; result: PNode) =
|
||||||
|
if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
|
||||||
|
result.add copyTree(n)
|
||||||
|
n.kind = nkEmpty
|
||||||
|
return
|
||||||
|
for i in 0..<n.safeLen: extractImports(n[i], result)
|
||||||
|
|
||||||
|
proc prepareExamples(d: PDoc; n: PNode) =
|
||||||
|
let inp = toFullPath(d.conf, n.info)
|
||||||
|
if d.runnableExamples == nil:
|
||||||
|
d.runnableExamples = newTree(nkStmtList,
|
||||||
|
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
|
||||||
|
d.runnableExamples.info = n.info
|
||||||
|
let imports = newTree(nkStmtList)
|
||||||
|
var savedLastSon = copyTree n.lastSon
|
||||||
|
extractImports(savedLastSon, imports)
|
||||||
|
for imp in imports: d.runnableExamples.add imp
|
||||||
|
d.runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon)
|
||||||
|
|
||||||
|
proc isRunnableExample(n: PNode): bool =
|
||||||
|
# Templates and generics don't perform symbol lookups.
|
||||||
|
result = n.kind == nkSym and n.sym.magic == mRunnableExamples or
|
||||||
|
n.kind == nkIdent and n.ident.s == "runnableExamples"
|
||||||
|
|
||||||
proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
|
proc getAllRunnableExamples(d: PDoc; n: PNode; dest: var Rope) =
|
||||||
case n.kind
|
case n.kind
|
||||||
of nkCallKinds:
|
of nkCallKinds:
|
||||||
if n[0].kind == nkSym and n[0].sym.magic == mRunnableExamples and
|
if isRunnableExample(n[0]) and
|
||||||
n.len >= 2 and n.lastSon.kind == nkStmtList:
|
n.len >= 2 and n.lastSon.kind == nkStmtList:
|
||||||
|
prepareExamples(d, n)
|
||||||
dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
|
dispA(d.conf, dest, "\n<p><strong class=\"examples_text\">$1</strong></p>\n",
|
||||||
"\n\\textbf{$1}\n", [rope"Examples:"])
|
"\n\\textbf{$1}\n", [rope"Examples:"])
|
||||||
inc d.listingCounter
|
inc d.listingCounter
|
||||||
|
|
@ -627,6 +677,10 @@ proc generateDoc*(d: PDoc, n: PNode) =
|
||||||
of nkImportStmt:
|
of nkImportStmt:
|
||||||
for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
|
for i in 0 .. sonsLen(n)-1: traceDeps(d, n.sons[i])
|
||||||
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
|
of nkFromStmt, nkImportExceptStmt: traceDeps(d, n.sons[0])
|
||||||
|
of nkCallKinds:
|
||||||
|
var comm: Rope = nil
|
||||||
|
getAllRunnableExamples(d, n, comm)
|
||||||
|
if comm > nil: add(d.modDesc, comm)
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc add(d: PDoc; j: JsonNode) =
|
proc add(d: PDoc; j: JsonNode) =
|
||||||
|
|
@ -819,6 +873,7 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) =
|
||||||
generateDoc(d, ast)
|
generateDoc(d, ast)
|
||||||
writeOutput(d, conf.projectFull, HtmlExt)
|
writeOutput(d, conf.projectFull, HtmlExt)
|
||||||
generateIndex(d)
|
generateIndex(d)
|
||||||
|
testExamples(d)
|
||||||
|
|
||||||
proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) =
|
proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string) =
|
||||||
var filen = addFileExt(filename, "txt")
|
var filen = addFileExt(filename, "txt")
|
||||||
|
|
@ -853,6 +908,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename, outExt: string)
|
||||||
d.modDesc = rope(modDesc)
|
d.modDesc = rope(modDesc)
|
||||||
writeOutput(d, filename, outExt)
|
writeOutput(d, filename, outExt)
|
||||||
generateIndex(d)
|
generateIndex(d)
|
||||||
|
testExamples(d)
|
||||||
|
|
||||||
proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
|
proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) =
|
||||||
commandRstAux(cache, conf, conf.projectFull, HtmlExt)
|
commandRstAux(cache, conf, conf.projectFull, HtmlExt)
|
||||||
|
|
@ -876,6 +932,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) =
|
||||||
let filename = getOutFile(conf, conf.projectFull, JsonExt)
|
let filename = getOutFile(conf, conf.projectFull, JsonExt)
|
||||||
if not writeRope(content, filename):
|
if not writeRope(content, filename):
|
||||||
rawMessage(conf, errCannotOpenFile, filename)
|
rawMessage(conf, errCannotOpenFile, filename)
|
||||||
|
testExamples(d)
|
||||||
|
|
||||||
proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
proc commandTags*(cache: IdentCache, conf: ConfigRef) =
|
||||||
var ast = parseFile(conf.projectMainIdx, cache, conf)
|
var ast = parseFile(conf.projectMainIdx, cache, conf)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ template closeImpl(body: untyped) {.dirty.} =
|
||||||
body
|
body
|
||||||
try:
|
try:
|
||||||
generateIndex(g.doc)
|
generateIndex(g.doc)
|
||||||
|
testExamples(g.doc)
|
||||||
except IOError:
|
except IOError:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -607,28 +607,6 @@ proc myProcess(context: PPassContext, n: PNode): PNode =
|
||||||
#if c.config.cmd == cmdIdeTools: findSuggest(c, n)
|
#if c.config.cmd == cmdIdeTools: findSuggest(c, n)
|
||||||
rod.storeNode(c.graph, c.module, result)
|
rod.storeNode(c.graph, c.module, result)
|
||||||
|
|
||||||
proc testExamples(c: PContext) =
|
|
||||||
let outputDir = c.config.getNimcacheDir / "runnableExamples"
|
|
||||||
createDir(outputDir)
|
|
||||||
let inp = toFullPath(c.config, c.module.info)
|
|
||||||
let outp = outputDir / extractFilename(inp.changeFileExt"" & "_examples.nim")
|
|
||||||
let nimcache = outp.changeFileExt"" & "_nimcache"
|
|
||||||
renderModule(c.runnableExamples, inp, outp, conf = c.config)
|
|
||||||
let backend = if isDefined(c.config, "js"): "js"
|
|
||||||
elif isDefined(c.config, "cpp"): "cpp"
|
|
||||||
elif isDefined(c.config, "objc"): "objc"
|
|
||||||
else: "c"
|
|
||||||
if os.execShellCmd(os.getAppFilename() & " " & backend & " --nimcache:" & nimcache & " -r " & outp) != 0:
|
|
||||||
quit "[Examples] failed: see " & outp
|
|
||||||
else:
|
|
||||||
# keep generated source file `outp` to allow inspection.
|
|
||||||
rawMessage(c.config, hintSuccess, ["runnableExamples: " & outp])
|
|
||||||
removeFile(outp.changeFileExt(ExeExt))
|
|
||||||
try:
|
|
||||||
removeDir(nimcache)
|
|
||||||
except OSError:
|
|
||||||
discard
|
|
||||||
|
|
||||||
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||||
var c = PContext(context)
|
var c = PContext(context)
|
||||||
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
|
if c.config.cmd == cmdIdeTools and not c.suggestionsMade:
|
||||||
|
|
@ -644,7 +622,6 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode =
|
||||||
popOwner(c)
|
popOwner(c)
|
||||||
popProcCon(c)
|
popProcCon(c)
|
||||||
storeRemaining(c.graph, c.module)
|
storeRemaining(c.graph, c.module)
|
||||||
if c.runnableExamples != nil: testExamples(c)
|
|
||||||
|
|
||||||
const semPass* = makePass(myOpen, myProcess, myClose,
|
const semPass* = makePass(myOpen, myProcess, myClose,
|
||||||
isFrontend = true)
|
isFrontend = true)
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,6 @@ type
|
||||||
# the generic type has been constructed completely. See
|
# the generic type has been constructed completely. See
|
||||||
# tests/destructor/topttree.nim for an example that
|
# tests/destructor/topttree.nim for an example that
|
||||||
# would otherwise fail.
|
# would otherwise fail.
|
||||||
runnableExamples*: PNode
|
|
||||||
|
|
||||||
template config*(c: PContext): ConfigRef = c.graph.config
|
template config*(c: PContext): ConfigRef = c.graph.config
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1953,13 +1953,6 @@ proc setMs(n: PNode, s: PSym): PNode =
|
||||||
n.sons[0] = newSymNode(s)
|
n.sons[0] = newSymNode(s)
|
||||||
n.sons[0].info = n.info
|
n.sons[0].info = n.info
|
||||||
|
|
||||||
proc extractImports(n: PNode; result: PNode) =
|
|
||||||
if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
|
|
||||||
result.add copyTree(n)
|
|
||||||
n.kind = nkEmpty
|
|
||||||
return
|
|
||||||
for i in 0..<n.safeLen: extractImports(n[i], result)
|
|
||||||
|
|
||||||
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
# this is a hotspot in the compiler!
|
# this is a hotspot in the compiler!
|
||||||
# DON'T forget to update ast.SpecialSemMagics if you add a magic here!
|
# DON'T forget to update ast.SpecialSemMagics if you add a magic here!
|
||||||
|
|
@ -2033,16 +2026,17 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
|
||||||
result = magicsAfterOverloadResolution(c, result, flags)
|
result = magicsAfterOverloadResolution(c, result, flags)
|
||||||
of mRunnableExamples:
|
of mRunnableExamples:
|
||||||
if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
|
if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList:
|
||||||
if sfMainModule in c.module.flags:
|
when false:
|
||||||
let inp = toFullPath(c.config, c.module.info)
|
if sfMainModule in c.module.flags:
|
||||||
if c.runnableExamples == nil:
|
let inp = toFullPath(c.config, c.module.info)
|
||||||
c.runnableExamples = newTree(nkStmtList,
|
if c.runnableExamples == nil:
|
||||||
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
|
c.runnableExamples = newTree(nkStmtList,
|
||||||
let imports = newTree(nkStmtList)
|
newTree(nkImportStmt, newStrNode(nkStrLit, expandFilename(inp))))
|
||||||
var saved_lastSon = copyTree n.lastSon
|
let imports = newTree(nkStmtList)
|
||||||
extractImports(saved_lastSon, imports)
|
var savedLastSon = copyTree n.lastSon
|
||||||
for imp in imports: c.runnableExamples.add imp
|
extractImports(savedLastSon, imports)
|
||||||
c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree saved_lastSon)
|
for imp in imports: c.runnableExamples.add imp
|
||||||
|
c.runnableExamples.add newTree(nkBlockStmt, c.graph.emptyNode, copyTree savedLastSon)
|
||||||
result = setMs(n, s)
|
result = setMs(n, s)
|
||||||
else:
|
else:
|
||||||
result = c.graph.emptyNode
|
result = c.graph.emptyNode
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue