diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index cac2e8d1..311058b7 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v2
- name: install nim
id: install_nim
- uses: iffy/install-nim@v3
+ uses: iffy/install-nim@v5
- name: install library dependencies
run: nimble install -y
- name: install doc dependencies
diff --git a/.github/workflows/netlify_build_docs.yml b/.github/workflows/netlify_build_docs.yml
index 63be024c..b1d4320a 100644
--- a/.github/workflows/netlify_build_docs.yml
+++ b/.github/workflows/netlify_build_docs.yml
@@ -8,10 +8,12 @@ jobs:
tests:
runs-on: ubuntu-latest
steps:
+ - name: apt install
+ run: sudo apt install -y libpcre3 libblas-dev liblapack-dev
- uses: actions/checkout@v2
- name: install nim
id: install_nim
- uses: iffy/install-nim@v3
+ uses: iffy/install-nim@v5
- name: install library dependencies
run: nimble install -y
- name: install doc dependencies
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d80f86c2..b3663764 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -10,16 +10,16 @@ jobs:
strategy:
matrix:
nim:
- - '1.6.x'
- 'stable'
- 'devel'
fail-fast: false
name: Nim ${{ matrix.nim }}
steps:
- uses: actions/checkout@v3
- - uses: jiro4989/setup-nim-action@v1.4.3
+ - uses: jiro4989/setup-nim-action@v2.2.2
with:
nim-version: ${{ matrix.nim }}
+ - run: sudo apt install libpcre3 libpcre3-dev
- run: nimble -y install
- run: nimble docsdeps
- run: nimble test
diff --git a/README.md b/README.md
index ab4584f8..72bb557b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,3 @@
-
# nimib 🐳 - nim 👑 driven ⛵ publishing ✍
Nimib provides an API to convert your Nim code and its outputs to html documents.
@@ -50,8 +49,6 @@ provides syntax highlighting of embedded languages in nimib documents (eg. markd
First have a look at the following html document: [hello.html](https://pietroppeter.github.io/nimib/hello.html)
This was produced with `nim r docsrc/hello`, where [docsrc/hello.nim](https://github.com/pietroppeter/nimib/blob/main/docsrc/hello.nim) is:
-
-
```nim
import strformat, strutils
import nimib
@@ -100,8 +97,6 @@ nbText:
nbSave
```
-
-
### Other examples of usage
in this repo:
@@ -228,16 +223,11 @@ that allow to override the above value, skip the config file or other options.
All the options available can be seen by running any nimib file with option `nbHelp`
(execution will stop after `nbInit`).
-
-
-
```nim
import osproc
-withDir nb.srcDir:
+withDir nb.doc.srcDir:
echo execProcess("nim r --verbosity:0 --hints:off --warnings:off hello --nbHelp")
```
-
-
```
Nimib options:
@@ -248,12 +238,9 @@ Nimib options:
--nbHomeDir, --nimibHomeDir set homeDir as relative (to CfgDir) or absolute; overrides config
--nbFilename, --nimibFilename overrides name of output file (e.g. somefile --nbFilename:othername.html)
--nbShow, --nimibShow open in browser at the end of nbSave
-```
-
-
-
+```
The value of options are available in `nb.options` field which also
tracks further options in `nb.options.other: seq[tuple[kind: CmdLineKind; name, value: string]]`.
@@ -371,5 +358,4 @@ because I made a [package](https://github.com/pietroppeter/nimoji) for that and
### why the Q & A?
because [someone made it into an art form](https://github.com/oakes/vim_cubed#q--a)
-and they tell me [imitation is the sincerest form of flattery](https://www.goodreads.com/quotes/558084-imitation-is-the-sincerest-form-of-flattery-that-mediocrity-can)
-
+and they tell me [imitation is the sincerest form of flattery](https://www.goodreads.com/quotes/558084-imitation-is-the-sincerest-form-of-flattery-that-mediocrity-can)
\ No newline at end of file
diff --git a/docsrc/allblocks.nim b/docsrc/allblocks.nim
index 460cb8de..a82a3a4c 100644
--- a/docsrc/allblocks.nim
+++ b/docsrc/allblocks.nim
@@ -4,17 +4,17 @@ import std/[strutils, strformat]
nbInit
-var nbToc: NbBlock
+var nbToc: NbText
template addToc =
- newNbBlock("nbText", false, nb, nbToc, ""):
- nbToc.output = "## Table of Contents:\n\n"
+ nbToc = newNbText(text="## Table of Contents:\n\n")
+ nb.add nbToc
-template nbCodeBlock(name:string) =
+template nbCodeBlock(name: string) =
let anchorName = name.toLower.replace(" ", "-")
nbText " \n### " & name & "\n\n---"
# see below, but any number works for a numbered list
- nbToc.output.add "1. " & name & " \n"
+ nbToc.text.add "1. " & name & " \n"
nbText: """
> This nimib document provides a brief description and example for
@@ -120,7 +120,7 @@ The `typ` parameter specifies the video's MIME type, and is optional!
"""
nimibCode:
- nbVideo(url="media/bad_apple!!.mp4", typ="video/mp4")
+ nbVideo(url="media/bad_apple!!.mp4", filetype="video/mp4")
nbCodeBlock: "nbAudio"
nbText: """
diff --git a/docsrc/cheatsheet.nim b/docsrc/cheatsheet.nim
index 3f10f990..ddb1fdb3 100644
--- a/docsrc/cheatsheet.nim
+++ b/docsrc/cheatsheet.nim
@@ -1,5 +1,5 @@
## Markdown cheatsheet with nimib
-import nimib, std/strutils
+import nimib, std/[strutils, strformat, json]
nbInit
nbText: """
@@ -15,34 +15,36 @@ nbText: """
"""
# customize source highlighting:
-nb.context["highlight"] = """
+nb.doc.context["highlight"] = %"""
"""
+
# a custom text block that shows markdown source
-template nbTextWithSource*(body: untyped) =
- newNbBlock("nbTextWithSource", false, nb, nb.blk, body):
- nb.blk.output = body
- nb.blk.context["code"] = body
+newNbBlock(NbTextWithSource of NbText):
+ toHtml:
+ result = withNewlines:
+ nbTextToHtml(blk, nb)
+ &"""
{blk.text} """
-nb.renderPlans["nbTextWithSource"] = @["mdOutputToHtml"]
-nb.partials["nbTextWithSource"] = """{{&outputToHtml}}
-{{code}} """
+template nbTextWithSource*(body: string) =
+ let blk = newNbTextWithSource(text=body)
+ nb.add blk
# how to add a ToC
var
- nbToc: NbBlock
+ nbToc: NbText
template addToc =
- newNbBlock("nbText", false, nb, nbToc, ""):
- nbToc.output = "# Table of Contents:\n\n"
+ nbToc = newNbText(text="# Table of Contents:\n\n")
+ nb.add nbToc
template nbSection(name:string) =
let anchorName = name.toLower.replace(" ", "-")
nbText " \n# " & name & "\n\n---"
# see below, but any number works for a numbered list
- nbToc.output.add "1. " & name & " \n"
+ nbToc.text.add "1. " & name & " \n"
# here is the start of the document
nbText """
@@ -100,7 +102,7 @@ nbTextWithSource """1. First ordered list item
* Unordered list can use asterisks
- Or minuses
+ Or pluses"""
-nb.blk.output = nb.blk.output.replace(dot, " ")
+nb.blk.NbTextWithSource.text = nb.blk.NbTextWithSource.text.replace(dot, " ")
nbText """
> in ordered to have the ordered sublist work correctly
diff --git a/docsrc/far/from/home.nim b/docsrc/far/from/home.nim
index 71b219c0..f3187b90 100644
--- a/docsrc/far/from/home.nim
+++ b/docsrc/far/from/home.nim
@@ -1,7 +1,7 @@
import nimib, strutils
nbInit
-nb.context["path_to_root"] = nb.context["path_to_root"].castStr.replace("/", r"\") # for CI since I run locally on Windows and CI runs on Linux: behaviour is the same
-nb.context["path_to_here"] = nb.context["path_to_here"].castStr.replace("/", r"\") # for CI since I run locally on Windows and CI runs on Linux: behaviour is the same
+nb.doc.context["path_to_root"] = %nb.doc.context{"path_to_root"}.getStr.replace("/", r"\") # for CI since I run locally on Windows and CI runs on Linux: behaviour is the same
+nb.doc.context["path_to_here"] = %nb.doc.context{"path_to_here"}.getStr.replace("/", r"\") # for CI since I run locally on Windows and CI runs on Linux: behaviour is the same
nb.partials["document"] = """
diff --git a/docsrc/index.nim b/docsrc/index.nim
index b9a4b6d0..cf2b37b1 100644
--- a/docsrc/index.nim
+++ b/docsrc/index.nim
@@ -12,7 +12,7 @@ nb.title = "Nimib Docs"
let
repo = "https://github.com/pietroppeter/nimib"
docs = if defined(mdOutput): "https://pietroppeter.github.io/nimib" else: "."
- hello = read(nb.srcDir / "hello.nim".RelativeFile)
+ hello = read(nb.doc.srcDir / "hello.nim".RelativeFile)
nbText: hlMdF"""
# nimib :whale: - nim :crown: driven :sailboat: publishing :writingHand:
@@ -65,13 +65,13 @@ provides syntax highlighting of embedded languages in nimib documents (eg. markd
First have a look at the following html document: [hello.html]({docs}/hello.html)
-This was produced with `nim r {nb.cfg.srcDir}/hello`, where [{nb.cfg.srcDir}/hello.nim]({repo}/blob/main/{nb.cfg.srcDir}/hello.nim) is:
+This was produced with `nim r {nb.doc.cfg.srcDir}/hello`, where [{nb.doc.cfg.srcDir}/hello.nim]({repo}/blob/main/{nb.doc.cfg.srcDir}/hello.nim) is:
""".emojize
when not defined(mdOutput):
nbCode: discard
- nb.blk.code = hello # "\n" should not be needed here (fix required in rendering)
+ nb.blk.NbCode.code = hello # "\n" should not be needed here (fix required in rendering)
else:
nbText &"""
```nim
@@ -124,7 +124,7 @@ The following are the main elements of a default nimib document:
* (optional) latex rendering through [katex](https://katex.org/) (see below)
* a header with navigation to a home page, a minimal title and an automatic detection of github repo (with link)
* a footer with a "made with nimib" line and a `Show source` button that shows the full source to create the document.
-* (optional) possibility to create a markdown version of the same document (see this document for an example: [{nb.cfg.srcDir}/index.nim]({repo}/blob/main/{nb.cfg.srcDir}/index.nim))
+* (optional) possibility to create a markdown version of the same document (see this document for an example: [{nb.doc.cfg.srcDir}/index.nim]({repo}/blob/main/{nb.doc.cfg.srcDir}/index.nim))
Customization over the default is mostly achieved through nim-mustache or changing
`NbDoc` and `NbBlock` elements (see below api).
@@ -210,7 +210,7 @@ All the options available can be seen by running any nimib file with option `nbH
nbCode:
import osproc
- withDir nb.srcDir:
+ withDir nb.doc.srcDir:
echo execProcess("nim r --verbosity:0 --hints:off --warnings:off hello --nbHelp")
let renderProcType = "type NbRenderProc = proc (doc: var NbDoc, blk: var NbBlock) {. nimcall .}"
@@ -338,7 +338,7 @@ and they tell me [imitation is the sincerest form of flattery](https://www.goodr
""".emojize
when defined(mdOutput):
- nb.filename = "../README.md"
+ nb.doc.filename = "../README.md"
nbSave
else:
nbSave
diff --git a/docsrc/nolan.nim b/docsrc/nolan.nim
index 4c74069b..0226cb9b 100644
--- a/docsrc/nolan.nim
+++ b/docsrc/nolan.nim
@@ -1,5 +1,5 @@
import nimib
-import strutils, sequtils, strformat
+import strutils, sequtils, strformat, json
nbInit
nbText: """
@@ -11,7 +11,7 @@ nbText: """
>
> It reorders a **Limerick** according to Nolan's [Memento film structure](https://en.wikipedia.org/wiki/Memento_(film)#Film_structure).
"""
-let filename_variant = nb.filename.replace(".html", "_no_source.html")
+let filename_variant = nb.doc.filename.replace(".html", "_no_source.html")
let iChange = nb.blocks.len
nbText:fmt"""
> Click on `Show Source` at the bottom to see the nim file that generates this document.
@@ -48,11 +48,11 @@ nbSave
# save another document without source to test the opt-out of Show Source feature
nbText: "---\n> This is how we can remove the `Show Source` functionality"
nbCode:
- nb.context["no_source"] = true
+ nb.doc.context["no_source"] = %true
# we will generate this last block to change a previous block and then remove it
nbText:fmt"""
-> To see the variant with Show Source [click here]({(nb.filename.AbsoluteFile).relPath})
+> To see the variant with Show Source [click here]({(nb.doc.filename.AbsoluteFile).relPath})
"""
-nb.blocks[iChange] = nbDoc.blocks.pop
-nb.filename = filename_variant
+nb.blocks[iChange] = nb.blocks.pop
+nb.doc.filename = filename_variant
nbSave
\ No newline at end of file
diff --git a/docsrc/numerical.nim b/docsrc/numerical.nim
index 0c4b310d..5708efd7 100644
--- a/docsrc/numerical.nim
+++ b/docsrc/numerical.nim
@@ -1,16 +1,16 @@
#remember to run also with -d:numericalDefaultStyle
-import nimib, strformat, strutils
+import nimib, strformat, strutils, json
nbInit
nb.useLatex
let
- filename_default_style = nb.filename.replace(".html", "_default_style.html")
- default_style = nb.context["stylesheet"]
+ filename_default_style = nb.doc.filename.replace(".html", "_default_style.html")
+ default_style = nb.doc.context["stylesheet"]
-nb.context["stylesheet"] = """ """
+nb.doc.context["stylesheet"] = %""" """
proc introText(defaultStyle = false): string =
let otherStyle = if defaultStyle:
- fmt"; _you are looking at default style, for custom style [click here]({(nb.filename.AbsoluteFile).relPath})_"
+ fmt"; _you are looking at default style, for custom style [click here]({(nb.doc.filename.AbsoluteFile).relPath})_"
else:
fmt"; _for default style [click here]({(filename_default_style.AbsoluteFile).relPath})_"
@@ -26,7 +26,7 @@ proc introText(defaultStyle = false): string =
nbText: introText()
# save to change later
-var blockIntroText = nb.blk
+var blockIntroText = nb.blk.NbText
nbText: fmt"""# Using NumericalNim
@@ -128,17 +128,17 @@ $h=0.01$ | {pe y3hn[^1]} | {pe y3rk[^1]}
and here is the code for this Markdown block:
""" # right alignment of numbers does not seem to work. is this an issue of latex.css?
-let mdCode = nb.blk.code
+let mdCode = nb.blk.NbTextWithCode.code
nbCode:
discard
-nb.blk.code = mdCode
+nb.blk.NbCode.code = mdCode
# add a show Markdown source. It would be nice when hovering a code block to show (on the side? how to do it on mobile?) the call code (nbText: or other)
# first save with latex style
nbSave
# then save with default style
-blockIntroText.output = introText(default_style=true)
-nb.filename = filename_default_style
-nb.context["stylesheet"] = default_style
+blockIntroText.text = introText(default_style=true)
+nb.doc.filename = filename_default_style
+nb.doc.context["stylesheet"] = default_style
nbSave
diff --git a/docsrc/pythno.nim b/docsrc/pythno.nim
index ac4fd6d9..27e946dd 100644
--- a/docsrc/pythno.nim
+++ b/docsrc/pythno.nim
@@ -25,8 +25,9 @@ nbCode:
echo repeat("ThisIsNotPython", 6)
-nb.blk.context["output"] = camel2snake(nb.blk.output)
-nb.blk.code = nb.blk.code.multiReplace([
+let codeBlock = nb.blk.NbCode
+codeBlock.output = camel2snake(codeBlock.output)
+codeBlock.code = codeBlock.code.multiReplace([
("proc", "def"),
("string", "str"),
(";", ","), # tricky one
diff --git a/src/nimib.nim b/src/nimib.nim
index 372d2457..7054618d 100644
--- a/src/nimib.nim
+++ b/src/nimib.nim
@@ -1,67 +1,64 @@
-import std/[os, strutils, sugar, strformat, macros, macrocache, sequtils, jsonutils]
-export jsonutils
-import nimib / [types, blocks, docs, boost, config, options, capture, jsutils, logging]
-export types, blocks, docs, boost, sugar, jsutils
+import std/[os, strutils, sugar, strformat, macros, macrocache, sequtils, json]
+import std / jsonutils except toJson
+export jsonutils except toJson
+import markdown
+import nimib / [types, blocks, docs, boost, config, options, capture, jsons, globals, jsutils, nimibSugars, sources, highlight, logging, renders, builtinBlocks]
+export types, blocks, docs, boost, sugar, globals, nimibSugars, jsutils, sources, highlight, jsons, renders, builtinBlocks
# types exports mustache, tables, paths
from nimib / themes import nil
export themes.useLatex, themes.darkMode, themes.`title=`, themes.disableHighlightJs
-from nimib / renders import nil
-
from mustachepkg/values import searchTable, searchDirs, castStr
export searchTable, searchDirs, castStr
-template moduleAvailable*(module: untyped): bool =
- (compiles do: import module)
-
-template nbInit*(theme = themes.useDefault, backend = renders.useHtmlBackend, thisFileRel = "") =
- var nb {.inject.}: NbDoc
- nb.initDir = getCurrentDir().AbsoluteDir
- loadOptions nb
- loadCfg nb
+template nbInit*(theme: proc (nb: var Nb) = themes.useDefault, renderer: NbRender = nbToHtml, thisFileRel = "") =
+ var nb {.inject.}: Nb
+ nb.doc = NbDoc(kind: "NbDoc")
+ nb.doc.initDir = getCurrentDir().AbsoluteDir
+ loadOptions nb.doc
+ loadCfg nb.doc
# nbInit can be called not from inside the correct file (e.g. when rendering markdown files in nimibook)
if thisFileRel == "":
- nb.thisFile = instantiationInfo(-1, true).filename.AbsoluteFile
+ nb.doc.thisFile = instantiationInfo(-1, true).filename.AbsoluteFile
else:
- nb.thisFile = nb.srcDir / thisFileRel.RelativeFile
- log "thisFile: " & $nb.thisFile
+ nb.doc.thisFile = nb.doc.srcDir / thisFileRel.RelativeFile
+ log "thisFile: " & $nb.doc.thisFile
try:
- nb.source = read(nb.thisFile)
+ nb.doc.source = read(nb.doc.thisFile)
except IOError:
log "cannot read source"
- if nb.options.filename == "":
- nb.filename = nb.thisFile.string.splitFile.name & ".html"
+ if nb.doc.options.filename == "":
+ nb.doc.filename = nb.doc.thisFile.string.splitFile.name & ".html"
else:
- nb.filename = nb.options.filename
+ nb.doc.filename = nb.doc.options.filename
- if nb.cfg.srcDir != "":
- log "srcDir: " & $nb.srcDir
- nb.filename = (nb.thisDir.relativeTo nb.srcDir).string / nb.filename
- log "filename: " & nb.filename
+ if nb.doc.cfg.srcDir != "":
+ log "srcDir: " & $nb.doc.srcDir
+ nb.doc.filename = (nb.doc.thisDir.relativeTo nb.doc.srcDir).string / nb.doc.filename
+ log "filename: " & nb.doc.filename
- if nb.cfg.homeDir != "":
- if not dirExists(nb.homeDir):
- log "creating nb.homeDir: " & $nb.homeDir
- createDir(nb.homeDir)
-
- log "setting current directory to nb.homeDir: " & $nb.homeDir
+ if nb.doc.cfg.homeDir != "":
+ if not dirExists(nb.doc.homeDir):
+ log "creating nb.homeDir: " & $nb.doc.homeDir
+ createDir(nb.doc.homeDir)
- setCurrentDir nb.homeDir
+ log "setting current directory to nb.doc.homeDir: " & $nb.doc.homeDir
+ setCurrentDir nb.doc.homeDir
# can be overriden by theme, but it is better to initialize this anyway
- nb.templateDirs = @["./", "./templates/"]
- nb.partials = initTable[string, string]()
- nb.context = newContext(searchDirs = @[]) # templateDirs and partials added during nbSave
+ #nb.templateDirs = @["./", "./templates/"]
+ #nb.partials = initTable[string, string]()
+ nb.doc.context = newJObject() #newContext(searchDirs = @[]) # templateDirs and partials added during nbSave
# apply render backend (default backend can be overriden by theme)
- backend nb
+ nb.backend = renderer
# apply theme
- theme nb
+ theme nb # how do we handle themes?
template nbInitMd*(thisFileRel = "") =
var tfr = if thisFileRel == "":
@@ -69,216 +66,20 @@ template nbInitMd*(thisFileRel = "") =
else:
thisFileRel
- nbInit(backend=renders.useMdBackend, theme=themes.noTheme, tfr)
+ nbInit(renderer=nbToMd, theme=themes.noTheme, tfr)
- if nb.options.filename == "":
- nb.filename = nb.filename.splitFile.name & ".md"
+ if nb.doc.options.filename == "":
+ nb.doc.filename = nb.doc.filename.splitFile.name & ".md"
-# block generation templates
+#[ # block generation templates
template newNbCodeBlock*(cmd: string, body, blockImpl: untyped) =
newNbBlock(cmd, true, nb, nb.blk, body, blockImpl)
template newNbSlimBlock*(cmd: string, blockImpl: untyped) =
# a slim block is a block with no body
- newNbBlock(cmd, false, nb, nb.blk, "", blockImpl)
-
-# block templates
-template nbCode*(body: untyped) =
- newNbCodeBlock("nbCode", body):
- captureStdout(nb.blk.output):
- body
-
-template nbCodeSkip*(body: untyped) =
- newNbCodeBlock("nbCodeSkip", body):
- discard
-
-template nbCapture*(body: untyped) =
- newNbCodeBlock("nbCapture", body):
- captureStdout(nb.blk.output):
- body
-
-template nbCodeInBlock*(body: untyped): untyped =
- block:
- nbCode:
- body
-
-template nimibCode*(body: untyped) =
- newNbCodeBlock("nimibCode", body):
- discard
- body
-
-template nbText*(text: string) =
- newNbSlimBlock("nbText"):
- nb.blk.output = text
-
-template nbTextWithCode*(body: untyped) =
- newNbCodeBlock("nbText", body):
- nb.blk.output = body
-
-template nbImage*(url: string, caption = "", alt = "") =
- newNbSlimBlock("nbImage"):
- nb.blk.context["url"] = nb.relToRoot(url)
- nb.blk.context["alt_text"] =
- if alt == "":
- caption
- else:
- alt
-
- nb.blk.context["caption"] = caption
-
-# todo captions and subtitles support maybe?
-template nbVideo*(url: string, typ: string = "", autoplay = false, muted = false, loop: bool = false) =
- newNbSlimBlock("nbVideo"):
- nb.blk.context["url"] = nb.relToRoot(url)
- nb.blk.context["type"] =
- if typ == "": "video/" & url.splitFile.ext[1..^1] # remove the leading dot
- else: typ
-
- if autoplay: nb.blk.context["autoplay"] = "autoplay"
- if muted: nb.blk.context["muted"] = "muted"
- if loop: nb.blk.context["loop"] = "loop"
-
-template nbAudio*(url: string, typ: string = "", autoplay = false, muted = false, loop: bool = false) =
- newNbSlimBlock("nbAudio"):
- nb.blk.context["url"] = nb.relToRoot(url)
- nb.blk.context["type"] =
- if typ == "": "audio/" & url.splitFile.ext[1..^1]
- else: typ
-
- if autoplay: nb.blk.context["autoplay"] = "autoplay"
- if muted: nb.blk.context["muted"] = "muted"
- if loop: nb.blk.context["loop"] = "loop"
-
-template nbFile*(name: string, content: string) =
- ## Generic string file
- newNbSlimBlock("nbFile"):
- name.writeFile content
- nb.blk.context["filename"] = name
- nb.blk.context["ext"] = name.getExt
- nb.blk.context["content"] = content
-
-template nbFile*(name: string, body: untyped) =
- newNbCodeBlock("nbFile", body):
- name.writeFile nb.blk.code
- nb.blk.context["filename"] = name
- nb.blk.context["ext"] = name.getExt
- nb.blk.context["content"] = nb.blk.code
-
-template nbFile*(name: string) =
- ## Read content from a file instead of writing to it
- newNbSlimBlock("nbFile"):
- nb.blk.context["filename"] = name
- nb.blk.context["ext"] = name.getExt
- nb.blk.context["content"] = readFile(name)
-
-when moduleAvailable(nimpy):
- template nbInitPython*() =
- import nimpy
- let nbPythonBuiltins = pyBuiltinsModule()
-
- template nbPython(pythonStr: string) =
- newNbSlimBlock("nbPython"):
- nb.blk.code = pythonStr
- captureStdout(nb.blk.output):
- discard nbPythonBuiltins.exec(pythonStr)
-
-template nbShow*(obj: untyped) =
- nbRawHtml(obj.toHtml())
-
-template nbRawOutput*(content: string) {.deprecated: "Use nbRawHtml instead".} =
- nbRawHtml(content)
-
-template nbRawHtml*(content: string) =
- newNbSlimBlock("nbRawHtml"):
- nb.blk.output = content
-
-template nbJsFromStringInit*(body: string): NbBlock =
- var result = NbBlock(command: "nbJsFromCode", code: body, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
- result.context["transformedCode"] = body
- result.context["putAtTop"] = false
- result
-
-template addStringToJs*(script: NbBlock, body: string) =
- script.code &= "\n" & body
- script.context["transformedCode"] = script.context["transformedCode"].vString & "\n" & body
-
-template addToDocAsJs*(script: NbBlock) =
- nb.blocks.add script
- nb.blk = script
-
-template nbJsFromString*(body: string) =
- let script = nbJsFromStringInit(body)
- script.addToDocAsJs
-
-template nbJsFromCode*(args: varargs[untyped]) =
- let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
- var result = NbBlock(command: "nbJsFromCode", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
- result.context["transformedCode"] = code
- result.context["putAtTop"] = false
- result.addToDocAsJs
-
-template nbJsFromCodeInBlock*(args: varargs[untyped]) =
- let (code, originalCode) = nimToJsString(putCodeInBlock=true, args)
- var result = NbBlock(command: "nbJsFromCode", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
- result.context["transformedCode"] = code
- result.context["putAtTop"] = false
- result.addToDocAsJs
-
-template nbJsFromCodeGlobal*(args: varargs[untyped]) =
- let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
- var result = NbBlock(command: "nbJsFromCode", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
- result.context["transformedCode"] = code
- result.context["putAtTop"] = true
- result.addToDocAsJs
-
-template nbJsFromCodeOwnFile*(args: varargs[untyped]) =
- let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
- var result = NbBlock(command: "nbJsFromCodeOwnFile", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
- result.context["transformedCode"] = code
- result.addToDocAsJs
-
-template nbCodeToJs*(args: varargs[untyped]) {.deprecated: "Use nbJsFromCode or nbJsFromString instead".} =
- nbJsFromCode(args)
-
-
-when moduleAvailable(karax/kbase):
- template nbKaraxCode*(args: varargs[untyped]) =
- let rootId = "karax-" & $nb.newId()
- nbRawHtml: "
"
- nbKaraxCodeBackend(rootId, args)
-
-when moduleAvailable(happyx):
- template nbHappyxCode*(args: varargs[untyped]) =
- let rootId = "happyx-" & $nb.newId()
- nbRawHtml: "
"
- nbHappyxCodeBackend(rootId, args)
-
-template nbJsShowSource*(message: string = "") {.deprecated: "Use nbCodeDisplay instead".} =
- nb.blk.context["js_show_nim_source"] = true
- if message.len > 0:
- nb.blk.context["js_show_nim_source_message"] = message
-
-template nbCodeToJsShowSource*(message: string = "") {.deprecated: "Use nbCodeDisplay instead".} =
- nbJsShowSource(message)
-
-template nbCodeDisplay*(tmplCall: untyped, body: untyped) =
- ## display codes used in a template (e.g. nbJsFromCode) after the template call
- tmplCall:
- body
- newNbCodeBlock("nbCode", body):
- discard
+ newNbBlock(cmd, false, nb, nb.blk, "", blockImpl) ]#
-template nbCodeAnd*(tmplCall: untyped, body: untyped) =
- ## can be used to run code both in c and js backends (e.g. nbCodeAnd(nbJsFromCode))
- nbCode: # this should work because template name starts with nbCode
- body
- tmplCall:
- body
-template nbClearOutput*() =
- if not nb.blk.isNil:
- nb.blk.output = ""
- nb.blk.context["output"] = ""
template nbSave* =
# order if searchDirs/searchTable is relevant: directories have higher priority. rationale:
@@ -287,21 +88,21 @@ template nbSave* =
# - in case you need to manage additional exceptions for a specific document add a new set of partials before calling nbSave
nb.nbCollectAllNbJs()
- nb.context.searchDirs(nb.templateDirs)
- nb.context.searchTable(nb.partials)
+ #nb.context.searchDirs(nb.templateDirs)
+ #nb.context.searchTable(nb.partials)
write nb
- if nb.options.show:
- open nb
+ if nb.doc.options.show:
+ open nb.doc
# how to change this to a better version using nb?
template relPath*(path: AbsoluteFile | AbsoluteDir): string =
- (path.relativeTo nb.homeDir).string
+ (path.relativeTo nb.doc.homeDir).string
# aliases to minimize breaking changes after refactoring nbDoc -> nb. Should be deprecated at some point?
-template nbDoc*: NbDoc = nb
+#[ template nbDoc*: NbDoc = nb
template nbBlock*: NbBlock = nb.blk
-template nbHomeDir*: AbsoluteDir = nb.homeDir
+template nbHomeDir*: AbsoluteDir = nb.homeDir ]#
# use --nbShow runtime option instead of this
template nbShow* =
diff --git a/src/nimib/blocks.nim b/src/nimib/blocks.nim
index ecbc662b..f7a05533 100644
--- a/src/nimib/blocks.nim
+++ b/src/nimib/blocks.nim
@@ -16,7 +16,28 @@ func nbNormalize*(text: string): string =
text.replace("\c\l", "\n").replace("\c", "\n").strip # this could be made more efficient
# note that: '\c' == '\r' and '\l' == '\n'
-template newNbBlock*(cmd: string, readCode: static[bool], nbDoc, nbBlock, body, blockImpl: untyped) =
+proc add*(nb: var Nb, blk: NbBlock) =
+ nb.blk = blk
+ if nb.containers.len == 0:
+ nb.doc.blocks.add blk
+ else:
+ nb.containers[^1].blocks.add blk
+
+template withContainer*(nb: var Nb, container: NbContainer, body: untyped) =
+ # nb.add container # should this be here?
+ nb.containers.add container
+ body
+ discard nb.containers.pop
+
+func blocksFlattened*(doc: NbContainer): seq[NbBlock] =
+ for blk in doc.blocks:
+ result.add blk
+ if blk of NbContainer:
+ result.add blk.NbContainer.blocksFlattened()
+
+# do we need this anymore? How do we implement something similar now? I'm thinking mainly of the logging
+# insert it into the newBlockName procs?
+template newNbBlockOld*(cmd: string, readCode: static[bool], nbDoc, nbBlock, body, blockImpl: untyped) =
nbBlock = NbBlock(command: cmd, context: newContext(searchDirs = @[], partials = nbDoc.partials))
when readCode:
nbBlock.code = nbNormalize:
diff --git a/src/nimib/builtinBlocks.nim b/src/nimib/builtinBlocks.nim
new file mode 100644
index 00000000..1f2e1fd6
--- /dev/null
+++ b/src/nimib/builtinBlocks.nim
@@ -0,0 +1,525 @@
+import std / [strformat, os, strutils, json]
+import std / jsonutils except toJson
+
+import ./types, ./blocks, ./capture, ./nimibSugars, ./globals, ./jsons, ./paths, ./docs, ./jsutils
+
+template moduleAvailable*(module: untyped): bool =
+ (compiles do: import module)
+
+# block templates
+#[ template nbCode*(body: untyped) =
+ newNbCodeBlock("nbCode", body):
+ captureStdout(nb.blk.output):
+ body ]#
+
+newNbBlock(NbCode of NbContainer):
+ code: string
+ output: string
+ toHtml:
+ withNewlines:
+ nb.renderPartial("nbCodeSource", jsonutils.toJson(blk))
+ nbContainerToHtml(blk, nb)
+ nb.renderPartial("nbCodeOutput", jsonutils.toJson(blk))
+
+proc nbCodeToMd(blk: NbBlock, nb: Nb): string =
+ let blk = blk.NbCode
+ withNewlines:
+ if blk.code.len > 0:
+ withNewLines:
+ "```nim"
+ blk.code
+ "```"
+ if blk.output.len > 0:
+ withNewLines:
+ "```"
+ blk.output
+ "```"
+
+nbToMd.funcs["NbCode"] = nbCodeToMd
+
+
+template code*(nb: Nb, body: untyped) =
+ let blk = newNbCode()
+ blk.code = getCode(body)
+ nb.withContainer(blk):
+ captureStdout(blk.output):
+ body
+ nb.add blk
+
+template nbCode*(body: untyped) =
+ nb.code:
+ body
+
+template codeSkip*(nb: Nb, body: untyped) =
+ let blk = newNbCode()
+ blk.code = getCode(body)
+ nb.add blk
+
+template nbCodeSkip*(body: untyped) =
+ nb.codeSkip(body)
+
+#[ template nbCodeSkip*(body: untyped) =
+ newNbCodeBlock("nbCodeSkip", body):
+ discard ]#
+
+template capture*(nb: Nb, body: untyped) =
+ let blk = newNbCode()
+ nb.withContainer(blk):
+ captureStdout(blk.output):
+ body
+ nb.add blk
+
+template nbCapture*(body: untyped) =
+ nb.capture(body)
+
+#[ template nbCapture*(body: untyped) =
+ newNbCodeBlock("nbCapture", body):
+ captureStdout(nb.blk.output):
+ body ]#
+
+template codeInBlock*(nb: Nb, body: untyped) =
+ block:
+ nb.code:
+ body
+
+template nbCodeInBlock*(body: untyped): untyped =
+ nb.codeInBlock:
+ body
+
+template nimibCode*(body: untyped) =
+ nbCode:
+ body
+
+#[ template nimibCode*(body: untyped) =
+ newNbCodeBlock("nimibCode", body):
+ discard
+ body ]#
+
+#[ template nbText*(text: string) =
+ newNbSlimBlock("nbText"):
+ nb.blk.output = text ]#
+
+newNbBlock(NbText):
+ text: string
+ toHtml:
+ nb.renderPartial("nbText", jsonutils.toJson(blk))
+
+func nbTextToMd*(blk: NbBlock, nb: Nb): string =
+ blk.NbText.text
+
+nbToMd.funcs["NbText"] = nbTextToMd
+
+func text*(nb: var Nb, text: string) =
+ let blk = newNbText(text=text)
+ nb.add blk
+
+template nbText*(ttext: string) =
+ nb.text(ttext)
+
+#[ template nbTextWithCode*(body: untyped) =
+ newNbCodeBlock("nbText", body):
+ nb.blk.output = body ]#
+
+newNbBlock(NbTextWithCode of NbText):
+ code: string
+ toHtml:
+ nbTextToHtml(blk, nb)
+
+func nbTextWithCodeToMd*(blk: NbBlock, nb: Nb): string =
+ blk.NbTextWithCode.text
+
+nbToMd.funcs["NbTextWithCode"] = nbTextWithCodeToMd
+
+template textWithCode*(nb: Nb, body: untyped) =
+ let ttext = body
+ let tcode = getCode(body)
+ let blk = newNbTextWithCode(text=ttext, code=tcode)
+ nb.add blk
+
+template nbTextWithCode*(body: untyped) =
+ nb.textWithCode:
+ body
+
+newNbBlock(NbImage):
+ url: string
+ caption: string
+ alt: string
+ toHtml:
+ &"""
+
+
+ {blk.caption}
+
+"""
+
+func nbImageToMd*(blk: NbBlock, nb: Nb): string =
+ let blk = blk.NbImage
+ withNewLines:
+ &""
+ if blk.caption.len > 0:
+ withNewLines:
+ ""
+ &"**Figure:** {blk.caption}"
+
+nbToMd.funcs["NbImage"] = nbImageToMd
+
+func image*(nb: var Nb, turl: string, tcaption = "", talt = "") =
+ let blk = newNbImage()
+ blk.url =
+ if isAbsolute(turl) or turl.startsWith("http"):
+ turl
+ else:
+ nb.doc.context{"path_to_root"}.getStr / turl
+ blk.alt = if talt.len == 0: tcaption else: talt
+ blk.caption = tcaption
+ nb.add blk
+
+template nbImage*(url: string, caption = "", alt = "") =
+ nb.image(url, caption, alt)
+
+#[ template nbImage*(url: string, caption = "", alt = "") =
+ newNbSlimBlock("nbImage"):
+ nb.blk.context["url"] = nb.relToRoot(url)
+ nb.blk.context["alt_text"] =
+ if alt == "":
+ caption
+ else:
+ alt
+
+ nb.blk.context["caption"] = caption ]#
+
+newNbBlock(NbFile):
+ filename: string
+ ext: string
+ content: string
+ toHtml:
+ &"""
+{blk.filename}
+{blk.content}
+"""
+# Make pre-code with hljs-extensions into a partial! Or a function that calls a partial even? (that constructs the JsNode for us)
+
+proc file*(nb: var Nb, tname: string, tcontent: string) =
+ ## Generic string file
+ tname.writeFile tcontent
+ let blk = newNbFile(filename=tname, ext=tname.getExt, content=tcontent)
+ nb.add blk
+
+template file*(nb: Nb, tname: string, body: untyped) =
+ ## Read code and write it to file
+ let content = getCode(body)
+ tname.writeFile content
+ let blk = newNbFile(filename=tname, ext=tname.getExt, content=content)
+ nb.add blk
+
+proc file*(nb: var Nb, tname: string) =
+ ## Read content from a file instead of writing to it
+ let content = readFile(tname)
+ let blk = newNbFile(filename=tname, ext=tname.getExt, content=content)
+ nb.add blk
+
+newNbBlock(NbVideo):
+ url: string
+ filetype: string
+ autoplay: bool
+ muted: bool
+ loop: bool
+ toHtml:
+ withNewLines:
+ ""
+ &""" 0:
+ &"""type="{blk.filetype}" """
+ ">"
+ "Your browser does not support the video element. "
+
+func video*(nb: var Nb, url: string, filetype: string = "", autoplay = false, muted = false, loop: bool = false) =
+ let blk = newNbVideo(autoplay=autoplay, muted=muted, loop=loop)
+ blk.url = nb.doc.relToRoot(url)
+ blk.filetype =
+ if filetype == "": "video/" & url.splitFile.ext[1..^1] # remove the leading dot
+ else: filetype
+ nb.add blk
+
+# todo captions and subtitles support maybe?
+template nbVideo*(url: string, filetype: string = "", autoplay = false, muted = false, loop: bool = false) =
+ nb.video(url, filetype, autoplay, muted, loop)
+
+newNbBlock(NbAudio of NbVideo):
+ toHtml:
+ withNewLines:
+ ""
+ &""" 0:
+ &"""type="{blk.filetype}" """
+ ">"
+ "Your browser does not support the video element. "
+
+func audio*(nb: var Nb, url: string, filetype: string = "", autoplay = false, muted = false, loop: bool = false) =
+ let blk = newNbAudio(autoplay=autoplay, muted=muted, loop=loop)
+ blk.url = nb.doc.relToRoot(url)
+ blk.filetype =
+ if filetype == "": "audio/" & url.splitFile.ext[1..^1] # remove the leading dot
+ else: filetype
+ nb.add blk
+
+template nbAudio*(url: string, filetype: string = "", autoplay = false, muted = false, loop: bool = false) =
+ nb.audio(url, filetype, autoplay, muted, loop)
+
+template nbFile*(name: string, content: string) =
+ nb.file(name, content)
+
+template nbFile*(name: string, body: untyped) =
+ nb.file(name, body)
+
+template nbFile*(name: string) =
+ nb.file(name)
+
+#[ template nbFile*(name: string, content: string) =
+ ## Generic string file
+ newNbSlimBlock("nbFile"):
+ name.writeFile content
+ nb.blk.context["filename"] = name
+ nb.blk.context["ext"] = name.getExt
+ nb.blk.context["content"] = content
+
+template nbFile*(name: string, body: untyped) =
+ newNbCodeBlock("nbFile", body):
+ name.writeFile nb.blk.code
+ nb.blk.context["filename"] = name
+ nb.blk.context["ext"] = name.getExt
+ nb.blk.context["content"] = nb.blk.code
+
+template nbFile*(name: string) =
+ ## Read content from a file instead of writing to it
+ newNbSlimBlock("nbFile"):
+ nb.blk.context["filename"] = name
+ nb.blk.context["ext"] = name.getExt
+ nb.blk.context["content"] = readFile(name) ]#
+
+when moduleAvailable(nimpy):
+ newNbBlock(NbPython of NbCode):
+ toHtml:
+ withNewlines:
+ if blk.code.len > 0:
+ &"{blk.code} "
+ if blk.output.len > 0:
+ &"{blk.output} "
+
+ template nbInitPython*() =
+ import nimpy
+ let nbPythonBuiltins = pyBuiltinsModule()
+
+ proc python(nb: var Nb, pythonStr: string) =
+ let blk = newNbPython(code = pythonStr)
+ captureStdout(blk.output):
+ discard nbPythonBuiltins.exec(pythonStr)
+ nb.add blk
+
+ template nbPython(pythonStr: string) =
+ nb.python(pythonStr)
+
+ #[ template nbPython(pythonStr: string) =
+ newNbSlimBlock("nbPython"):
+ nb.blk.code = pythonStr
+ captureStdout(nb.blk.output):
+ discard nbPythonBuiltins.exec(pythonStr) ]#
+
+newNbBlock(NbRawHtml):
+ html: string
+ toHtml:
+ blk.html
+
+func rawHtml*(nb: var Nb, content: string) =
+ let blk = newNbRawHtml(html = content)
+ nb.add blk
+
+template nbRawHtml*(content: string) =
+ nb.rawHtml(content)
+
+func show*[T](nb: var Nb, obj: T) =
+ nb.rawHtml(obj.toHtml())
+
+template nbShow*(obj: untyped) =
+ nb.show(obj)
+
+newNbBlock(NbDiv of NbContainer):
+ class: string
+ style: string
+ toHtml:
+ withNewLines:
+ &""
+ nbContainerToHtml(blk, nb)
+ "
"
+
+template nbDiv*(classes: string, styles: string, body: untyped) =
+ let blk = newNbDiv(class=classes, style=styles)
+ nb.withContainer(blk):
+ body
+ nb.add blk
+
+template nbDiv*(body: untyped) =
+ nbDiv("", ""):
+ body
+
+#[ template nbShow*(obj: untyped) =
+ nbRawHtml(obj.toHtml())
+
+template nbRawOutput*(content: string) {.deprecated: "Use nbRawHtml instead".} =
+ nbRawHtml(content)
+
+template nbRawHtml*(content: string) =
+ newNbSlimBlock("nbRawHtml"):
+ nb.blk.output = content ]#
+
+func nbJsFromStringInit*(body: string): NbBlock =
+ newNbJsFromCode(code=body, transformedCode=body, putAtTop=false)
+
+#[ template nbJsFromStringInit*(body: string): NbBlock =
+ var result = NbBlock(command: "nbJsFromCode", code: body, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
+ result.context["transformedCode"] = body
+ result.context["putAtTop"] = false
+ result ]#
+
+func addStringToJs*(script: NbJsFromCode or NbJsFromCodeOwnFile, body: string) =
+ script.code &= "\n" & body
+ script.transformedCode &= "\n" & body
+
+#[ template addStringToJs*(script: NbBlock, body: string) =
+ script.code &= "\n" & body
+ script.context["transformedCode"] = script.context["transformedCode"].vString & "\n" & body ]#
+
+#[ func addToDocAsJs*(nb: var Nb, script: NbBlock) =
+ nb.add script
+
+template addToDocAsJs*(script: NbBlock) =
+ nb.blocks.add script
+ nb.blk = script ]#
+
+func jsFromString*(nb: var Nb, body: string) =
+ let script = nbJsFromStringInit(body)
+ nb.add script
+
+template nbJsFromString*(body: string) =
+ nb.jsFromString(body)
+
+template jsFromCode*(nb: var Nb, args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
+ let blk = newNbJsFromCode(code=originalCode, transformedCode=code, putAtTop=false)
+ nb.add blk
+
+template nbJsFromCode*(args: varargs[untyped]) =
+ nb.jsFromCode(args)
+
+template jsFromCodeInBlock*(nb: var Nb, args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=true, args)
+ let blk = newNbJsFromCode(code=originalCode, transformedCode=code, putAtTop=false)
+ nb.add blk
+
+template nbJsFromCodeInBlock*(args: varargs[untyped]) =
+ nb.jsFromCodeInBlock(args)
+
+template jsFromCodeGlobal*(nb: var Nb, args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
+ let blk = newNbJsFromCode(code=originalCode, transformedCode=code, putAtTop=true)
+ nb.add blk
+
+template nbJsFromCodeGlobal*(args: varargs[untyped]) =
+ nb.jsFromCodeGlobal(args)
+
+template jsFromCodeOwnFile*(nb: var Nb, args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
+ let blk = newNbJsFromCodeOwnFile(code=originalCode, transformedCode=code)
+ nb.add blk
+
+template nbJsFromCodeOwnFile*(args: varargs[untyped]) =
+ nb.jsFromCodeOwnFile(args)
+
+#[ template nbJsFromCode*(args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
+ var result = NbBlock(command: "nbJsFromCode", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
+ result.context["transformedCode"] = code
+ result.context["putAtTop"] = false
+ result.addToDocAsJs
+
+template nbJsFromCodeInBlock*(args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=true, args)
+ var result = NbBlock(command: "nbJsFromCode", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
+ result.context["transformedCode"] = code
+ result.context["putAtTop"] = false
+ result.addToDocAsJs
+
+template nbJsFromCodeGlobal*(args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
+ var result = NbBlock(command: "nbJsFromCode", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
+ result.context["transformedCode"] = code
+ result.context["putAtTop"] = true
+ result.addToDocAsJs
+
+template nbJsFromCodeOwnFile*(args: varargs[untyped]) =
+ let (code, originalCode) = nimToJsString(putCodeInBlock=false, args)
+ var result = NbBlock(command: "nbJsFromCodeOwnFile", code: originalCode, context: newContext(searchDirs = @[], partials = nb.partials), output: "")
+ result.context["transformedCode"] = code
+ result.addToDocAsJs
+
+template nbCodeToJs*(args: varargs[untyped]) {.deprecated: "Use nbJsFromCode or nbJsFromString instead".} =
+ nbJsFromCode(args) ]#
+
+
+when moduleAvailable(karax/kbase):
+ template karaxCode*(nb: var Nb, args: varargs[untyped]) =
+ let rootId = "karax-" & $nb.doc.newId()
+ nb.rawHtml: "
"
+ nbKaraxCodeBackend(rootId, args)
+
+ template nbKaraxCode*(args: varargs[untyped]) =
+ nb.karaxCode(args)
+
+when moduleAvailable(happyx):
+ template happyxCode*(nb: var Nb, args: varargs[untyped]) =
+ let rootId = "happyx-" & $nb.doc.newId()
+ nbRawHtml: "
"
+ nbHappyxCodeBackend(rootId, args)
+
+ template nbHappyxCode*(args: varargs[untyped]) =
+ nb.happyxCode(args)
+
+#[ template nbJsShowSource*(message: string = "") {.deprecated: "Use nbCodeDisplay instead".} =
+ nb.blk.context["js_show_nim_source"] = true
+ if message.len > 0:
+ nb.blk.context["js_show_nim_source_message"] = message
+
+template nbCodeToJsShowSource*(message: string = "") {.deprecated: "Use nbCodeDisplay instead".} =
+ nbJsShowSource(message) ]#
+
+template codeDisplay*(nb: var Nb, tmplCall: untyped, body: untyped) =
+ tmplCall:
+ body
+ nb.codeSkip:
+ body
+
+template nbCodeDisplay*(tmplCall: untyped, body: untyped) =
+ ## display codes used in a template (e.g. nbJsFromCode) after the template call
+ nb.codeDisplay(tmplCall, body)
+
+template codeAnd*(nb: var Nb, tmplCall: untyped, body: untyped) =
+ ## can be used to run code both in c and js backends (e.g. nbCodeAnd(nbJsFromCode))
+ nb.code: # this should work because template name starts with nbCode
+ body
+ tmplCall:
+ body
+
+template nbCodeAnd*(tmplCall: untyped, body: untyped) =
+ nb.codeAnd(tmplCall, body)
+
+template nbClearOutput*() =
+ if not nb.blk.isNil and nb.blk of NbCode:
+ nb.blk.NbCode.output = ""
\ No newline at end of file
diff --git a/src/nimib/docs.nim b/src/nimib/docs.nim
index 4840a17c..6b8d05e5 100644
--- a/src/nimib/docs.nim
+++ b/src/nimib/docs.nim
@@ -1,4 +1,4 @@
-import std/os
+import std/[os, json]
import std/uri
import browsers
import types, logging, renders
@@ -9,16 +9,16 @@ proc relToRoot*(doc: NbDoc, url: string): string =
if isAbsolute(url) or isAbsolute(parseUri(url)):
url
else:
- doc.context["path_to_root"].vString / url
+ doc.context{"path_to_root"}.getStr / url
-proc write*(doc: var NbDoc) =
+proc write*(nb: Nb) =
log "current directory: " & getCurrentDir()
- let dir = doc.filename.splitFile().dir
+ let dir = nb.doc.filename.splitFile().dir
if not dir.dirExists:
log "creating directory: " & dir
createDir(dir)
- log "saving file: " & doc.filename
- writeFile(doc.filename, render(doc))
+ log "saving file: ", nb.doc.filename
+ writeFile(nb.doc.filename, nb.render(nb.doc))
proc open*(doc: NbDoc) =
openDefaultBrowser(doc.filename)
diff --git a/src/nimib/globals.nim b/src/nimib/globals.nim
new file mode 100644
index 00000000..3ad5e911
--- /dev/null
+++ b/src/nimib/globals.nim
@@ -0,0 +1,5 @@
+import ./types
+
+var nbToJson*: Table[string, proc (s: string, i: var int): NbBlock]
+var nbToHtml* = NbRender() # since we need it for json, let's make it also for html
+var nbToMd* = NbRender()
\ No newline at end of file
diff --git a/src/nimib/jsons.nim b/src/nimib/jsons.nim
new file mode 100644
index 00000000..5afcfe00
--- /dev/null
+++ b/src/nimib/jsons.nim
@@ -0,0 +1,37 @@
+import jsony
+import ./types, ./globals
+export jsony
+
+template addNbBlockToJson*(kind: untyped) =
+ nbToJson[$kind] =
+ proc (s: string, i: var int): NbBlock =
+ var v: kind
+ new v
+ parseHook(s, i, v[])
+ result = v
+
+ method dump*(n: kind): string =
+ jsony.toJson(n[])
+
+proc parseHook*(s: string, i: var int, v: var NbBlock) =
+ # First parse the typename
+ var n: NbBlock = NbBlock()
+ let current_i = i
+ parseHook(s, i, n[])
+ # Reset i
+ i = current_i
+ # Parse the correct type
+ let kind = n.kind
+ if kind notIn nbToJson:
+ raise ValueError.newException "cannot find kind in nbToJson: \"" & kind & '"'
+ v = nbToJson[kind](s, i)
+
+method dump*(n: NbBlock): string {.base.} =
+ jsony.toJson(n[])
+
+proc dumpHook*(s: var string, v: NbBlock) =
+ s.add v.dump()
+
+template dumpKey*(s: var string, v: string) =
+ const v2 = jsony.toJson(v) & ":"
+ s.add v2
diff --git a/src/nimib/jsutils.nim b/src/nimib/jsutils.nim
index 5e7d0321..9cd5ec58 100644
--- a/src/nimib/jsutils.nim
+++ b/src/nimib/jsutils.nim
@@ -1,5 +1,21 @@
import std / [macros, macrocache, tables, strutils, strformat, sequtils, sugar, os, hashes]
-import ./types
+import ./types, ./nimibSugars, ./globals, ./jsons, ./blocks, ./renders
+from std/jsonutils import nil
+
+
+newNbBlock(NbJsFromCode): # should inherit from nbCode, but it is defined in nimib.nim...
+ code: string
+ transformedCode: string
+ putAtTop: bool
+ toHtml:
+ ""
+
+newNbBlock(NbJsFromCodeOwnFile):
+ code: string
+ transformedCode: string
+ jsCode: string
+ toHtml:
+ &""
proc contains(tab: CacheTable, keyToCheck: string): bool =
for key, val in tab:
@@ -179,7 +195,8 @@ macro nbHappyxCodeBackend*(rootId: untyped, args: varargs[untyped]) =
let call = newCall(ident"nbJsFromCodeOwnFile", callArgs)
result = call
-proc compileNimToJs*(doc: var NbDoc, blk: var NbBlock) =
+#[ proc compileNimToJs*(doc: var NbDoc, blk: var NbBlock) =
+ let blk =
let tempdir = getTempDir() / "nimib"
createDir(tempdir)
let (dir, filename, ext) = doc.thisFile.splitFile()
@@ -225,4 +242,57 @@ proc nbCollectAllNbJs*(doc: var NbDoc) =
var blk = NbBlock(command: "nbJsFromCodeOwnFile", code: code, context: newContext(searchDirs = @[], partials = doc.partials), output: "")
blk.context["transformedCode"] = code
doc.blocks.add blk
- doc.blk = blk
+ doc.blk = blk ]#
+
+proc compileNimToJs*(nb: var Nb, blk: NbBlock): string =
+ let blk = blk.NbJsFromCodeOwnFile
+ let tempdir = getTempDir() / "nimib"
+ createDir(tempdir)
+ let (dir, filename, ext) = (getCurrentDir().AbsoluteDir, "js_file", ".nim")#doc.thisFile.splitFile()
+ let nimfile = dir / (filename & "_nbCodeToJs_" & $nb.doc.newId() & ext).RelativeFile
+ let jsfile = tempdir / &"out{hash(nb.doc.thisFile)}.js"
+ var codeText = blk.transformedCode
+ let nbJsCounter = nb.doc.nbJsCounter
+ nb.doc.nbJsCounter += 1
+ var bumpGensymString = """
+import std/[macros, json]
+
+macro bumpGensym(n: static int) =
+ for i in 0 .. n:
+ let _ = gensym()
+
+"""
+ bumpGensymString.add &"bumpGensym({nbJsCounter})\n"
+ codeText = bumpGensymString & codeText
+ writeFile(nimfile, codeText)
+ let kxiname = "nimib_kxi_" & $nb.doc.newId()
+ let errorCode = execShellCmd(&"nim js -d:danger -d:kxiname=\"{kxiname}\" -o:{jsfile} {nimfile}")
+ if errorCode != 0:
+ raise newException(OSError, "The compilation of a javascript file failed! Did you remember to capture all needed variables?\n" & $nimfile)
+ removeFile(nimfile)
+ let jscode = readFile(jsfile)
+ removeFile(jsfile)
+ return jscode
+
+proc nbCollectAllNbJs*(nb: var Nb) =
+ var topCode = "" # placed at the top (nbJsFromCodeGlobal)
+ var code = ""
+ for blk in nb.doc.blocksFlattened:
+ if blk of NbJsFromCode:
+ let blk = blk.NbJsFromCode
+ if blk.putAtTop:
+ topCode.add "\n" & blk.transformedCode
+ else:
+ code.add "\n" & blk.transformedCode
+ code = topCode & "\n" & code
+
+ if not code.isEmptyOrWhitespace:
+ # Create block which which will compile the code when rendered (nbJsFromJsOwnFile)
+ let blk = NbJsFromCodeOwnFile(kind: "NbJsFromCodeOwnFile", code: "", transformedCode: code)
+ nb.add blk
+
+ # loop over all nbJsFromCodeOwnFile and compile them
+ for blk in nb.doc.blocksFlattened:
+ if blk of NbJsFromCodeOwnFile:
+ var blk = blk.NbJsFromCodeOwnFile
+ blk.jsCode = nb.compileNimToJs(blk)
diff --git a/src/nimib/nimibSugars.nim b/src/nimib/nimibSugars.nim
new file mode 100644
index 00000000..c8ae49d9
--- /dev/null
+++ b/src/nimib/nimibSugars.nim
@@ -0,0 +1,216 @@
+import std / [macros, strutils, sequtils, genasts]
+
+proc parseCallStmt(n: NimNode): tuple[lhs: string, rhs: NimNode] =
+ n.expectKind nnkCall
+ if n[1].len > 1: # toHtml case
+ (n[0].strVal, n[1])
+ else: # field case
+ (n[0].strVal, n[1][0])
+
+#[ this is what fields with default values look like
+Call
+ Ident "a"
+ StmtList
+ Asgn
+ Ident "string"
+ StrLit ""
+]#
+
+func getTypeFields*(typeSym: NimNode): seq[NimNode] =
+ ## return seq[nnkIdentDefs]
+ typeSym.expectKind nnkSym
+ # add all own fields
+ # look through typeSym.getImpl and look for OfInherit and recurse!
+ let typeDef = typeSym.getImpl
+ #debugecho "typeDef of ", typeSym.repr, ":", typedef.repr
+ let ofInherit = typeDef[2][0][1]
+ if ofInherit.kind == nnkOfInherit:
+ let parentTypeSym = ofInherit[0]
+ if parentTypeSym.strVal != "RootObj":
+ result.add parentTypeSym.getTypeFields
+ let recList = typeDef[2][0][2]
+ result.add recList.children.toSeq
+
+func removePostfix*(identDef: NimNode): NimNode =
+ result = identDef.copyNimTree()
+ if result[0].kind == nnkPostfix:
+ result[0] = result[0][1]
+
+#[ var procParams = @[capitalizedTypeName.ident] & fieldsList
+var objectConstructor = nnkObjConstr.newTree(
+ capitalizedTypeName.ident
+)
+objectConstructor.add newColonExpr(ident"kind", capitalizedTypeName.newLit)
+for (fName, _) in fields:
+ objectConstructor.add newColonExpr(fName.ident, fName.ident)
+var procBody = newStmtList(objectConstructor)
+
+var procName = postfix(ident("new" & capitalizedTypeName), "*")
+let initializer = newProc(procName, procParams, procBody)
+
+echo "init:\n", initializer.repr
+]#
+macro generateBlockInitializer*(typeName: typed): untyped =
+ var fieldsList = typeName.getTypeFields().mapIt(it.removePostfix).filterIt(it[0].strVal != "kind")
+ for identDef in fieldsList:
+ identDef[2] = genAst(fieldType = identDef[1]):
+ default(typedesc[fieldType])
+ #echo fieldsList.mapIt(it.repr)
+
+ let procParams = @[typeName] & fieldsList
+ var objectConstructor = nnkObjConstr.newTree(
+ typeName
+ )
+ objectConstructor.add newColonExpr(ident"kind", typeName.strVal.newLit)
+ for identDef in fieldsList:
+ let fieldName = identDef[0]
+ objectConstructor.add newColonExpr(fieldName, fieldName)
+ let procBody = newStmtList(objectConstructor)
+ let procName = postfix(ident("new" & typeName.strVal), "*")
+ let initializer = newProc(procName, procParams, procBody)
+ #echo "init:\n", initializer.repr
+ return initializer
+
+macro newNbBlock*(typeName: untyped, body: untyped): untyped =
+ # typeName is either `ident` or
+ # Infix
+ # Ident "of"
+ # Ident "nbImage"
+ # Ident "NbBlock"
+
+ let (typeNameStr, parentType) =
+ if typeName.kind == nnkIdent:
+ (typeName.strVal, "NbBlock".ident)
+ else:
+ (typeName[1].strVal, typeName[2])
+
+ let capitalizedTypeName = typeNameStr.capitalizeAscii
+ let lowercasedTypeName = typeNameStr.toLower
+
+ # body is:
+ # StmtList
+ # Call
+ # Ident "url"
+ # StmtList
+ # Ident "string"
+ # Call
+ # Ident "burl"
+ # StmtList
+ # Asgn
+ # Ident "int"
+ # IntLit 1
+ # Call
+ # Ident "toHtml"
+ # StmtList
+ # DotExpr
+ # Ident "blk"
+ # Ident "url"
+
+ #echo "Body:\n", body.treeRepr
+
+ var fields: seq[tuple[fieldName: string, fieldType: NimNode]]
+ var toHtmlBody: NimNode
+ body.expectKind(nnkStmtList)
+ for n in body:
+ let (name, body) = n.parseCallStmt()
+ if eqIdent(name, "toHtml"):
+ toHtmlBody = body
+ else:
+ fields.add (name, body)
+
+ var fieldsList: seq[NimNode]
+ var exportedFieldsList = nnkRecList.newTree()
+ for (fName, fType) in fields:
+ fieldsList.add newIdentDefs(fName.ident, fType)
+ exportedFieldsList.add newIdentDefs(postfix(fName.ident, "*"), fType)
+
+ # TODO: add parent list of fields to fieldsList as well!
+ # This probaly has to be done in a typed macro?
+ # In that case, let the typed macro construct the initializer!
+
+ let typeDefinition = nnkTypeSection.newTree(nnkTypeDef.newTree(
+ postfix(capitalizedTypeName.ident, "*"),
+ newEmptyNode(), # generic
+ nnkRefTy.newTree(
+ nnkObjectTy.newTree(
+ newEmptyNode(), # pragma
+ nnkOfInherit.newTree(
+ parentType
+ ),
+ exportedFieldsList
+ )
+ )
+ ))
+
+ #echo "Type:\n", typeDefinition.repr
+
+ # Next: generate initializer with prefilled kind
+ # Be vary of how we write the kind. SHould it be normalized or exactly like the user wrote it?
+ # It's more predictable if it is like the user wrote it. But more reasonable to normalize it...
+
+ let initializer = genAst(typeName = capitalizedTypeName.ident):
+ generateBlockInitializer(typeName)
+
+ #[ var procParams = @[capitalizedTypeName.ident] & fieldsList
+ var objectConstructor = nnkObjConstr.newTree(
+ capitalizedTypeName.ident
+ )
+ objectConstructor.add newColonExpr(ident"kind", capitalizedTypeName.newLit)
+ for (fName, _) in fields:
+ objectConstructor.add newColonExpr(fName.ident, fName.ident)
+ var procBody = newStmtList(objectConstructor)
+
+ var procName = postfix(ident("new" & capitalizedTypeName), "*")
+ let initializer = newProc(procName, procParams, procBody)
+
+ echo "init:\n", initializer.repr ]#
+
+ # Next: generate nbImageToHtml from toHtmlBody
+ let renderProcName = (lowercasedTypeName & "ToHtml").ident
+ let renderProc = genAst(name = renderProcName, body = toHtmlBody, blk = ident"blk", nb = ident"nb", typeName=capitalizedTypeName.ident):
+ proc name*(blk: NbBlock, nb: Nb): string =
+ let blk = typeName(blk)
+ body
+
+
+ # Next: generate these lines:
+ # nbToHtml.funcs["NbImage"] = nbImageToHtml
+ # addNbBlockToJson(NbImage)
+ # should we make this into a proc instead so it can be used in the non-sugar variant as well?
+ let hookAssignements = genAst(key = capitalizedTypeName.newLit, f = renderProcName, typeName = capitalizedTypeName.ident):
+ nbToHtml.funcs[key] = f
+ addNbBlockToJson(typeName)
+
+ result = newStmtList(
+ typeDefinition,
+ initializer,
+ renderProc,
+ hookAssignements
+ )
+
+ #echo result.repr
+
+ #assert false
+
+macro withNewlines*(body: untyped) : string =
+ body.expectKind nnkStmtList
+ result = "".newLit
+ if body.len > 0:
+ for i, line in body:
+ # TODO: handle for loops
+ # if it's an if-statement, check if it has an else-clause. Otherwise add one which returns ""
+ if line.kind == nnkIfStmt and line.findChild(it.kind == nnkElse).isNil:
+ line.add nnkElse.newTree("".newLit)
+
+ # Only add a newline if the line contains anything and isn't the last line
+ # Also if it already ends with a newline we don't have to add one
+ result = genAst(result=result, line=line, isLast=newLit(i==body.len-1)):
+ let lineVal = line
+ if not isLast and lineVal.len > 0 and lineVal[^1] != '\n':
+ result & lineVal & "\n"
+ else:
+ result & lineVal
+
+
+
+
diff --git a/src/nimib/renders.nim b/src/nimib/renders.nim
index a2141433..0a323685 100644
--- a/src/nimib/renders.nim
+++ b/src/nimib/renders.nim
@@ -1,18 +1,68 @@
-import std / [strutils, tables, sugar, os, strformat, sequtils]
-import ./types, ./jsutils, ./logging, markdown, mustache
+import std / [strutils, tables, sugar, os, strformat, sequtils, json]
+import ./types, markdown, mustache, ./jsutils, ./nimibSugars, ./themes, ./globals, ./jsons, ./logging
import highlight
import mustachepkg/values
+from std/jsonutils import nil
-proc mdOutputToHtml(doc: var NbDoc, blk: var NbBlock) =
+#[ proc mdOutputToHtml(doc: var NbDoc, blk: var NbBlock) =
blk.context["outputToHtml"] = markdown(blk.output, config=initGfmConfig()).dup(removeSuffix)
proc highlightCode(doc: var NbDoc, blk: var NbBlock) =
- blk.context["codeHighlighted"] = highlightNim(blk.code)
+ blk.context["codeHighlighted"] = highlightNim(blk.code) ]#
+
+
+func nbDocToHtml*(blk: NbBlock, nb: Nb): string =
+ let doc = blk.NbDoc
+ let docJson = %[] # it's unused
+ result = withNewlines:
+ ""
+ """"""
+ nb.renderPartial("head", docJson)
+ ""
+ nb.renderPartial("header", docJson)
+ nb.renderPartial("left", docJson)
+ mainToHtml(doc, nb)
+ nb.renderPartial("right", docJson)
+ nb.renderPartial("footer", docJson)
+ ""
+
+addNbBlockToJson(NbDoc) # should this be here?
+nbToHtml.funcs["NbDoc"] = nbDocToHtml
+
+func nbCodeSourcePartial*(blk: JsonNode, nb: Nb): string =
+ let code = blk{"code"}.getStr
+ if code.len > 0:
+ &"{code.highlightNim} "
+ else:
+ ""
+
+nbToHtml.partials["nbCodeSource"] = nbCodeSourcePartial
+
+func nbCodeOutputPartial*(blk: JsonNode, nb: Nb): string =
+ let output = blk{"output"}.getStr
+ if output.len > 0:
+ &"{output} "
+ else:
+ ""
+
+nbToHtml.partials["nbCodeOutput"] = nbCodeOutputPartial
+
+func markdownToHtml*(markdownText: string): string =
+ {.cast(noSideEffect).}: # not sure why markdown is marked with side effects
+ markdown(markdownText, config=initGfmConfig())
+
+func nbTextPartial*(blk: JsonNode, nb: Nb): string =
+ let text = blk{"text"}.getStr
+ markdownToHtml(text)
+
+nbToHtml.partials["nbText"] = nbTextPartial
+
+proc useHtmlBackend*(nb: var Nb) =
+ nb.backend = nbToHtml
-proc useHtmlBackend*(doc: var NbDoc) =
- doc.partials["nbText"] = "{{&outputToHtml}}"
+#[ doc.partials["nbText"] = "{{&outputToHtml}}"
doc.partials["nbCode"] = """
{{>nbCodeSource}}
{{>nbCodeOutput}}"""
@@ -62,9 +112,36 @@ Your browser does not support the audio element.
doc.renderProcs = initTable[string, NbRenderProc]()
doc.renderProcs["mdOutputToHtml"] = mdOutputToHtml
doc.renderProcs["highlightCode"] = highlightCode
- doc.renderProcs["compileNimToJs"] = compileNimToJs
+ doc.renderProcs["compileNimToJs"] = compileNimToJs ]#
+
+#[ func nbDocToMd*(blk: NbBlock, nb: Nb): string =
+ let doc = blk.NbDoc
+ result = nbContainerToHtml(doc, nb)
+
+nbToMd.funcs["NbDoc"] = nbDocToMd
+
+proc nbTextToMd*(blk: NbBlock, nb: Nb): string =
+ let blk = blk.NbText
+
+nbToMd.funcs["NbText"] = nbTextToMd
+
+proc nbCodeToMd*(blk: NbBlock, nb: Nb): string =
+ discard
+
+nbToMd.funcs["NbCode"] = nbCodeToMd ]#
+
+func nbDocToMd*(blk: NbBlock, nb: Nb): string =
+ let doc = blk.NbDoc
+ let docJson = %[] # it's unused
+ result = withNewlines:
+ nbContainerToMd(doc, nb)
+
+nbToMd.funcs["NbDoc"] = nbDocToMd
+
+proc useMdBackend*(nb: var Nb) =
+ nb.backend = nbToMd
-proc useMdBackend*(doc: var NbDoc) =
+#[
doc.partials["document"] = """
{{#blocks}}
@@ -140,3 +217,4 @@ proc render*(nb: var NbDoc): string =
blocks.add nb.render(blk)
nb.context["blocks"] = blocks
return "{{> document}}".render(nb.context)
+ ]#
\ No newline at end of file
diff --git a/src/nimib/sources.nim b/src/nimib/sources.nim
index 55ca7d43..e3742b40 100644
--- a/src/nimib/sources.nim
+++ b/src/nimib/sources.nim
@@ -33,6 +33,7 @@ proc startPos(node: NimNode): Pos =
else:
result = toPos(node.lineInfoObj())
for child in node.children:
+ if child.kind == nnkEmpty: continue
let childPos = child.startPos()
# If we can't get the line info for some reason, skip it!
if childPos.line == 0: continue
@@ -65,7 +66,9 @@ proc finishPos*(node: NimNode): Pos =
result = toPos(node.lineInfoObj())
proc isCommandLine*(s: string, command: string): bool =
- nimIdentNormalize(s.strip()).startsWith(nimIdentNormalize(command))
+ # how do we handle nb.code as well as nbCode?
+ # check for contains instead?
+ nimIdentNormalize(s.strip()).startsWith(nimIdentNormalize(command)) or nimIdentNormalize(command) in nimIdentNormalize(s.strip())
proc isCommentLine*(s: string): bool =
s.strip.startsWith('#')
@@ -86,7 +89,7 @@ proc findStartLine*(source: seq[string], startPos: Pos): int =
return startPos.line - 1
-proc findEndLine*(source: seq[string], command: string, startLine, endPos: int): int =
+proc findEndLine*(source: seq[string], startsOnCommandLine: bool, startLine, endPos: int): int =
result = endPos
# Handle if line is an unclosed triple-quote string
if source[endPos].count("\"\"\"") mod 2 == 1:
@@ -94,10 +97,10 @@ proc findEndLine*(source: seq[string], command: string, startLine, endPos: int):
while result < source.high and source[result].count("\"\"\"") mod 2 == 0:
inc result
# Handle if there are ending comments
- let startsOnCommandLine = source[startLine].isCommandLine(command)
+ #let startsOnCommandLine = source[startLine].isCommandLine(command)
if result > startLine or not startsOnCommandLine:
let baseIndent =
- if source[startLine].isCommandLine(command):
+ if startsOnCommandLine:
skipWhile(source[startLine], {' '}) + 1 # we want to add indent here.
# this is problematic because we don't know the indentation of the code block because
# we don't know the indentation size used. So we just add 1 and check that it is larger or equal.
@@ -107,19 +110,18 @@ proc findEndLine*(source: seq[string], command: string, startLine, endPos: int):
while result < source.high and (source[result+1].startsWith(baseIndentStr) or source[result+1].isEmptyOrWhitespace):
inc result
-proc getCodeBlock*(source, command: string, startPos, endPos: Pos): string =
+proc getCodeBlock*(source: string, startPos, endPos: Pos): string =
## Extracts the code in source from startPos to endPos with additional processing to get the entire code block.
let rawLines = source.splitLines()
let rawStartLine = startPos.line - 1
let rawStartCol = startPos.column - 1
var startLine = findStartLine(rawLines, startPos)
- var endLine = findEndLine(rawLines, command, startLine, endPos.line - 1)
-
- var lines = rawLines[startLine .. endLine]
-
let startsOnCommandLine = block:
- let preline = lines[0][0 ..< rawStartCol]
+ let preline = rawLines[startLine][0 ..< rawStartCol]
startLine == rawStartLine and (not preline.isEmptyOrWhitespace) and (not (preline.nimIdentNormalize.strip() in ["for", "type"]))
+ var endLine = findEndLine(rawLines, startsOnCommandLine, startLine, endPos.line - 1)
+
+ var lines = rawLines[startLine .. endLine]
if startsOnCommandLine:
lines[0] = lines[0][rawStartCol .. ^1].strip()
@@ -143,7 +145,7 @@ proc getCodeBlock*(source, command: string, startPos, endPos: Pos): string =
preserveIndent = not preserveIndent
result = lines.join("\n")
-macro getCodeAsInSource*(source: string, command: static string, body: untyped): string =
+macro getCodeAsInSource*(source: string, body: untyped): string =
## Returns string for the code in body from source.
# substitute for `toStr` in blocks.nim
let startPos = startPos(body)
@@ -155,12 +157,15 @@ macro getCodeAsInSource*(source: string, command: static string, body: untyped):
let startPosLit = startPos.newLit
result = quote do:
- if `filename` notin nb.sourceFiles:
- nb.sourceFiles[`filename`] = readFile(`filename`)
+ if `filename` notin nb.doc.sourceFiles:
+ nb.doc.sourceFiles[`filename`] = readFile(`filename`)
doAssert `endFilename` == `filename`, """
Code from two different files were found in the same nbCode!
If you want to mix code from different files in nbCode, use -d:nimibCodeFromAst instead.
If you are not mixing code from different files, please open an issue on nimib's Github with a minimal reproducible example."""
- getCodeBlock(nb.sourceFiles[`filename`], `command`, `startPosLit`, `endPosLit`)
\ No newline at end of file
+ getCodeBlock(nb.doc.sourceFiles[`filename`], `startPosLit`, `endPosLit`)
+
+template getCode*(body: untyped): string =
+ getCodeAsInSource(nb.doc.source, body).strip
\ No newline at end of file
diff --git a/src/nimib/themes.nim b/src/nimib/themes.nim
index 94db5670..43149128 100644
--- a/src/nimib/themes.nim
+++ b/src/nimib/themes.nim
@@ -1,6 +1,127 @@
-from mustachepkg/values import castStr
-import types, gits, highlight, config
+#from mustachepkg/values import castStr
+import std/[json, macros, sequtils, strutils, strformat]
+import types, gits, highlight, config, jsons, globals, nimibSugars
+# github light svg adapted from: https://iconify.design/icon-sets/octicon/mark-github.html
+# github dark svg taken directly from github website
+const githubLogoLight* = """ """
+const githubLogoDark* = """ """
+
+func getTitle*(doc: NbDoc): string =
+ doc.context{"title"}.getStr("nimib document")
+
+
+# All of this should be moved to renders.nim?
+func headToHtml*(blk: JsonNode, nb: Nb): string =
+ result = withNewlines:
+ fmt"""
+
+ {nb.doc.getTitle}
+
+
+
+ {nb.doc.context.getOrDefault("stylesheet").getStr}
+ {nb.doc.context.getOrDefault("highlight").getStr}
+ {nb.doc.context.getOrDefault("nb_style").getStr}
+ {nb.doc.context.getOrDefault("latex").getStr}
+ """
+ if not nb.doc.context{"disableHighlightJs"}.getBool(false):
+ nb.doc.context{"highlightJs"}.getStr
+ ""
+
+
+func madeWithNimibToHtml*(): string =
+ """made with nimib 🐳 """
+
+func homeLinkToHtml*(homePath: string): string =
+ fmt"""🏡 """
+
+func headerLeftToHtml*(blk: JsonNode, nb: Nb): string =
+ homeLinkToHtml(nb.doc.context{"path_to_root"}.getStr("/"))
+
+func headerCenterToHtml*(blk: JsonNode, nb: Nb): string =
+ let title = nb.doc.getTitle()
+ fmt"{title}"
+
+func headerRightToHtml*(blk: JsonNode, nb: Nb): string =
+ if not isNil(nb.doc.context{"github_remote_url"}):
+ let githubRemoteUrl = nb.doc.context{"github_remote_url"}.getStr
+ let githubLogo = nb.doc.context{"github_logo"}.getStr(githubLogoLight)
+ result = fmt"""{github_logo} """
+
+func headerToHtml*(blk: JsonNode, nb: Nb): string =
+ result = withNewlines:
+ ""
+ """"""
+ " " & nb.renderPartial("header_left", blk) & " "
+ " " & nb.renderPartial("header_center", blk) & " "
+ " " & nb.renderPartial("header_right", blk) & " "
+ "
"
+ " "
+ " "
+
+func leftToHtml*(blk: JsonNode, nb: Nb): string =
+ ""
+
+func mainToHtml*(blk: NbBlock, nb: Nb): string =
+ result = withNewlines:
+ ""
+ nbContainerToHtml(blk, nb)
+ " "
+
+func rightToHtml*(blk: JsonNode, nb: Nb): string =
+ ""
+
+func footerLeftToHtml*(blk: JsonNode, nb: Nb): string =
+ madeWithNimibToHtml()
+
+func footerCenterToHtml*(blk: JsonNode, nb: Nb): string =
+ ""
+
+func showSourceButtonToHtml*(blk: JsonNode, nb: Nb): string =
+ """Show Source """
+
+func footerRightToHtml*(blk: JsonNode, nb: Nb): string =
+ showSourceButtonToHtml(blk, nb)
+
+func sourceSectionToHtml*(blk: JsonNode, nb: Nb): string =
+ fmt"""
+
+{nb.doc.context.getOrDefault("source_highlighted").getStr}
+
+ """
+
+func showSourceScriptToHtml*(blk: JsonNode, nb: Nb): string =
+ """
+
+ """
+
+func footerToHtml*(blk: JsonNode, nb: Nb): string =
+ result = withNewlines:
+ ""
+ """"""
+ " " & nb.renderPartial("footer_left", blk) & " "
+ " " & nb.renderPartial("footer_center", blk) & " "
+ " " & nb.renderPartial("footer_right", blk) & " "
+ "
"
+ " "
+ nb.renderPartial("source_section", blk)
+ nb.renderPartial("show_source_script", blk)
+
+
+#[ # TODO: Make these old partials into procs with inputs so they can be reused!
const document* = """
@@ -38,7 +159,7 @@ const main* = """
{{&.}}
{{/blocks}}
-"""
+""" ]#
# https://css-tricks.com/emojis-as-favicons/ changed font-size to 80 to fit whale
const faviconWhale* = """ """
@@ -86,7 +207,7 @@ figcaption {
"""
-const header* = """
+#[ const header* = """
{{> header_left }}
@@ -97,10 +218,6 @@ const header* = """
"""
const homeLink* = """
🏡 """
const githubLink* = """
{{{github_logo}}} """
-# github light svg adapted from: https://iconify.design/icon-sets/octicon/mark-github.html
-# github dark svg taken directly from github website
-const githubLogoLight* = """
"""
-const githubLogoDark* = """
"""
const footer* = """
@@ -132,54 +249,69 @@ function toggleSourceDisplay() {
"""
proc optOut*(content, keyword: string): string =
- "{{^" & keyword & "}}" & content & "{{/" & keyword & "}}"
+ "{{^" & keyword & "}}" & content & "{{/" & keyword & "}}" ]#
-proc useDefault*(doc: var NbDoc) =
- doc.context["path_to_root"] = (doc.srcDirRel).string
- doc.context["path_to_here"] = (doc.thisFileRel).string
- doc.context["source"] = doc.source
+proc useDefault*(nb: var Nb) =
+ nb.doc.context["path_to_root"] = %(nb.doc.srcDirRel).string
+ nb.doc.context["path_to_here"] = %(nb.doc.thisFileRel).string
+ nb.doc.context["source"] = %nb.doc.source
- doc.partials["document"] = document
- doc.partials["main"] = main
+ #doc.partials["document"] = document
+ #doc.partials["main"] = main
+ nb.backend.partials["left"] = leftToHtml
+ nb.backend.partials["right"] = rightToHtml
# head
- doc.partials["head"] = head
- doc.context["version"] = getNimibVersion()
- doc.context["favicon"] = faviconWhale
- doc.context["stylesheet"] = waterLight
- doc.context["highlight"] = atomOneLight
- doc.context["highlightJs"] = highlightJsTags
- doc.context["nb_style"] = nbStyle
+ nb.backend.partials["head"] = headToHtml
+ nb.doc.context["version"] = %getNimibVersion()
+ nb.doc.context["favicon"] = %faviconWhale
+ nb.doc.context["stylesheet"] = %waterLight
+ nb.doc.context["highlight"] = %atomOneLight
+ nb.doc.context["highlightJs"] = %highlightJsTags
+ nb.doc.context["nb_style"] = %nbStyle
# header
- doc.partials["header"] = header
- doc.partials["header_left"] = homeLink
- doc.context["title"] = doc.context["path_to_here"]
- doc.partials["header_center"] = "" & doc.context["title"].castStr & ""
+ nb.backend.partials["header"] = headerToHtml
+ nb.backend.partials["header_left"] = headerLeftToHtml
+ nb.doc.context["title"] = nb.doc.context["path_to_here"]
+ nb.backend.partials["header_center"] = headerCenterToHtml
if isGitAvailable() and isOnGithub():
- doc.partials["header_right"] = githubLink
- doc.context["github_remote_url"] = getGitRemoteUrl()
- doc.context["github_logo"] = githubLogoLight
+ nb.backend.partials["header_right"] = headerRightToHtml
+ nb.doc.context["github_remote_url"] = %getGitRemoteUrl()
+ nb.doc.context["github_logo"] = %githubLogoLight
# footer
- doc.partials["footer"] = footer
- doc.partials["footer_left"] = madeWithNimib
- doc.partials["footer_right"] = optOut(showSourceButton, "no_source")
- doc.partials["source_section"] = optOut(sourceSection, "no_source")
- doc.partials["show_source_script"] = optOut(showSourceScript, "no_source")
- doc.context["source_highlighted"] = highlightNim(doc.context["source"].castStr)
+ nb.backend.partials["footer"] = footerToHtml
+ nb.backend.partials["footer_left"] = footerLeftToHtml
+ nb.backend.partials["footer_center"] = footerCenterToHtml
+ nb.backend.partials["footer_right"] = footerRightToHtml
+ nb.backend.partials["source_section"] = sourceSectionToHtml
+ nb.backend.partials["show_source_script"] = showSourceScriptToHtml
+ nb.doc.context["source_highlighted"] = %highlightNim(nb.doc.context["source"].getStr)
proc darkMode*(doc: var NbDoc) =
- doc.context["stylesheet"] = waterDark
- doc.context["github_logo"] = githubLogoDark
- doc.context["highlight"] = androidStudio
+ doc.context["stylesheet"] = %waterDark
+ doc.context["github_logo"] = %githubLogoDark
+ doc.context["highlight"] = %androidStudio
+
+proc darkMode*(nb: var Nb) =
+ nb.doc.darkMode()
proc useLatex*(doc: var NbDoc) =
- doc.context["latex"] = latex
+ doc.context["latex"] = %latex
+
+proc useLatex*(nb: var Nb) =
+ nb.doc.useLatex()
proc disableHighlightJs*(doc: var NbDoc) =
- doc.context["disableHighlightJs"] = true
+ doc.context["disableHighlightJs"] = %true
+
+proc disableHighlightJs*(nb: var Nb) =
+ nb.doc.disableHighlightJs()
proc `title=`*(doc: var NbDoc, text: string) =
# to deprecate?
- doc.context["title"] = text
+ doc.context["title"] = %text
+
+proc `title=`*(nb: var Nb, text: string) =
+ `title=`(nb.doc, text)
-proc noTheme*(doc: var NbDoc) =
+proc noTheme*(doc: var Nb) =
discard
diff --git a/src/nimib/types.nim b/src/nimib/types.nim
index a3c31928..2ba4d687 100644
--- a/src/nimib/types.nim
+++ b/src/nimib/types.nim
@@ -1,13 +1,10 @@
import mustache, std / tables, nimib / paths, std / parseopt
export mustache, tables, paths
-import std / [os]
+import std / [os, json, strutils]
type
- NbBlock* = ref object
- command*: string
- code*: string
- output*: string
- context*: Context
+ NbBlock* = ref object of RootObj
+ kind*: string
NbOptions* = object
skipCfg*: bool
cfgName*, srcDir*, homeDir*, filename*: string
@@ -15,8 +12,14 @@ type
other*: seq[tuple[kind: CmdLineKind; name, value: string]]
NbConfig* = object
srcDir*, homeDir*: string
- NbRenderProc* = proc (doc: var NbDoc, blk: var NbBlock) {. nimcall .}
- NbDoc* = object
+ NbRenderFunc* = proc (blk: NbBlock, nb: Nb): string {. noSideEffect .}
+ NbPartialFunc* = proc (blk: JsonNode, nb: Nb): string {. noSideEffect .}
+ NbRender* = ref object of RootObj
+ funcs*: Table[string, NbRenderFunc]
+ partials*: Table[string, NbPartialFunc]
+ NbContainer* = ref object of NbBlock
+ blocks*: seq[NbBlock]
+ NbDoc* = ref object of NbContainer
thisFile*: AbsoluteFile
filename*: string
source*: string
@@ -26,15 +29,20 @@ type
cfg*: NbConfig
cfgDir*: AbsoluteDir
rawCfg*: string
- blk*: NbBlock ## current block being processed
- blocks*: seq[NbBlock]
- context*: Context
- partials*: Table[string, string]
- templateDirs*: seq[string]
- renderPlans*: Table[string, seq[string]]
- renderProcs*: Table[string, NbRenderProc]
- id: int
- nbJsCounter*: int
+ context*: JsonNode
+ id*: int
+ nbJsCounter*: int
+ Nb* = object
+ # TODO: which fields should be moved from NbDoc to Nb?
+ # As little as possible?
+ # NbDoc should contain all info relevant to rendering the page and
+ # Nb should just contain stuff needed for producing the NbDoc (like id and nbJsCounter)
+ blk*: NbBlock # last block processed
+ doc*: NbDoc # could be a NbBlock but we could give more guarantees with a NbDoc
+ containers*: seq[NbContainer] # current container
+ backend*: NbRender # current backend
+
+#proc `$`*(blk: NbBlock): string = $blk[]
proc thisDir*(doc: NbDoc): AbsoluteDir = doc.thisFile.splitFile.dir
proc srcDir*(doc: NbDoc): AbsoluteDir =
@@ -52,4 +60,30 @@ proc srcDirRel*(doc: NbDoc): RelativeDir = doc.srcDir.relativeTo doc.thisDir
proc newId*(doc: var NbDoc): int =
## Provides a unique integer each time it is called
result = doc.id
- inc doc.id
\ No newline at end of file
+ inc doc.id
+
+proc blocks*(nb: var Nb): var seq[NbBlock] =
+ nb.doc.blocks
+
+func render*(nb: Nb, blk: NbBlock): string =
+ if blk.kind in nb.backend.funcs:
+ nb.backend.funcs[blk.kind](blk, nb)
+ else:
+ ""
+
+func renderPartial*(render: NbRender, name: string, blk: JsonNode, nb: Nb): string =
+ if name in render.partials:
+ render.partials[name](blk, nb)
+ else:
+ ""
+
+func renderPartial*(nb: Nb, name: string, blk: JsonNode): string =
+ nb.backend.renderPartial(name, blk, nb)
+
+func nbContainerToHtml*(blk: NbBlock, nb: Nb): string =
+ let blk = blk.NbContainer
+ for b in blk.blocks:
+ result.add nb.render(b).strip & '\n'
+ result.strip
+
+const nbContainerToMd* = nbContainerToHtml
diff --git a/tests/tblocks.nim b/tests/tblocks.nim
index 8ec6b699..a812af35 100644
--- a/tests/tblocks.nim
+++ b/tests/tblocks.nim
@@ -1,28 +1,22 @@
import nimib
import std / [unittest, strutils]
+newNbBlock(ReadCodeBlock):
+ code: string
+ toHtml:
+ blk.code
+
suite "newNbBlock":
nbInit
- var blk: NbBlock
test "readCode":
template readCodeBlock(body: untyped) =
- newNbBlock("readCodeBlock", true, nb, blk):
- body
- do:
- discard
+ let blk = newReadCodeBlock()
+ blk.code = getCode(body)
+ nb.add blk
- template dontReadCodeBlock(body: untyped) =
- newNbBlock("dontReadCodeBlock", false, nb, blk):
- body
- do:
- discard
-
readCodeBlock:
let a = 1.23
- check blk.code == "let a = 1.23"
- dontReadCodeBlock:
- let b = 3.21
- check blk.code == ""
+ check nb.blk.ReadCodeBlock.code == "let a = 1.23"
diff --git a/tests/tnimib.nim b/tests/tnimib.nim
index ea791c34..6b68962f 100644
--- a/tests/tnimib.nim
+++ b/tests/tnimib.nim
@@ -7,18 +7,18 @@ nbInit # todo: add a test suite for nbInit
suite "nbText":
test "single line text string":
nbText: "hi"
- check nb.blk.output == "hi"
+ check nb.blk.NbText.text == "hi"
test "single line text string with strformat":
let name = "you"
nbText: fmt"hi {name}"
- check nb.blk.output == "hi you"
+ check nb.blk.NbText.text == "hi you"
test "multi line text string":
nbText: """hi
how are you?
"""
- check nb.blk.output == """hi
+ check nb.blk.NbText.text == """hi
how are you?
"""
@@ -29,29 +29,29 @@ how are you?
nbText: &"""hi {name}
how are you? {answer}
"""
- check nb.blk.output == """hi you
+ check nb.blk.NbText.text == """hi you
how are you? fine
"""
suite "nbTextWithCode":
test "single line text string":
nbTextWithCode: "hi"
- check nb.blk.code == "\"hi\""
- check nb.blk.output == "hi"
+ check nb.blk.NbTextWithCode.code == "\"hi\""
+ check nb.blk.NbTextWithCode.text == "hi"
test "single line text string with strformat":
let name = "you"
nbTextWithCode: fmt"hi {name}"
- check nb.blk.code == "fmt\"hi {name}\""
- check nb.blk.output == "hi you"
+ check nb.blk.NbTextWithCode.code == "fmt\"hi {name}\""
+ check nb.blk.NbTextWithCode.text == "hi you"
test "multi line text string - variant 1":
nbTextWithCode:
"""hi
how are you?
"""
- check nb.blk.code == "\"\"\"hi\nhow are you?\n\"\"\""
- check nb.blk.output == """hi
+ check nb.blk.NbTextWithCode.code == "\"\"\"hi\nhow are you?\n\"\"\""
+ check nb.blk.NbTextWithCode.text == """hi
how are you?
"""
@@ -62,10 +62,10 @@ hi
how are you?
"""
when defined(nimibCodeFromAst):
- check nb.blk.code == "\"\"\"hi\nhow are you?\n\"\"\""
+ check nb.blk.NbTextWithCode.code == "\"\"\"\nhi\nhow are you?\n\"\"\""
else:
- check nb.blk.code == "\"\"\"\nhi\nhow are you?\n\"\"\""
- check nb.blk.output == """hi
+ check nb.blk.NbTextWithCode.code == "\"\"\"\nhi\nhow are you?\n\"\"\""
+ check nb.blk.NbTextWithCode.text == """hi
how are you?
"""
@@ -73,8 +73,8 @@ how are you?
nbTextWithCode: """hi
how are you?
"""
- check nb.blk.code == "\"\"\"hi\nhow are you?\n\"\"\""
- check nb.blk.output == """hi
+ check nb.blk.NbTextWithCode.code == "\"\"\"hi\nhow are you?\n\"\"\""
+ check nb.blk.NbTextWithCode.text == """hi
how are you?
"""
@@ -85,32 +85,30 @@ how are you?
nbTextWithCode: &"""hi {name}
how are you? {answer}
"""
- check nb.blk.code == "&\"\"\"hi {name}\nhow are you? {answer}\n\"\"\""
- check nb.blk.output == """hi you
+ check nb.blk.NbTextWithCode.code == "&\"\"\"hi {name}\nhow are you? {answer}\n\"\"\""
+ check nb.blk.NbTextWithCode.text == """hi you
how are you? fine
"""
suite "nbCode":
test "single line of code, no output":
nbCode: discard
- check nb.blk.code == "discard"
- check nb.blk.output == ""
+ check nb.blk.NbCode.code == "discard"
+ check nb.blk.NbCode.output == ""
test "single line of code, with output":
nbCode: echo "hi"
- check nb.blk.code == "echo \"hi\""
- check nb.blk.output == "hi\n"
+ check nb.blk.NbCode.code == "echo \"hi\""
+ check nb.blk.NbCode.output == "hi\n"
suite "nbRawHtml":
test "pure text":
nbRawHtml: "Hello world!"
- check nb.blk.code == ""
- check nb.blk.output == "Hello world!"
+ check nb.blk.NbRawHtml.html == "Hello world!"
test "html tags":
nbRawHtml: " div-span
"
- check nb.blk.code == ""
- check nb.blk.output == " div-span
"
+ check nb.blk.NbRawHtml.html == " div-span
"
test "Use inside template":
# codeAsInSource can't read the correct line if block is used inside a template
@@ -121,29 +119,25 @@ suite "nbRawHtml":
nbRawHtml: ""
slide:
- check nb.blk.code == ""
- check nb.blk.output == ""
+ check nb.blk.NbRawHtml.html == ""
- check nb.blk.code == ""
- check nb.blk.output == " "
+ check nb.blk.NbRawHtml.html == " "
suite "nbClearOutput":
test "nbCode":
nbCode:
echo "Hello world!!!"
- check nb.blk.output == "Hello world!!!\n"
- check nb.blk.context["output"].vString == "Hello world!!!"
+ check nb.blk.NbCode.output == "Hello world!!!\n"
nbClearOutput()
- check nb.blk.output == ""
- check nb.blk.context["output"].vString == ""
+ check nb.blk.NbCode.output == ""
suite "nbCodeSkip":
test "single line of code with output":
nbCodeSkip:
echo "random output..."
- check nb.blk.output == ""
- check nb.blk.code == "echo \"random output...\""
+ check nb.blk.NbCode.output == ""
+ check nb.blk.NbCode.code == "echo \"random output...\""
test "destructive code":
# Make sure the code is NOT executed
@@ -151,21 +145,103 @@ suite "nbCodeSkip":
nbCodeSkip:
fail()
- check nb.blk.output == ""
- check nb.blk.code == "fail()"
+ check nb.blk.NbCode.output == ""
+ check nb.blk.NbCode.code == "fail()"
suite "nbCapture":
test "single line of code, with output":
nbCapture:
echo "captured output"
- check nb.blk.output == "captured output\n"
+ check nb.blk.NbCode.output == "captured output\n"
test "single line of code, without output":
nbCapture:
discard
- check nb.blk.output == ""
+ check nb.blk.NbCode.output == ""
+
+
+suite "nbJs":
+ test "nbJsFromString":
+ nbJsFromString: hlNim"""
+let a = 1
+echo a
+"""
+ check nb.blk.NbJsFromCode.code == """
+let a = 1
+echo a
+"""
+ check nb.blk.NbJsFromCode.transformedCode.len > 0
+
+ test "nbJsFromCode":
+ nbJsFromCode:
+ let a = 1
+ echo a
+ check nb.blk.NbJsFromCode.transformedCode.len > 0
+ check "a = 1" in nb.blk.NbJsFromCode.transformedCode
+ check "block:" notin nb.blk.NbJsFromCode.transformedCode
+
+ test "nbJsFromCode, capture variable":
+ let a = 1
+ nbJsFromCode(a):
+ echo a
+ check nb.blk.NbJsFromCode.transformedCode.len > 0
+ check "a = parseJson" in nb.blk.NbJsFromCode.transformedCode
+
+ test "nbJsFromCodeGlobal":
+ nbJsFromCodeGlobal:
+ import std / dom
+ var x = 1
+ check nb.blk.NbJsFromCode.transformedCode.len > 0
+ check "x = 1" in nb.blk.NbJsFromCode.transformedCode
+ check "block:" notin nb.blk.NbJsFromCode.transformedCode
+
+ test "nbJsFromCodeInBlock":
+ nbJsFromCodeInBlock:
+ let x = 3.14
+ echo x
+ check nb.blk.NbJsFromCode.transformedCode.len > 0
+ check "x = 3.14" in nb.blk.NbJsFromCode.transformedCode
+ check "block:" in nb.blk.NbJsFromCode.transformedCode
+
+ test "nbJsFromCodeOwnFile + exportc":
+ nbJsFromCodeOwnFile:
+ proc setup() {.exportc.} =
+ echo 1
+ check "setup()" in nb.blk.NbJsFromCodeOwnFile.transformedCode
+
+ test "nbCodeDisplay":
+ nbCodeDisplay(nbJsFromCode):
+ import p5
+ echo "hi p5"
+ draw:
+ ellipse(mouseX, mouseY, 20)
+ check nb.blocks[^1].kind == "NbCode"
+ check nb.blocks[^2].kind == "NbJsFromCode"
+ check nb.blocks[^2].NbJsFromCode.transformedCode.len > 0
+ check "ellipse(mouseX, mouseY, 20)" in nb.blocks[^2].NbJsFromCode.transformedCode
+ when defined(nimibCodeFromAst):
+ check nb.blocks[^1].NbCode.code.startsWith("import p5")
+ else:
+ check nb.blocks[^1].NbCode.code.startsWith("import p5")
+ check nb.blocks[^1].NbCode.output == ""
+
+ test "nbCodeAnd":
+ nbCodeAnd(nbJsFromCode):
+ let you = "me"
+ echo "hi ", you
+ check nb.blocks[^2].kind == "NbCode"
+ check nb.blocks[^1].kind == "NbJsFromCode"
+ check nb.blocks[^1].NbJsFromCode.transformedCode.len > 0
+ check "you = \"me\"" in nb.blocks[^1].NbJsFromCode.transformedCode
+ check nb.blocks[^2].NbCode.code.startsWith("let you =")
+ check nb.blocks[^2].NbCode.output == "hi me\n"
+
+test "getNimibVersion()":
+ let version = getNimibVersion()
+
+ check version.count('.') == 2
when moduleAvailable(nimpy) and false:
nbInitPython()
@@ -178,59 +254,11 @@ print(s)
print(a)
"""
nbPython: pyString
- check nb.blk.code == pyString
- check nb.blk.output == "[0, 2, 4]\n3.14\n"
+ check nb.blk.NbPython.code == pyString
+ check nb.blk.NbPython.output == "[0, 2, 4]\n3.14\n"
when moduleAvailable(karax/kbase):
- suite "nbJs":
- test "nbJsFromString":
- nbJsFromString: hlNim"""
- let a = 1
- echo a
- """
- check nb.blk.code == """
- let a = 1
- echo a
- """
- check nb.blk.context["transformedCode"].vString.len > 0
-
- test "nbJsFromCode":
- nbJsFromCode:
- let a = 1
- echo a
- check nb.blk.context["transformedCode"].vString.len > 0
- check "a = 1" in nb.blk.context["transformedCode"].vString
- check "block:" notin nb.blk.context["transformedCode"].vString
-
- test "nbJsFromCode, capture variable":
- let a = 1
- nbJsFromCode(a):
- echo a
- check nb.blk.context["transformedCode"].vString.len > 0
- check "a = parseJson" in nb.blk.context["transformedCode"].vString
-
- test "nbJsFromCodeGlobal":
- nbJsFromCodeGlobal:
- import std / dom
- var x = 1
- check nb.blk.context["transformedCode"].vString.len > 0
- check "x = 1" in nb.blk.context["transformedCode"].vString
- check "block:" notin nb.blk.context["transformedCode"].vString
-
- test "nbJsFromCodeInBlock":
- nbJsFromCodeInBlock:
- let x = 3.14
- echo x
- check nb.blk.context["transformedCode"].vString.len > 0
- check "x = 3.14" in nb.blk.context["transformedCode"].vString
- check "block:" in nb.blk.context["transformedCode"].vString
-
- test "nbJsFromCodeOwnFile + exportc":
- nbJsFromCodeOwnFile:
- proc setup() {.exportc.} =
- echo 1
- check "setup()" in nb.blk.context["transformedCode"].vString
-
+ suite "nbKarax":
test "nbKaraxCode":
let x = 3.14
nbKaraxCode(x):
@@ -238,37 +266,5 @@ when moduleAvailable(karax/kbase):
karaxHtml:
p:
text message
- check nb.blk.code.len > 0
- check nb.blk.context["transformedCode"].vString.len > 0
-
- test "nbCodeDisplay":
- nbCodeDisplay(nbJsFromCode):
- import p5
- echo "hi p5"
- draw:
- ellipse(mouseX, mouseY, 20)
- check nb.blocks[^1].command == "nbCode"
- check nb.blocks[^2].command == "nbJsFromCode"
- check nb.blocks[^2].context["transformedCode"].vString.len > 0
- check "ellipse(mouseX, mouseY, 20)" in nb.blocks[^2].context["transformedCode"].vString
- when defined(nimibCodeFromAst):
- check nb.blocks[^1].code.startsWith("import\n p5")
- else:
- check nb.blocks[^1].code.startsWith("import p5")
- check nb.blocks[^1].output == ""
-
- test "nbCodeAnd":
- nbCodeAnd(nbJsFromCode):
- let you = "me"
- echo "hi ", you
- check nb.blocks[^2].command == "nbCode"
- check nb.blocks[^1].command == "nbJsFromCode"
- check nb.blocks[^1].context["transformedCode"].vString.len > 0
- check "you = \"me\"" in nb.blocks[^1].context["transformedCode"].vString
- check nb.blocks[^2].code.startsWith("let you =")
- check nb.blocks[^2].output == "hi me\n"
-
-test "getNimibVersion()":
- let version = getNimibVersion()
-
- check version.count('.') == 2
+ check nb.blk.NbJsFromCodeOwnFile.code.len > 0
+ check nb.blk.NbJsFromCodeOwnFile.transformedCode.len > 0
\ No newline at end of file
diff --git a/tests/trenders.nim b/tests/trenders.nim
index 1341b206..7f03afab 100644
--- a/tests/trenders.nim
+++ b/tests/trenders.nim
@@ -1,5 +1,4 @@
import nimib
-import nimib / renders
import unittest, strutils
nbInit
@@ -16,7 +15,9 @@ suite "render (block), html default backend":
test "nbCode with output":
nbCode: echo "hi"
check nb.render(nb.blk).strip == """
-echo "hi" hi """
+echo "hi"
+hi
+ """
# switch to markdown backend
useMdBackend nb
@@ -28,47 +29,39 @@ suite "render (block), markdown backend":
test "nbCode without output":
nbCode: discard
- check nb.render(nb.blk) == """
-
+ check nb.render(nb.blk).strip() == """
```nim
discard
```
-
-"""
+""".strip()
test "nbCode with output":
nbCode: echo "hi"
- check nb.render(nb.blk) == """
+ check nb.render(nb.blk).strip() == """
```nim
echo "hi"
```
-
-
```
hi
```
-
-"""
+""".strip()
test "nbImage with caption":
nbImage("https://nim-lang.org/assets/img/logo_bw.png", "nim-lang.org favicon")
- check nb.render(nb.blk) == """
+ check nb.render(nb.blk).strip == """

-
**Figure:** nim-lang.org favicon
-"""
+""".strip
test "nbImage without caption":
nbImage("https://nim-lang.org/assets/img/logo_bw.png")
check nb.render(nb.blk) == """

