-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathnimib.nim
287 lines (226 loc) Β· 9.31 KB
/
nimib.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
import std/[os, strutils, sugar, strformat, macros, macrocache, sequtils, jsonutils]
export jsonutils
import nimib / [types, blocks, docs, boost, config, options, capture, jsutils]
export types, blocks, docs, boost, sugar, jsutils
# 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
# 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
else:
nb.thisFile = nb.srcDir / thisFileRel.RelativeFile
echo "[nimib] thisFile: ", nb.thisFile
try:
nb.source = read(nb.thisFile)
except IOError:
echo "[nimib] cannot read source"
if nb.options.filename == "":
nb.filename = nb.thisFile.string.splitFile.name & ".html"
else:
nb.filename = nb.options.filename
if nb.cfg.srcDir != "":
echo "[nimib] srcDir: ", nb.srcDir
nb.filename = (nb.thisDir.relativeTo nb.srcDir).string / nb.filename
echo "[nimib] filename: ", nb.filename
if nb.cfg.homeDir != "":
echo "[nimib] setting current directory to nb.homeDir: ", nb.homeDir
setCurrentDir nb.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
# apply render backend (default backend can be overriden by theme)
backend nb
# apply theme
theme nb
template nbInitMd*(thisFileRel = "") =
var tfr = if thisFileRel == "":
instantiationInfo(-1).filename
else:
thisFileRel
nbInit(backend=renders.useMdBackend, theme=themes.noTheme, tfr)
if nb.options.filename == "":
nb.filename = nb.filename.splitFile.name & ".md"
template enableLineNumbersDoc* =
nb.context["enableLineNumbers"] = true
template enableLineNumbersBlock* =
nb.blk.context["enableLineNumbers"] = true
# 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 nbCodeWithNumbers*(body: untyped) =
newNbCodeBlock("nbCode", body):
captureStdout(nb.blk.output):
body
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"] =
if isAbsolute(url) or url[0..3] == "http":
url
else:
nb.context["path_to_root"].vString / url
nb.blk.context["alt_text"] =
if alt == "":
caption
else:
alt
nb.blk.context["caption"] = caption
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
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: "<div id=\"" & rootId & "\"></div>"
nbKaraxCodeBackend(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
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:
# - in memory partial contains default mustache assets
# - to override/customize (for a bunch of documents) the best way is to modify a version on file
# - 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)
write nb
if nb.options.show:
open nb
# how to change this to a better version using nb?
template relPath*(path: AbsoluteFile | AbsoluteDir): string =
(path.relativeTo nb.homeDir).string
# aliases to minimize breaking changes after refactoring nbDoc -> nb. Should be deprecated at some point?
template nbDoc*: NbDoc = nb
template nbBlock*: NbBlock = nb.blk
template nbHomeDir*: AbsoluteDir = nb.homeDir
# use --nbShow runtime option instead of this
template nbShow* =
nbSave
open nb
# the following does not affect user imports but only imports not exported in this module
{. warning[UnusedImport]:off .}