-
"""
test "nbImage with alt text":
nbImage("https://nim-lang.org/assets/img/logo_bw.png", alt="nim-lang.org favicon")
- check nb.render(nb.blk) == """
+ check nb.render(nb.blk).strip == """

-
-"""
+""".strip
diff --git a/tests/tsources.nim b/tests/tsources.nim
index d2af6ce2..4689a7ca 100644
--- a/tests/tsources.nim
+++ b/tests/tsources.nim
@@ -1,12 +1,12 @@
-import nimib, strformat
+import nimib, strformat, json
import unittest
suite "test sources":
- template check =
+ template checkCode(t: typedesc = NbCode) =
#echo &"---\n{nbBlock.code}\n"
# the replace stuff needed on windows where the lines read from file will have windows native new lines
test $currentTest:
- actual = nbBlock.code
+ actual = nb.blk.t.code
check actual.nbNormalize == expected.nbNormalize
if actual.nbNormalize != expected.nbNormalize:
echo &"===\n---actual:\n{actual.repr}\n---expected\n{expected.repr}\n---\n==="
@@ -25,7 +25,7 @@ suite "test sources":
let
x = 1
"""
- check
+ checkCode()
nbCode:
# a comment
@@ -36,25 +36,25 @@ let
let # and a comment with nbCode
y = 1
"""
- check
+ checkCode()
nbCode echo y
expected = "echo y"
- check
+ checkCode()
nbCode: echo y
expected = "echo y"
- check
+ checkCode()
nbCode(echo y)
expected = "echo y"
- check
+ checkCode()
nbCode ((echo ("( This is ( weird string)")))
expected = "echo (\"( This is ( weird string)\")"
- check
+ checkCode()
nbTextWithCode: """problem
solution"""
expected = "\"\"\"problem\nsolution\"\"\""
- check
+ checkCode(NbTextWithCode)
template discardBlock(body: untyped) = discard
@@ -65,7 +65,7 @@ solution"""
discardBlock:
echo y
"""
- check
+ checkCode()
nbCode:
let garbage = 1
@@ -73,40 +73,40 @@ discardBlock:
middle
end"""
expected = "let garbage = 1\nlet bigString = \"\"\"start\n middle\nend\"\"\""
- check
+ checkCode()
nbCode:
block:
echo y
expected = "block:\n echo y"
- check
+ checkCode()
when not defined(nimibCodeFromAst):
nbCode:
echo y
# This should be included!
expected = "echo y\n# This should be included!"
- check
+ checkCode()
nbCode:
echo y
# Include this as well!
expected = "echo y\n\n# Include this as well!"
- check
+ checkCode()
nbCode:
echo y
# Don't include this!
expected = "echo y"
- check
+ checkCode()
nbCode:
echo y
# The newline at the beginning of the block!
expected = "echo y"
- check
+ checkCode()
nbCode:
block:
@@ -114,7 +114,7 @@ end"""
b = 1
expected = "block:\n let\n b = 1"
- check
+ checkCode()
template notNbCode(body: untyped) =
nbCode:
@@ -124,7 +124,7 @@ end"""
echo y
expected = "echo y"
- check
+ checkCode()
template `&`(a,b: int) = discard
@@ -133,34 +133,34 @@ end"""
2
expected = "1 &\n 2"
- check
+ checkCode()
nbCode:
- nb.context["no_source"] = true
+ nb.doc.context["no_source"] = %true
- expected = "nb.context[\"no_source\"] = true"
- check
+ expected = "nb.doc.context[\"no_source\"] = %true"
+ checkCode()
nbCode: discard
expected = "discard"
- check
+ checkCode()
nbCode:
for n in 0 .. 1:
discard
expected = "for n in 0 .. 1:\n discard"
- check
+ checkCode()
template nbCodeInTemplate =
nbCode:
- nb.renderPlans["nbText"] = @["mdOutputToHtml"]
+ nb.doc.context["nbText"] = %["mdOutputToHtml"]
nbCodeInTemplate()
- expected = """nb.renderPlans["nbText"] = @["mdOutputToHtml"]"""
- check
+ expected = """nb.doc.context["nbText"] = %["mdOutputToHtml"]"""
+ checkCode()
nbCode:
type A = object
expected = "type A = object"
- check
+ checkCode()
\ No newline at end of file