From cc7e0e8a7a8b1faff94310d4625e116451b10969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Bla=C5=BEek?= Date: Tue, 6 Feb 2024 00:16:34 +0100 Subject: [PATCH] Formatted with nph 0.4 --- src/xidoc.nim | 118 ++++----- src/xidocpkg/commands/checkbox.nim | 1 - src/xidocpkg/commands/css.nim | 1 - src/xidocpkg/commands/default.nim | 350 ++++++++++++++++++--------- src/xidocpkg/commands/draw.nim | 47 ++-- src/xidocpkg/commands/javascript.nim | 31 ++- src/xidocpkg/commands/math.nim | 106 ++++++-- src/xidocpkg/commands/utils.nim | 59 +++-- src/xidocpkg/error.nim | 12 +- src/xidocpkg/expand.nim | 136 ++++++----- src/xidocpkg/janetinterpret.nim | 42 +++- src/xidocpkg/jsinterpret.nim | 257 ++++++++++++++++++-- src/xidocpkg/parser.nim | 21 +- src/xidocpkg/pikchr.nim | 13 +- src/xidocpkg/string_view.nim | 11 +- src/xidocpkg/translations.nim | 79 ++---- src/xidocpkg/types.nim | 39 ++- tests/test_documents.nim | 205 ++++++++++------ tests/test_parser.nim | 44 ++-- 19 files changed, 1029 insertions(+), 543 deletions(-) diff --git a/src/xidoc.nim b/src/xidoc.nim index 28ef5a7..5aca612 100644 --- a/src/xidoc.nim +++ b/src/xidoc.nim @@ -16,28 +16,28 @@ import xidocpkg/types export FormattedXidocError -type - TargetEx = enum - teHtml = "html" - teLatex = "latex" - teGemtext = "gemtext" - teSvg = "svg" +type TargetEx = enum + teHtml = "html" + teLatex = "latex" + teGemtext = "gemtext" + teSvg = "svg" -const extensions = [ - teHtml: "html", - teLatex: "tex", - teGemtext: "gmi", - teSvg: "svg", -] +const extensions = [teHtml: "html", teLatex: "tex", teGemtext: "gmi", teSvg: "svg"] const targetMapping = [ - teHtml: tHtml, - teLatex: tLatex, - teGemtext: tGemtext, - teSvg: tHtml, + teHtml: tHtml, teLatex: tLatex, teGemtext: tGemtext, teSvg: tHtml ] -proc renderXidoc*(body: string, path = "", target = teHtml, snippet = false, safeMode = false, verbose = false, colorfulError = false, flags = newSeq[string]()): string = +proc renderXidoc*( + body: string, + path = "", + target = teHtml, + snippet = false, + safeMode = false, + verbose = false, + colorfulError = false, + flags = newSeq[string](), +): string = let bodyRef = new string bodyRef[] = body let doc = Document( @@ -47,14 +47,12 @@ proc renderXidoc*(body: string, path = "", target = teHtml, snippet = false, saf safeMode: safeMode, verbose: verbose, flags: flags, - stack: @[Frame( - cmdName: "[top]".toStringView, - path: some(path), - )], + stack: @[Frame(cmdName: "[top]".toStringView, path: some(path))], ) doc.stack[0].commands = defaultCommands(doc) let rendered = - try: doc.renderBody + try: + doc.renderBody except XidocError: raise getCurrentException().XidocError.format(doc, termColors = colorfulError) if snippet: @@ -73,22 +71,19 @@ proc renderXidoc*(body: string, path = "", target = teHtml, snippet = false, saf of teLatex: let lang = translate(pLatexLanguageName, doc.lookup(lang)) let documentClass = - if doc.settings.documentClass == "": "article" - else: doc.settings.documentClass - "documentclass"{documentClass} & - "usepackage"["utf8"]{"inputenc"} & - "usepackage"[lang]{"babel"} & - "usepackage"{"geometry"} & - head & - "begin"{"document"} & - rendered & - "end"{"document"} + if doc.settings.documentClass == "": "article" else: doc.settings.documentClass + "documentclass"{documentClass} & "usepackage"["utf8"]{"inputenc"} & + "usepackage"[lang]{"babel"} & "usepackage"{"geometry"} & head & + "begin"{"document"} & rendered & "end"{"document"} of teGemtext: &"{head}{rendered}" of teSvg: let svgs = htg.`div`(rendered).parseHtml.findAll("svg") if svgs.len != 1: - raise XidocError(msg: &"When compiling to SVG, exactly one element must be produced, found {svgs.len}").format(doc, termColors = colorfulError) + raise XidocError( + msg: + &"When compiling to SVG, exactly one element must be produced, found {svgs.len}" + ).format(doc, termColors = colorfulError) $svgs[0] when isMainModule and not defined(js): @@ -96,15 +91,16 @@ when isMainModule and not defined(js): import std/os import std/terminal - proc xidoc(target = teHtml, - snippet = false, - safe = false, - dryRun = false, - noColor = false, - forceStdin = false, - flag = newSeq[string](), - paths: seq[string]) = - + proc xidoc( + target = teHtml, + snippet = false, + safe = false, + dryRun = false, + noColor = false, + forceStdin = false, + flag = newSeq[string](), + paths: seq[string], + ) = proc renderFile(path, input: string): string = result = renderXidoc( input, @@ -112,9 +108,7 @@ when isMainModule and not defined(js): target = target, snippet = snippet, safeMode = safe, - colorfulError = (not noColor) and - stderr.isATty and - getEnv("NO_COLOR") == "", + colorfulError = (not noColor) and stderr.isATty and getEnv("NO_COLOR") == "", flags = flag, ) if path != "": @@ -124,8 +118,10 @@ when isMainModule and not defined(js): if forceStdin or paths.len == 0: try: let path = - if paths.len == 0: "" - else: paths[0] + if paths.len == 0: + "" + else: + paths[0] let rendered = renderFile(path, stdin.readAll) if not dryRun: stdout.writeLine(rendered) @@ -149,12 +145,16 @@ when isMainModule and not defined(js): dispatch xidoc, help = { "target": "what language to transpile to; one of \"html\", \"latex\", \"gemtext\"", - "snippet": "generate just a code snippet instead of a whole document; useful for embedding", - "safe": "only allow commands that are known to not be vulnerable to injection attacks", + "snippet": + "generate just a code snippet instead of a whole document; useful for embedding", + "safe": + "only allow commands that are known to not be vulnerable to injection attacks", "dry-run": "do not write anything, just check for errors", - "no-color": "disable colorful error messages (also disabled when STDERR is not a TTY or when the NO_COLOR environment variable is present)", - "force-stdin": "read from STDIN even if a filename is specified; in that case, it will be used as the path for the document", - "flag": "pass a flag to the document", + "no-color": + "disable colorful error messages (also disabled when STDERR is not a TTY or when the NO_COLOR environment variable is present)", + "force-stdin": + "read from STDIN even if a filename is specified; in that case, it will be used as the path for the document", + "flag": "pass a flag to the document" }, short = { "target": 't', @@ -163,18 +163,20 @@ when isMainModule and not defined(js): "dry-run": 'd', "no-color": 'C', "force-stdin": '0', - "flag": 'f', + "flag": 'f' } - elif defined(js): # JavaScript library - import std/jsffi - proc renderXidocJs(body: cstring, config: JsObject): cstring {.exportc: "renderXidoc".} = + proc renderXidocJs( + body: cstring, config: JsObject + ): cstring {.exportc: "renderXidoc".} = try: let config = - if config == jsUndefined: newJsObject() - else: config + if config == jsUndefined: + newJsObject() + else: + config let rendered = renderXidoc( $body, snippet = config.snippet.to(bool), diff --git a/src/xidocpkg/commands/checkbox.nim b/src/xidocpkg/commands/checkbox.nim index 77d8a82..b3f26e5 100644 --- a/src/xidocpkg/commands/checkbox.nim +++ b/src/xidocpkg/commands/checkbox.nim @@ -5,7 +5,6 @@ import ./utils import std/tables commands checkboxCommands: - proc dashCmd(arg: !Markup): Markup {.command: "-".} = case doc.target of tHtml: diff --git a/src/xidocpkg/commands/css.nim b/src/xidocpkg/commands/css.nim index 73fe4b7..00373a2 100644 --- a/src/xidocpkg/commands/css.nim +++ b/src/xidocpkg/commands/css.nim @@ -6,7 +6,6 @@ import std/strutils import std/tables commands cssCommands: - proc underCmd(arg: !String): String {.command: "_".} = &"[{arg}]" diff --git a/src/xidocpkg/commands/default.nim b/src/xidocpkg/commands/default.nim index 04b9f47..c35070f 100644 --- a/src/xidocpkg/commands/default.nim +++ b/src/xidocpkg/commands/default.nim @@ -40,13 +40,20 @@ const shtTwilight: staticRead("../../prism/twilight.css"), shtCoy: staticRead("../../prism/coy.css"), shtSolarizedLight: staticRead("../../prism/solarized-light.css"), - shtTomorrowNight: staticRead("../../prism/tomorrow-night.css"), + shtTomorrowNight: staticRead("../../prism/tomorrow-night.css") ] commands defaultCommands: - - template theoremLikeCommand(procName: untyped, cmdName: static string, phrase: static Phrase, htmlTmpl, latexTmpl: static string, commands: Commands = nil) = - proc procName(thName: ?Markup, label: ?String, content: !Markup): Markup {.command: cmdName, safe, useCommands: commands.} = + template theoremLikeCommand( + procName: untyped, + cmdName: static string, + phrase: static Phrase, + htmlTmpl, latexTmpl: static string, + commands: Commands = nil, + ) = + proc procName( + thName: ?Markup, label: ?String, content: !Markup + ): Markup {.command: cmdName, safe, useCommands: commands.} = let thName = thName.filter(n => n != "") let word = phrase.translate(doc.lookup(lang)) var num: string @@ -64,17 +71,28 @@ commands defaultCommands: fullName.add(" (" & thName & ")") fullName.add(".") ifSome label: - htg.`div`(class = &"xd-theorem-like xd-$1" % cmdName, id = label, htg.strong(fullName), " ", (htmlTmpl % content)) + htg.`div`( + class = &"xd-theorem-like xd-$1" % cmdName, + id = label, + htg.strong(fullName), + " ", + (htmlTmpl % content), + ) do: - htg.`div`(class = &"xd-theorem-like xd-$1" % cmdName, htg.strong(fullName), " ", (htmlTmpl % content)) + htg.`div`( + class = &"xd-theorem-like xd-$1" % cmdName, + htg.strong(fullName), + " ", + (htmlTmpl % content), + ) of tLatex: doc.addToHead.incl "usepackage"{"amsthm"} doc.addToHead.incl: "theoremstyle"{"definition"} & "newtheorem"{"XD" & cmdName}{word} & - "theoremstyle"{"definition"} & "newtheorem*"{"XD" & cmdName & "*"}{word} & - "newenvironment"{"XD" & cmdName & "Manual"}["1"]{ - "renewcommand"{"\\theXD" & cmdName}{"#1"} & "\\XD" & cmdName - }{"\\endXD" & cmdName} + "theoremstyle"{"definition"} & "newtheorem*"{"XD" & cmdName & "*"}{word} & + "newenvironment"{"XD" & cmdName & "Manual"}["1"]{ + "renewcommand"{"\\theXD" & cmdName}{"#1"} & "\\XD" & cmdName + }{"\\endXD" & cmdName} var envName = "XD" & cmdName var envContent = "" ifSome label: @@ -89,7 +107,8 @@ commands defaultCommands: env(envName, envContent) of tGemtext: # TODO: labels - "\n\n$1. $2" % [ifSome(thName, "$1 ($2)" % [word, thName], "$1" % [word]), content] + "\n\n$1. $2" % + [ifSome(thName, "$1 ($2)" % [word, thName], "$1" % [word]), content] proc commentCmd(arg: Literal) {.command: "#", safe.} = discard @@ -118,13 +137,19 @@ commands defaultCommands: proc quoteCmd(arg: !Markup): Markup {.command: "\"", safe.} = pQuotation.translate(doc.lookup(lang)) % arg - proc inlineMathCmd(math: !String): Markup {.command: "$", safe, useCommands: mathCommands.} = + proc inlineMathCmd( + math: !String + ): Markup {.command: "$", safe, useCommands: mathCommands.} = doc.renderMath(math, displayMode = false) - proc blockMathCmd(math: !String): Markup {.command: "$$", safe, useCommands: mathCommands.} = + proc blockMathCmd( + math: !String + ): Markup {.command: "$$", safe, useCommands: mathCommands.} = doc.renderMath(math, displayMode = true) - proc alignedMathCmd(math: !String): Markup {.command: "$$&", safe, useCommands: mathCommands.} = + proc alignedMathCmd( + math: !String + ): Markup {.command: "$$&", safe, useCommands: mathCommands.} = doc.renderMath(env("align*", math), displayMode = true, addDelimiters = false) proc heineify(math: string): string = @@ -189,7 +214,9 @@ commands defaultCommands: of tGemtext: arg - proc blockQuoteCmd(quote: !Markup, author: ?Markup): Markup {.command: "block-quote", safe.} = + proc blockQuoteCmd( + quote: !Markup, author: ?Markup + ): Markup {.command: "block-quote", safe.} = case doc.target of tHtml: htg.blockquote: @@ -204,7 +231,9 @@ commands defaultCommands: # TODO author support "\n> $1\n" % quote - proc checkboxesCmd(arg: !String): Markup {.command: "checkboxes", safe, useCommands: checkboxCommands.} = + proc checkboxesCmd( + arg: !String + ): Markup {.command: "checkboxes", safe, useCommands: checkboxCommands.} = case doc.target of tHtml: doc.addToStyle.incl """.xd-checkbox-unchecked{list-style-type:"☐ "}.xd-checkbox-checked{list-style-type:"☑ "}.xd-checkbox-crossed{list-style-type:"☒ "}""" @@ -233,12 +262,17 @@ commands defaultCommands: of tGemtext: "\n```\n{$1}\n```\n" % code - proc codeBlockCmd(lang: ?String, code: !String): Markup {.command: "code-block", safe.} = + proc codeBlockCmd( + lang: ?String, code: !String + ): Markup {.command: "code-block", safe.} = case doc.target of tHtml: applySyntaxHighlightingTheme() ifSome lang: - htg.pre(class = &"language-{lang}", htg.code(class = &"language-{lang}", code.highlightCode(lang))) + htg.pre( + class = &"language-{lang}", + htg.code(class = &"language-{lang}", code.highlightCode(lang)), + ) do: htg.pre(htg.code(code.escapeText(doc.target))) of tLatex: @@ -247,7 +281,9 @@ commands defaultCommands: of tGemtext: "\n```\n{$1}\n```\n" % code - proc collapseCmd(title: !Markup, content: !Markup): Markup {.command: "collapse", safe.} = + proc collapseCmd( + title: !Markup, content: !Markup + ): Markup {.command: "collapse", safe.} = case doc.target of tHtml: htg.details(htg.summary(title), content) @@ -256,7 +292,9 @@ commands defaultCommands: of tGemtext: content # TODO: should somehow include title? - proc collapsibleCmd(title: !Markup, content: !Markup): Markup {.command: "collapsible", safe.} = + proc collapsibleCmd( + title: !Markup, content: !Markup + ): Markup {.command: "collapsible", safe.} = case doc.target of tHtml: htg.details(open = "", htg.summary(title), content) @@ -299,8 +337,17 @@ commands defaultCommands: template def(global: static bool) {.dirty.} = let params = ifSome(paramList, paramList.splitWhitespace, @[]) - doc.stack[when global: 0 else: ^2].commands[name] = proc(arg: StringView): XidocValue = - let argsList = if arg == "": @[] else: parseXidocArguments(arg) + doc.stack[ + when global: + 0 + else: + ^2 + ].commands[name] = proc(arg: StringView): XidocValue = + let argsList = + if arg == "": + @[] + else: + parseXidocArguments(arg) if argsList.len != params.len: xidocError &"""Command {name} needs exactly {params.len} argument{(if params.len == 1: "" else: "s")}, {argsList.len} given""" # Merging the following two lines into one causes the thing to break. WTF? @@ -308,18 +355,22 @@ commands defaultCommands: doc.stack[^1].args = argsTable result = XidocValue(typ: Markup, str: doc.renderStr(body)) - proc defCmd(name: !String, paramList: ?String, body: Raw): Markup {.command: "def", safe.} = + proc defCmd( + name: !String, paramList: ?String, body: Raw + ): Markup {.command: "def", safe.} = def(global = false) - proc defGlobalCmd(name: !String, paramList: ?String, body: Raw): Markup {.command: "def-global".} = + proc defGlobalCmd( + name: !String, paramList: ?String, body: Raw + ): Markup {.command: "def-global".} = def(global = true) proc descriptionListCmd(args: *Markup): Markup {.command: "description-list", safe.} = if args.len mod 2 != 0: xidocError "Arguments to description-list must come in pairs" var defs: seq[array[2, string]] - for i in 0..<(args.len div 2): - defs.add [args[2*i], args[2*i + 1]] + for i in 0 ..< (args.len div 2): + defs.add [args[2 * i], args[2 * i + 1]] case doc.target of tHtml: htg.dl(defs.mapIt(htg.dt(it[0]) & htg.dd(it[1])).join) @@ -330,7 +381,9 @@ commands defaultCommands: theoremLikeCommand(dfnCmd, "dfn", pDefinition, "$1", "$1") - proc drawCmd(width: ?String, height: ?String, desc: !String): Markup {.command: "draw", useCommands: drawCommands.} = + proc drawCmd( + width: ?String, height: ?String, desc: !String + ): Markup {.command: "draw", useCommands: drawCommands.} = case doc.target of tHtml: &"""{desc}""" @@ -367,11 +420,15 @@ commands defaultCommands: do: htg.figure(content) of tLatex: - env("figure", "[h]\\centering" & content & ifSome(caption, "caption"{caption}, "")) + env( + "figure", "[h]\\centering" & content & ifSome(caption, "caption"{caption}, "") + ) of tGemtext: "\n" & content & ifSome(caption, "\n" & caption, "") - proc forEachCmd(name: !String, list: !List, tmpl: Raw): List {.command: "for-each", safe.} = + proc forEachCmd( + name: !String, list: !List, tmpl: Raw + ): List {.command: "for-each", safe.} = var results: seq[XidocValue] for item in list: let itemCopy = item @@ -383,7 +440,9 @@ commands defaultCommands: let path = doc.stack[0].path ifSome(path, path.absolutePath, "") - proc getDocPathRelativeToContainingCmd(arg: !String): String {.command: "get-doc-path-relative-to-containing".} = + proc getDocPathRelativeToContainingCmd( + arg: !String + ): String {.command: "get-doc-path-relative-to-containing".} = when defined(js): "" else: @@ -415,7 +474,9 @@ commands defaultCommands: theoremLikeCommand(hintCmd, "hint", pHint, "$1", "$1") - proc htmlAddAttrsCmd(args: *String, tag: !Markup): Markup {.command: "html-add-attrs".} = + proc htmlAddAttrsCmd( + args: *String, tag: !Markup + ): Markup {.command: "html-add-attrs".} = case doc.target of tHtml: var matches: array[2, string] @@ -425,9 +486,9 @@ commands defaultCommands: var classes = newSeq[string]() for arg in args: if arg.startsWith "#": - attrs.add "id=\"$1\"" % arg[1..^1] + attrs.add "id=\"$1\"" % arg[1 ..^ 1] elif arg.startsWith ".": - classes.add arg[1..^1] + classes.add arg[1 ..^ 1] else: attrs.add arg if classes.len != 0: @@ -477,14 +538,17 @@ commands defaultCommands: body: body, target: doc.target, snippet: true, - stack: @[Frame( - cmdName: "[top]".toStringView, - lang: some doc.lookup(lang), - path: some(path), - )] + stack: + @[ + Frame( + cmdName: "[top]".toStringView, + lang: some doc.lookup(lang), + path: some(path), + ) + ], ) subdoc.stack[0].commands = defaultCommands(subdoc) - for i in 0..<(args.len div 2): + for i in 0 ..< (args.len div 2): subdoc.templateArgs[args[2 * i]] = args[2 * i + 1] subdoc.renderStr except IOError: @@ -547,21 +611,21 @@ commands defaultCommands: proc langCmd(langStr: !String, body: Raw): Markup {.command: "lang", safe.} = let lang = case langStr.toLowerAscii - of "en", "english": lEnglish - of "cs", "cz", "czech": lCzech - of "de", "german": lGerman - else: xidocError "Unknown language: $1" % langStr + of "en", "english": + lEnglish + of "cs", "cz", "czech": + lCzech + of "de", "german": + lGerman + else: + xidocError "Unknown language: $1" % langStr doc.stack[^1].lang = some lang doc.renderStr(body) theoremLikeCommand(lemmaCmd, "lemma", pLemma, "$1", "$1") proc linesCmd(lns: *Markup): Markup {.command: "lines", safe.} = - const seps = [ - tHtml: htg.br(), - tLatex: "\\\\", - tGemtext: "\n" - ] + const seps = [tHtml: htg.br(), tLatex: "\\\\", tGemtext: "\n"] lns.join(seps[doc.target]) proc linkCmd(name: ?Markup, url: !String): Markup {.command: "link", safe.} = @@ -573,7 +637,9 @@ commands defaultCommands: of tGemtext: ifSome(name, "\n=> $1 $2" % [url, name], "\n=> $1" % [url]) - proc linkImageCmd(alt: !String, url: !String, link: ?String): Markup {.command: "link-image".} = + proc linkImageCmd( + alt: !String, url: !String, link: ?String + ): Markup {.command: "link-image".} = case doc.target of tHtml: ifSome link: @@ -605,20 +671,25 @@ commands defaultCommands: xidocError "The list-dirs command is not available when using JavaScript" else: let currentDir = doc.lookup(path).splitFile.dir - walkDirs(currentDir / arg).toSeq.mapIt(XidocValue(typ: String, str: it.relativePath(currentDir))) + walkDirs(currentDir / arg).toSeq.mapIt( + XidocValue(typ: String, str: it.relativePath(currentDir)) + ) proc listFilesCmd(arg: !String): List {.command: "list-files".} = when defined(js): xidocError "The list-files command is not available when using JavaScript" else: let currentDir = doc.lookup(path).splitFile.dir - walkFiles(currentDir / arg).toSeq.mapIt(XidocValue(typ: String, str: it.relativePath(currentDir))) + walkFiles(currentDir / arg).toSeq.mapIt( + XidocValue(typ: String, str: it.relativePath(currentDir)) + ) proc matextCmd(arg: !String): Markup {.command: "matext".} = - let math = try: - arg.matext - except ValueError: - xidocError "Error when parsing math: $1" % arg + let math = + try: + arg.matext + except ValueError: + xidocError "Error when parsing math: $1" % arg case doc.target of tHtml: htg.pre(class = "xd-matext", math) @@ -674,7 +745,9 @@ commands defaultCommands: proc passRawCmd(arg: Raw): Markup {.command: "pass-raw".} = arg - proc pikchrCmd(width: ?Markup, height: ?Markup, text: !String): Markup {.command: "pikchr", safe.} = + proc pikchrCmd( + width: ?Markup, height: ?Markup, text: !String + ): Markup {.command: "pikchr", safe.} = if doc.target != tHtml: xidocError "Pikchr currently only works with the HTML backend" var svg = text.pikchr(darkMode = doc.settings.darkMode) @@ -683,7 +756,8 @@ commands defaultCommands: svg = htmlAddAttrsCmd(@[&"style=\"width:{width};height:{height}\""], svg) do: svg = htmlAddAttrsCmd(@[&"style=\"width:{width}\""], svg) - do: discard + do: + discard svg proc pikchrRawCmd(text: Raw): Markup {.command: "pikchr-raw", safe.} = @@ -708,21 +782,27 @@ commands defaultCommands: if label in doc.labelNums: let prefix = block: ifSome prefix: - if prefix == "": "" - else: prefix & " " + if prefix == "": + "" + else: + prefix & " " do: doc.labelNums[label].prefix prefix & doc.labelNums[label].num else: xidocError "Label not found: $1" % label case doc.target - of tHtml: htg.a(href = "#" & label, text) - of tLatex, tGemtext: text + of tHtml: + htg.a(href = "#" & label, text) + of tLatex, tGemtext: + text proc renderCmd(arg: !String): Markup {.command: "render", safe.} = doc.renderStr(arg) - proc replaceSuffixCmd(sub: !String, by: !String, str: !String): String {.command: "replace-suffix", safe.} = + proc replaceSuffixCmd( + sub: !String, by: !String, str: !String + ): String {.command: "replace-suffix", safe.} = var str = str if str.endsWith(sub): str.removeSuffix(sub) @@ -752,7 +832,9 @@ commands defaultCommands: else: xidocError &"Invalid setting: {key}" - proc resetTheoremLikeCounterCmd(prefix: ?String) {.command: "reset-theorem-like-counter", safe.} = + proc resetTheoremLikeCounterCmd( + prefix: ?String + ) {.command: "reset-theorem-like-counter", safe.} = doc.theoremLikeCounter = 1 ifSome prefix: doc.settings.theoremLikeNumberPrefix = prefix @@ -768,7 +850,9 @@ commands defaultCommands: of tGemtext: xidocError "Tables are currently not supported in the Gemtext backend" - proc sectionCmd(name: ?Markup, id: ?String, content: Raw): Markup {.command: "section", safe.} = + proc sectionCmd( + name: ?Markup, id: ?String, content: Raw + ): Markup {.command: "section", safe.} = let depth = doc.stack.countIt(it.cmdName == "section") case doc.target of tHtml: @@ -782,18 +866,29 @@ commands defaultCommands: else: "h6" ifSome id: doc.addTableOfContentsEntry(htg.a(href = "#" & id, name)) - htg.section(id = id, "<$1 class=\"xd-section-heading\">$2$3" % [headingTag, name, doc.renderStr(content)]) + htg.section( + id = id, + "<$1 class=\"xd-section-heading\">$2$3" % + [headingTag, name, doc.renderStr(content)], + ) do: - htg.section("<$1 class=\"xd-section-heading\">$2$3" % [headingTag, name, doc.renderStr(content)]) + htg.section( + "<$1 class=\"xd-section-heading\">$2$3" % + [headingTag, name, doc.renderStr(content)] + ) do: htg.section(doc.renderStr(content)) of tLatex: var cmd = case depth - of 1: "section" - of 2: "subsection" - of 3: "subsubsection" - else: xidocError "Sections can only be nested 3 levels deep in LaTeX" + of 1: + "section" + of 2: + "subsection" + of 3: + "subsubsection" + else: + xidocError "Sections can only be nested 3 levels deep in LaTeX" if id.isNone: cmd.add("*") ifSome name: @@ -823,33 +918,43 @@ commands defaultCommands: of "math-renderer": doc.settings.mathRenderer = case val.toLower - of "katex", "katex-html": mrKatexHtml - of "katex-mathml": mrKatexMathml - of "temml": mrTemml - else: raise newException(ValueError, "") + of "katex", "katex-html": + mrKatexHtml + of "katex-mathml": + mrKatexMathml + of "temml": + mrTemml + else: + raise newException(ValueError, "") of "mathml-only": xidocWarning "The \"mathml-only\" setting is deprecated. Please use the \"math-renderer\" setting instead." - doc.settings.mathRenderer = - if val.parseBool: mrKatexMathml - else: mrKatexHtml + doc.settings.mathRenderer = if val.parseBool: mrKatexMathml else: mrKatexHtml of "syntax-highlighting-theme": doc.settings.syntaxHighlightingTheme = case val.toLower - of "default": shtDefault - of "dark": shtDark - of "funky": shtFunky - of "funky-x": shtFunkyX - of "okaidia": shtOkaidia - of "twilight": shtTwilight - of "coy": shtCoy - of "solarized-light": shtSolarizedLight - of "tomorrow-night": shtTomorrowNight - else: raise newException(ValueError, "") + of "default": + shtDefault + of "dark": + shtDark + of "funky": + shtFunky + of "funky-x": + shtFunkyX + of "okaidia": + shtOkaidia + of "twilight": + shtTwilight + of "coy": + shtCoy + of "solarized-light": + shtSolarizedLight + of "tomorrow-night": + shtTomorrowNight + else: + raise newException(ValueError, "") of "temml": xidocWarning "The \"temml\" setting is deprecated. Please use the \"math-renderer\" setting instead." - doc.settings.mathRenderer = - if val.parseBool: mrTemml - else: mrKatexHtml + doc.settings.mathRenderer = if val.parseBool: mrTemml else: mrKatexHtml of "temml-stylesheet-path": doc.settings.temmlStylesheetPath = val else: @@ -860,17 +965,23 @@ commands defaultCommands: proc setDocLangCmd(arg: !String) {.command: "set-doc-lang", safe.} = doc.stack[0].lang = some( case arg.toLowerAscii - of "en", "english": lEnglish - of "cs", "cz", "czech": lCzech - of "de", "german": lGerman - else: xidocError "Unknown language: $1" % arg + of "en", "english": + lEnglish + of "cs", "cz", "czech": + lCzech + of "de", "german": + lGerman + else: + xidocError "Unknown language: $1" % arg ) proc setFaviconCmd(url: !String) {.command: "set-favicon".} = if doc.target == tHtml: doc.addToHead.incl htg.link(rel = "icon", href = url) - proc setSyntaxHighlightingThemeCmd(theme: !String): Markup {.command: "set-syntax-highlighting-theme", safe.} = + proc setSyntaxHighlightingThemeCmd( + theme: !String + ): Markup {.command: "set-syntax-highlighting-theme", safe.} = xidocWarning "[set-syntax-highlighting-theme] is deprecated. Use [set syntax-highlighting-theme] instead." setCmd("syntax-highlighting-theme", theme) @@ -909,11 +1020,14 @@ commands defaultCommands: of tGemtext: xidocError "The spoiler command is not supported in the Gemtext backend" - proc spoilerHintCmd(name: ?Markup, content: !Markup): Markup {.command: "spoiler-hint", safe.} = + proc spoilerHintCmd( + name: ?Markup, content: !Markup + ): Markup {.command: "spoiler-hint", safe.} = let word = pHint.translate(doc.lookup(lang)) case doc.target of tHtml: - htg.details(class = "xd-spoiler xd-theorem-like xd-hint", + htg.details( + class = "xd-spoiler xd-theorem-like xd-hint", htg.summary(htg.strong(ifSome(name, "$1 ($2)" % [word, name], "$1" % [word]))), content, ) @@ -924,11 +1038,14 @@ commands defaultCommands: of tGemtext: xidocError "The spoiler-hint command is not supported in the Gemtext backend" - proc spoilerSolutionCmd(name: ?Markup, content: !Markup): Markup {.command: "spoiler-solution", safe.} = + proc spoilerSolutionCmd( + name: ?Markup, content: !Markup + ): Markup {.command: "spoiler-solution", safe.} = let word = pSolution.translate(doc.lookup(lang)) case doc.target of tHtml: - htg.details(class = "xd-spoiler xd-theorem-like xd-solution", + htg.details( + class = "xd-spoiler xd-theorem-like xd-solution", htg.summary(htg.strong(ifSome(name, "$1 ($2)" % [word, name], "$1" % [word]))), content, ) @@ -972,7 +1089,8 @@ commands defaultCommands: try: doc.templateArgs[arg] except KeyError: - xidocError: &"Template argument not found: {arg}" + xidocError: + &"Template argument not found: {arg}" proc termCmd(arg: !Markup): Markup {.command: "term", safe.} = case doc.target @@ -994,7 +1112,8 @@ commands defaultCommands: doc.addToHead.incl "title"{title} ifSome author: doc.addToHead.incl "author"{author} - do: discard + do: + discard "\\maketitle" of tGemtext: "# $1\n\n" % title @@ -1016,15 +1135,16 @@ commands defaultCommands: case doc.target of tHtml: - - proc generateHtmlTag(tag: string, args: seq[string], body = "", paired = true): string = + proc generateHtmlTag( + tag: string, args: seq[string], body = "", paired = true + ): string = var attrs = newSeq[string]() var classes = newSeq[string]() for arg in args: if arg.startsWith "#": - attrs.add "id=\"$1\"" % arg[1..^1] + attrs.add "id=\"$1\"" % arg[1 ..^ 1] elif arg.startsWith ".": - classes.add arg[1..^1] + classes.add arg[1 ..^ 1] else: attrs.add arg if classes.len != 0: @@ -1039,17 +1159,19 @@ commands defaultCommands: for tag in htmlTags: # This proc makes sure that tag is captured by value - (proc(tag: string) = - if tag in htmlSelfClosingTags: - proc theTagCmd(args: *String): Markup {.command: &"<{tag}>".} = - generateHtmlTag(tag, args, paired = false) - else: - proc theTagCmd(args: *String, body: !Markup): Markup {.command: &"<{tag}>".} = - generateHtmlTag(tag, args, body) - )(tag) - of tLatex: + ( + proc(tag: string) = + if tag in htmlSelfClosingTags: + proc theTagCmd(args: *String): Markup {.command: &"<{tag}>".} = + generateHtmlTag(tag, args, paired = false) + else: + proc theTagCmd(args: *String, body: !Markup): Markup {.command: &"<{tag}>".} = + generateHtmlTag(tag, args, body) + + )(tag) + of tLatex: proc backslashCmd(command: !String, args: *Markup): Markup {.command: "\\".} = "\\" & command & args.mapIt("{$1}" % it).join diff --git a/src/xidocpkg/commands/draw.nim b/src/xidocpkg/commands/draw.nim index 46aa734..2764467 100644 --- a/src/xidocpkg/commands/draw.nim +++ b/src/xidocpkg/commands/draw.nim @@ -7,7 +7,6 @@ import std/strutils import std/tables commands drawCommands: - type XY = tuple[x, y: float] proc parseFloat(x: string): float = @@ -24,7 +23,7 @@ commands drawCommands: except ValueError, IndexDefect: xidocError &"Invalid coordinates: {xy}" - template drawParseArgs {.dirty.} = + template drawParseArgs() {.dirty.} = when declared(a): let a = a.parseXY when declared(b): @@ -38,21 +37,21 @@ commands drawCommands: func nonEmpty(s: string): bool = s != "" when declared(width): - let width = width.filter(nonEmpty).get( - when declared(fill): - if fill.map(nonEmpty) == some(true): - "0" + let width = + width.filter(nonEmpty).get( + when declared(fill): + if fill.map(nonEmpty) == some(true): "0" else: "3" else: "3" - else: - "3" - ) + ) when declared(color): let color = color.filter(nonEmpty).get("currentColor") when declared(fill): let fill = fill.filter(nonEmpty).get("transparent") - proc CarCmd(a: !String, r: !String, width: ?String, color: ?String, fill: ?String): Markup {.command: "Car".} = + proc CarCmd( + a: !String, r: !String, width: ?String, color: ?String, fill: ?String + ): Markup {.command: "Car".} = drawParseArgs case doc.target of tHtml: @@ -62,7 +61,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc CcrCmd(c: !String, r: !String, width: ?String, color: ?String, fill: ?String): Markup {.command: "Ccr".} = + proc CcrCmd( + c: !String, r: !String, width: ?String, color: ?String, fill: ?String + ): Markup {.command: "Ccr".} = drawParseArgs case doc.target of tHtml: @@ -72,7 +73,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc LabCmd(a: !String, b: !String, width: ?String, color: ?String): Markup {.command: "Lab".} = + proc LabCmd( + a: !String, b: !String, width: ?String, color: ?String + ): Markup {.command: "Lab".} = drawParseArgs case doc.target of tHtml: @@ -82,7 +85,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc LauCmd(a: !String, u: !String, width: ?String, color: ?String): Markup {.command: "Lau".} = + proc LauCmd( + a: !String, u: !String, width: ?String, color: ?String + ): Markup {.command: "Lau".} = drawParseArgs case doc.target of tHtml: @@ -92,7 +97,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc LcuCmd(c: !String, u: !String, width: ?String, color: ?String): Markup {.command: "Lcu".} = + proc LcuCmd( + c: !String, u: !String, width: ?String, color: ?String + ): Markup {.command: "Lcu".} = drawParseArgs case doc.target of tHtml: @@ -102,7 +109,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc RabCmd(a: !String, b: !String, width: ?String, color: ?String, fill: ?String): Markup {.command: "Rab".} = + proc RabCmd( + a: !String, b: !String, width: ?String, color: ?String, fill: ?String + ): Markup {.command: "Rab".} = drawParseArgs case doc.target of tHtml: @@ -112,7 +121,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc RauCmd(a: !String, u: !String, width: ?String, color: ?String, fill: ?String): Markup {.command: "Rau".} = + proc RauCmd( + a: !String, u: !String, width: ?String, color: ?String, fill: ?String + ): Markup {.command: "Rau".} = drawParseArgs case doc.target of tHtml: @@ -122,7 +133,9 @@ commands drawCommands: of tGemtext: xidocError "Drawing is currently not implemented in the Gemtext backend" - proc RcuCmd(c: !String, u: !String, width: ?String, color: ?String, fill: ?String): Markup {.command: "Rcu".} = + proc RcuCmd( + c: !String, u: !String, width: ?String, color: ?String, fill: ?String + ): Markup {.command: "Rcu".} = drawParseArgs case doc.target of tHtml: diff --git a/src/xidocpkg/commands/javascript.nim b/src/xidocpkg/commands/javascript.nim index a5d6a35..1b3b920 100644 --- a/src/xidocpkg/commands/javascript.nim +++ b/src/xidocpkg/commands/javascript.nim @@ -7,22 +7,27 @@ import std/strformat import std/strutils commands jsCommands: - - proc forCmd(name: !String, container: !String, body: !String): String {.command: "for".} = + proc forCmd( + name: !String, container: !String, body: !String + ): String {.command: "for".} = &"for(const {name} of {container}){{{body}}}" - proc ifCmd(condition: !String, body: !String, elses: *String): String {.command: "if".} = + proc ifCmd( + condition: !String, body: !String, elses: *String + ): String {.command: "if".} = result = &"if({condition}){{{body}}}" - for i in 0..<(elses.len div 2): + for i in 0 ..< (elses.len div 2): result &= &"else if({elses[2*i]}){{{elses[2*i+1]}}}" if elses.len mod 2 == 1: result &= &"else{{{elses[^1]}}}" - proc ifExprCmd(condition: !String, expr: !String, elses: *String): String {.command: "if=".} = + proc ifExprCmd( + condition: !String, expr: !String, elses: *String + ): String {.command: "if=".} = result = &"{condition}?{expr}" if elses.len mod 2 != 1: xidocError "[if=] requires an else clause" - for i in 0..<(elses.len div 2): + for i in 0 ..< (elses.len div 2): result &= &":{elses[2*i]}?{elses[2*i+1]}" result &= &":{elses[^1]}" @@ -32,7 +37,9 @@ commands jsCommands: proc letCmd(name: !String, value: !String): String {.command: "let".} = &"const {name}={value};" - proc rangeCmd(name: !String, n: !String, m: ?String, r: ?String, body: !String): String {.command: "range".} = + proc rangeCmd( + name: !String, n: !String, m: ?String, r: ?String, body: !String + ): String {.command: "range".} = ifSome m: ifSome r: &"for(let {name}={n};{name}<{m};{name}+={r}){{{body}}}" @@ -45,12 +52,14 @@ commands jsCommands: if cases.len mod 2 != 0: xidocError "Extra arguments to [switch] must come in pairs" result = &"switch({name}){{" - for i in 0..<(cases.len div 2): + for i in 0 ..< (cases.len div 2): let label = - if cases[2*i] == "default": "default" - else: "case " & cases[2*i] + if cases[2 * i] == "default": + "default" + else: + "case " & cases[2 * i] result &= &"{label}:{{{cases[2*i+1]}}}break;" - result &= "}"#} + result &= "}" #} proc varCmd(name: !String, value: ?String): String {.command: "var".} = ifSome(value, &"let {name}={value};", &"let {name};") diff --git a/src/xidocpkg/commands/math.nim b/src/xidocpkg/commands/math.nim index 69cdeeb..3d6e88f 100644 --- a/src/xidocpkg/commands/math.nim +++ b/src/xidocpkg/commands/math.nim @@ -13,7 +13,6 @@ import std/strutils import std/tables commands mathCommands: - proc underCmd(arg: !String): String {.command: "_".} = &"[{arg}]" @@ -26,25 +25,35 @@ commands mathCommands: # Enclosing stuff proc dotCmd(arg: !String): String {.command: ".".} = "{\\left($1\\right)}" % arg + proc bracketsCmd(arg: !String): String {.command: "()".} = "{\\left[$1\\right]}" % arg + proc bracesCmd(x: !String, y: ?String): String {.command: "{}".} = "{\\left\\{$1\\right\\}}" % (x & ifSome(y, "\\,\\middle|\\," & y, "")) + proc anglesCmd(arg: !String): String {.command: "<>".} = "{\\left\\langle $1\\right\\rangle}" % arg + proc pipeCmd(arg: !String): String {.command: "|".} = "{\\left\\lvert $1\\right\\rvert}" % arg + proc pipePipeCmd(arg: !String): String {.command: "||".} = "{\\left\\lVert $1\\right\\rVert}" % arg + proc vDotCmd(arg: !String): String {.command: "v.".} = "{\\overgroup{\\undergroup{$1}}}" % arg + proc floorCmd(arg: !String): String {.command: "floor".} = "{\\left\\lfloor $1\\right\\rfloor}" % arg + proc ceilCmd(arg: !String): String {.command: "ceil".} = "{\\left\\lceil $1\\right\\rceil}" % arg # Analysis/Calculus - proc intCmd(lb: ?String, ub: ?String, expr: !String, varname: !String): String {.command: "int".} = + proc intCmd( + lb: ?String, ub: ?String, expr: !String, varname: !String + ): String {.command: "int".} = if lb.isSome: if ub.isSome: "\\int_{$1}^{$2}$3\\,\\mathrm d$4" % [lb.get, ub.get, expr, varname] @@ -52,9 +61,15 @@ commands mathCommands: "\\int_{$1}$2\\,\\mathrm d$3" % [lb.get, expr, varname] else: "\\int $1\\,\\mathrm d$2" % [expr, varname] - proc intdCmd(varname: !String, lb: ?String, ub: ?String, expr: !String): String {.command: "intd".} = + + proc intdCmd( + varname: !String, lb: ?String, ub: ?String, expr: !String + ): String {.command: "intd".} = intCmd(lb, ub, expr, varname) - proc dintCmd(varname: !String, lb: ?String, ub: ?String, expr: !String): String {.command: "dint".} = + + proc dintCmd( + varname: !String, lb: ?String, ub: ?String, expr: !String + ): String {.command: "dint".} = if lb.isSome: if ub.isSome: "\\int_{$1}^{$2}\\mathrm d$4\\,$3" % [lb.get, ub.get, expr, varname] @@ -62,38 +77,51 @@ commands mathCommands: "\\int_{$1}\\mathrm d$3\\,$2" % [lb.get, expr, varname] else: "\\int\\mathrm d$2\\,$1" % [expr, varname] + proc limCmd(varname: ?String, point: ?String, set: ?String): String {.command: "lim".} = ifSome set: - "\\lim_{\\substack{$1\\to $2\\\\$1\\in $3}}" % [varname.get("n"), point.get("\\infty"), set] + "\\lim_{\\substack{$1\\to $2\\\\$1\\in $3}}" % + [varname.get("n"), point.get("\\infty"), set] do: "\\lim_{$1\\to $2}" % [varname.get("n"), point.get("\\infty")] + proc liminfCmd(varname: ?String, point: ?String): String {.command: "liminf".} = "\\liminf_{$1\\to $2}" % [varname.get("n"), point.get("\\infty")] + proc limsupCmd(varname: ?String, point: ?String): String {.command: "limsup".} = "\\limsup_{$1\\to $2}" % [varname.get("n"), point.get("\\infty")] - proc sumCmd(varname: ?String, lowerBound: ?String, upperBound: ?String): String {.command: "sum".} = - "\\sum_{$1=$2}^{$3}" % [varname.get("n"), lowerBound.get("1"), upperBound.get("\\infty")] + + proc sumCmd( + varname: ?String, lowerBound: ?String, upperBound: ?String + ): String {.command: "sum".} = + "\\sum_{$1=$2}^{$3}" % + [varname.get("n"), lowerBound.get("1"), upperBound.get("\\infty")] # Inspired by the physics package proc ddCmd(x: !String): String {.command: "dd".} = "{\\mathrm d$1}" % [x] + proc ddExpCmd(n: !String, x: !String): String {.command: "dd^".} = "{\\mathrm d^{$1}$2}" % [n, x] + proc dvCmd(f: ?String, x: !String): String {.command: "dv".} = if f.isSome: "\\frac{\\mathrm d$1}{\\mathrm d$2}" % [f.get, x] else: "\\frac{\\mathrm d}{\\mathrm d$1}" % [x] + proc dvExpCmd(n: !String, f: ?String, x: !String): String {.command: "dv^".} = if f.isSome: "\\frac{\\mathrm d^{$1}$2}{\\mathrm d$3^{$1}}" % [n, f.get, x] else: "\\frac{\\mathrm d^{$1}}{\\mathrm d$2^{$1}}" % [n, x] + proc pdvCmd(f: ?String, x: !String): String {.command: "pdv".} = if f.isSome: "\\frac{\\partial $1}{\\partial $2}" % [f.get, x] else: "\\frac{\\partial}{\\partial $1}" % [x] + proc pdvExpCmd(n: !String, f: ?String, x: !String): String {.command: "pdv^".} = if f.isSome: "\\frac{\\partial^{$1}$2}{\\partial $3^{$1}}" % [n, f.get, x] @@ -103,18 +131,25 @@ commands mathCommands: # Matrices proc matCmd(arg: !String): String {.command: "mat".} = "\\begin{matrix}$1\\end{matrix}" % [arg] + proc dotMatCmd(arg: !String): String {.command: ".mat".} = "\\begin{pmatrix}$1\\end{pmatrix}" % [arg] + proc bracketsMatCmd(arg: !String): String {.command: "(mat)".} = "\\begin{bmatrix}$1\\end{bmatrix}" % [arg] + proc pipesMatCmd(arg: !String): String {.command: "|mat|".} = "\\begin{vmatrix}$1\\end{vmatrix}" % [arg] + proc pipePipesMatCmd(arg: !String): String {.command: "||mat||".} = "\\begin{Vmatrix}$1\\end{Vmatrix}" % [arg] # Units proc unitCmd(number: ?String, unit: !Markup): String {.command: "unit".} = - let unitRendered = unit.replacef(peg"^{\letter+}", "\\mathrm{$1}").replacef(peg"{!\letter[^\\]}{\letter+}", "$1\\mathrm{$2}") + let unitRendered = + unit.replacef(peg"^{\letter+}", "\\mathrm{$1}").replacef( + peg"{!\letter[^\\]}{\letter+}", "$1\\mathrm{$2}" + ) if number.isSome: number.get & "\\," & unitRendered else: @@ -130,7 +165,9 @@ commands mathCommands: proc alignedMathCmd(arg: Literal) {.command: "$$&".} = xidocError "Math can't be nested inside math" -proc renderMath*(doc: Document, latex: string, displayMode: bool, addDelimiters = true): string = +proc renderMath*( + doc: Document, latex: string, displayMode: bool, addDelimiters = true +): string = case doc.target of tHtml: case doc.settings.mathRenderer @@ -138,23 +175,36 @@ proc renderMath*(doc: Document, latex: string, displayMode: bool, addDelimiters let path = if doc.settings.katexStylesheetPath == "": "https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.css" - else: doc.settings.katexStylesheetPath - doc.addToHead.incl htg.link(rel = "stylesheet", href = path, crossorigin = "anonymous") + else: + doc.settings.katexStylesheetPath + doc.addToHead.incl htg.link( + rel = "stylesheet", href = path, crossorigin = "anonymous" + ) of mrKatexMathml: discard of mrTemml: let path = if doc.settings.temmlStylesheetPath == "": "https://cdn.jsdelivr.net/npm/temml@0.10.10/dist/Temml-Local.css" - else: doc.settings.temmlStylesheetPath - doc.addToHead.incl htg.link(rel = "stylesheet", href = path, crossorigin = "anonymous") + else: + doc.settings.temmlStylesheetPath + doc.addToHead.incl htg.link( + rel = "stylesheet", href = path, crossorigin = "anonymous" + ) if displayMode: doc.addToStyle.incl """xd-block-math{display:block}""" - let format = if displayMode: "$1" else: "$1" - format % renderMathKatex(latex, - displayMode = displayMode, - trust = not doc.safeMode, - renderer = doc.settings.mathRenderer) + let format = + if displayMode: + "$1" + else: + "$1" + format % + renderMathKatex( + latex, + displayMode = displayMode, + trust = not doc.safeMode, + renderer = doc.settings.mathRenderer, + ) of tLatex: doc.addToHead.incl "\\usepackage{amsmath}" doc.addToHead.incl "\\usepackage{amssymb}" @@ -165,18 +215,21 @@ proc renderMath*(doc: Document, latex: string, displayMode: bool, addDelimiters else: latex of tGemtext: - let math = try: - latex.matext - except ValueError: - xidocError "Error when parsing math: $1" % latex + let math = + try: + latex.matext + except ValueError: + xidocError "Error when parsing math: $1" % latex &"\n```\n{math}\n```\n" commands proofCommands: - proc dotLeftCmd(arg: !Markup): Markup {.command: ".<".} = case doc.target of tHtml: - htg.div(class = "xd-subproof", doc.renderMath("(\\Leftarrow)", displayMode = false) & " " & arg) + htg.div( + class = "xd-subproof", + doc.renderMath("(\\Leftarrow)", displayMode = false) & " " & arg, + ) of tLatex: "\\par\\((\\Leftarrow)\\) " & arg else: @@ -185,7 +238,10 @@ commands proofCommands: proc dotRightCmd(arg: !Markup): Markup {.command: ".>".} = case doc.target of tHtml: - htg.div(class = "xd-subproof", doc.renderMath("(\\Rightarrow)", displayMode = false) & " " & arg) + htg.div( + class = "xd-subproof", + doc.renderMath("(\\Rightarrow)", displayMode = false) & " " & arg, + ) of tLatex: "\\par\\((\\Rightarrow)\\) " & arg else: diff --git a/src/xidocpkg/commands/utils.nim b/src/xidocpkg/commands/utils.nim index cba2ea4..878554d 100644 --- a/src/xidocpkg/commands/utils.nim +++ b/src/xidocpkg/commands/utils.nim @@ -15,14 +15,16 @@ import std/tables func `==`(a, b: BackwardsIndex): bool {.borrow.} -proc expandArguments(doc: Document, name: string, arg: StringView, types: openArray[ParamType]): seq[XidocValue] = +proc expandArguments( + doc: Document, name: string, arg: StringView, types: openArray[ParamType] +): seq[XidocValue] = if types.len == 1 and types[0] == Literal: return @[XidocValue(typ: String, str: arg)] if types.len == 1 and types[0] == Raw: return @[XidocValue(typ: String, str: arg.strip)] let args = parseXidocArguments(arg) var starPos = none int - var questionPos = 0..<0 + var questionPos = 0 ..< 0 for index, typ in types: case typ.kind of ptkMultiple: @@ -32,8 +34,8 @@ proc expandArguments(doc: Document, name: string, arg: StringView, types: openAr xidocError "There can only be one star parameter" break of ptkOptional: - if questionPos == 0..<0: - questionPos = index..index + if questionPos == 0 ..< 0: + questionPos = index .. index else: questionPos.b = index of ptkOne, ptkRaw, ptkLiteral: @@ -42,20 +44,23 @@ proc expandArguments(doc: Document, name: string, arg: StringView, types: openAr if starPos.isSome: let minLen = types.len - 1 if args.len < minLen: - xidocError "Command $1 needs at least $2 arguments, $3 given" % [name, $minLen, $args.len] + xidocError "Command $1 needs at least $2 arguments, $3 given" % + [name, $minLen, $args.len] else: let minLen = types.len - questionPos.len let maxLen = types.len if args.len < minLen or args.len > maxLen: - xidocError "Command $1 needs at least $2 and at most $3 arguments, $4 given" % [name, $minLen, $maxLen, $args.len] + xidocError "Command $1 needs at least $2 and at most $3 arguments, $4 given" % + [name, $minLen, $maxLen, $args.len] proc expandIfNeeded(doc: Document, arg: StringView, typ: ParamType): XidocValue = if typ.kind == ptkRaw: XidocValue(typ: String, str: arg) else: doc.expand(arg, typ.base) + ifSome starPos: block beforeStar: - for index, typ in types[0.. 0 and args[^1].isEmpty: `end` = args.len - 2 let base = types[starPos].base - let vals = args[start..`end`].map(arg => doc.expand(arg, base)) + let vals = args[start .. `end`].map(arg => doc.expand(arg, base)) result.add XidocValue(typ: List, list: vals) block afterStar: for index, typ in types[starPos + 1 .. ^1]: let index = ^(types.len - index - starPos - 1) let val = doc.expandIfNeeded(args[index], typ) result.add val - do: # starPos.isNone + do: block beforeQuestion: - for index, typ in types[0.. maxDisplayedArgLength: - truncatedArg = truncatedArg[0.. doc.renderStr(caps[0])) + result = result.replace( + peg("'\xc0' @@ '\xc1'"), + (match: int, cnt: int, caps: openArray[string]) => doc.renderStr(caps[0]), + ) if doc.stage >= 256: xidocError "Number of post-processing iterations exceeded; this might be an internal error" diff --git a/src/xidocpkg/janetinterpret.nim b/src/xidocpkg/janetinterpret.nim index 8608f71..2fa01b7 100644 --- a/src/xidocpkg/janetinterpret.nim +++ b/src/xidocpkg/janetinterpret.nim @@ -3,7 +3,6 @@ import std/sequtils import std/strutils when defined(janet): - proc janetCall*(code: string, args: varargs[string]): string = xidocError "Janet evaluation is not available when using JavaScript" @@ -11,14 +10,13 @@ when defined(janet): xidocError "Janet evaluation is not available when using JavaScript" else: - import std/exitprocs import std/os const srcDir = currentSourcePath.parentDir.parentDir - {.passl: "-lm"} - {.compile: "../janet/janet.c"} + {.passl: "-lm".} + {.compile: "../janet/janet.c".} {.push header: srcDir / "janet/janet.h".} {.push importc.} @@ -29,6 +27,7 @@ else: JanetSignal = cint JanetTable = object JanetType = cint + {.pop.} let @@ -39,11 +38,32 @@ else: proc isType(x: Janet, typ: JanetType): bool {.importc: "janet_checktype".} proc deinitJanet() {.importc: "janet_deinit".} - proc defineSym(env: ptr JanetTable, name: cstring, val: Janet, documentation: cstring, sourceFile: cstring, sourceLine: cint) {.importc: "janet_def_sm".} - proc doBytes(env: ptr JanetTable, bytes: cstring, len: cint, sourcePath: cstring, outp: ptr Janet): cint {.importc: "janet_dobytes".} + proc defineSym( + env: ptr JanetTable, + name: cstring, + val: Janet, + documentation: cstring, + sourceFile: cstring, + sourceLine: cint, + ) {.importc: "janet_def_sm".} + + proc doBytes( + env: ptr JanetTable, bytes: cstring, len: cint, sourcePath: cstring, outp: ptr Janet + ): cint {.importc: "janet_dobytes".} + proc initJanet(): cint {.importc: "janet_init".} - proc janetCoreEnv(replacements: ptr JanetTable): ptr JanetTable {.importc: "janet_core_env".} - proc protectedCall(fun: ptr JanetFunction, argc: cint, argv: ptr Janet, outp: ptr Janet, fiber: ptr ptr JanetFiber): JanetSignal {.importc: "janet_pcall".} + proc janetCoreEnv( + replacements: ptr JanetTable + ): ptr JanetTable {.importc: "janet_core_env".} + + proc protectedCall( + fun: ptr JanetFunction, + argc: cint, + argv: ptr Janet, + outp: ptr Janet, + fiber: ptr ptr JanetFiber, + ): JanetSignal {.importc: "janet_pcall".} + proc toJanet(x: cstring): Janet {.importc: "janet_cstringv".} proc unwrapFunction(x: Janet): ptr JanetFunction {.importc: "janet_unwrap_function".} proc unwrapString(x: Janet): cstring {.importc: "janet_unwrap_string".} @@ -66,8 +86,10 @@ else: let f = functionValue.unwrapFunction let wrappedArgs = args.mapIt(it.cstring.toJanet) var value: Janet - if f.protectedCall(args.len.cint, wrappedArgs[0].unsafeAddr, value.addr, nil) == jsError: - xidocError "Error while calling Janet function: $1\n$2" % [function, $value.unwrapString] + if f.protectedCall(args.len.cint, wrappedArgs[0].unsafeAddr, value.addr, nil) == + jsError: + xidocError "Error while calling Janet function: $1\n$2" % + [function, $value.unwrapString] if not value.isType(jtString): xidocError "Returned value from Janet function is not a string: $1" % [function] $value.unwrapString diff --git a/src/xidocpkg/jsinterpret.nim b/src/xidocpkg/jsinterpret.nim index 47f55ae..4b23ccf 100644 --- a/src/xidocpkg/jsinterpret.nim +++ b/src/xidocpkg/jsinterpret.nim @@ -13,19 +13,221 @@ const prismCoreJs = staticRead(srcDir / "prism/prism-core.min.js") const prismLanguages = block: var langs: Table[string, string] for file in walkDir(srcDir / "prism/languages"): - langs[file - .path - .splitFile - .name - .dup(removeSuffix(".min")) - .dup(removePrefix("prism-")) - ] = readFile(file.path) + langs[ + file.path.splitFile.name.dup(removeSuffix(".min")).dup(removePrefix("prism-")) + ] = readFile(file.path) langs -const prismLangDependencies = {"javascript": @["clike"], "actionscript": @["javascript"], "apex": @["clike", "sql"], "arduino": @["cpp"], "aspnet": @["markup", "csharp"], "birb": @["clike"], "bison": @["c"], "c": @["clike"], "csharp": @["clike"], "cpp": @["c"], "cfscript": @["clike"], "chaiscript": @["clike", "cpp"], "cilkc": @["c"], "cilkcpp": @["cpp"], "coffeescript": @["javascript"], "crystal": @["ruby"], "css-extras": @["css"], "d": @["clike"], "dart": @["clike"], "django": @["markup-templating"], "ejs": @["javascript", "markup-templating"], "etlua": @["lua", "markup-templating"], "erb": @["ruby", "markup-templating"], "fsharp": @["clike"], "firestore-security-rules": @["clike"], "flow": @["javascript"], "ftl": @["markup-templating"], "gml": @["clike"], "glsl": @["c"], "go": @["clike"], "gradle": @["clike"], "groovy": @["clike"], "haml": @["ruby"], "handlebars": @["markup-templating"], "haxe": @["clike"], "hlsl": @["c"], "idris": @["haskell"], "java": @["clike"], "javadoc": @["markup", "java", "javadoclike"], "jolie": @["clike"], "jsdoc": @["javascript", "javadoclike", "typescript"], "js-extras": @["javascript"], "json5": @["json"], "jsonp": @["json"], "js-templates": @["javascript"], "kotlin": @["clike"], "latte": @["clike", "markup-templating", "php"], "less": @["css"], "lilypond": @["scheme"], "liquid": @["markup-templating"], "markdown": @["markup"], "markup-templating": @["markup"], "mongodb": @["javascript"], "n4js": @["javascript"], "objectivec": @["c"], "opencl": @["c"], "parser": @["markup"], "php": @["markup-templating"], "phpdoc": @["php", "javadoclike"], "php-extras": @["php"], "plsql": @["sql"], "processing": @["clike"], "protobuf": @["clike"], "pug": @["markup", "javascript"], "purebasic": @["clike"], "purescript": @["haskell"], "qsharp": @["clike"], "qml": @["javascript"], "qore": @["clike"], "racket": @["scheme"], "cshtml": @["markup", "csharp"], "jsx": @["markup", "javascript"], "tsx": @["jsx", "typescript"], "reason": @["clike"], "ruby": @["clike"], "sass": @["css"], "scss": @["css"], "scala": @["java"], "shell-session": @["bash"], "smarty": @["markup-templating"], "solidity": @["clike"], "soy": @["markup-templating"], "sparql": @["turtle"], "sqf": @["clike"], "squirrel": @["clike"], "stata": @["mata", "java", "python"], "t4-cs": @["t4-templating", "csharp"], "t4-vb": @["t4-templating", "vbnet"], "tap": @["yaml"], "tt2": @["clike", "markup-templating"], "textile": @["markup"], "twig": @["markup-templating"], "typescript": @["javascript"], "v": @["clike"], "vala": @["clike"], "vbnet": @["basic"], "velocity": @["markup"], "wiki": @["markup"], "xeora": @["markup"], "xml-doc": @["markup"], "xquery": @["markup"]}.toTable -const prismLangAliases = {"html": "markup", "xml": "markup", "svg": "markup", "mathml": "markup", "ssml": "markup", "atom": "markup", "rss": "markup", "js": "javascript", "g4": "antlr4", "ino": "arduino", "arm-asm": "armasm", "art": "arturo", "adoc": "asciidoc", "avs": "avisynth", "avdl": "avro-idl", "gawk": "awk", "sh": "bash", "shell": "bash", "shortcode": "bbcode", "rbnf": "bnf", "oscript": "bsl", "cs": "csharp", "dotnet": "csharp", "cfc": "cfscript", "cilk-c": "cilkc", "cilk-cpp": "cilkcpp", "cilk": "cilkcpp", "coffee": "coffeescript", "conc": "concurnas", "jinja2": "django", "dns-zone": "dns-zone-file", "dockerfile": "docker", "gv": "dot", "eta": "ejs", "xlsx": "excel-formula", "xls": "excel-formula", "gamemakerlanguage": "gml", "po": "gettext", "gni": "gn", "ld": "linker-script", "go-mod": "go-module", "hbs": "handlebars", "mustache": "handlebars", "hs": "haskell", "idr": "idris", "gitignore": "ignore", "hgignore": "ignore", "npmignore": "ignore", "webmanifest": "json", "kt": "kotlin", "kts": "kotlin", "kum": "kumir", "tex": "latex", "context": "latex", "ly": "lilypond", "emacs": "lisp", "elisp": "lisp", "emacs-lisp": "lisp", "md": "markdown", "moon": "moonscript", "n4jsd": "n4js", "nani": "naniscript", "objc": "objectivec", "qasm": "openqasm", "objectpascal": "pascal", "px": "pcaxis", "pcode": "peoplecode", "plantuml": "plant-uml", "pq": "powerquery", "mscript": "powerquery", "pbfasm": "purebasic", "purs": "purescript", "py": "python", "qs": "qsharp", "rkt": "racket", "razor": "cshtml", "rpy": "renpy", "res": "rescript", "robot": "robotframework", "rb": "ruby", "sh-session": "shell-session", "shellsession": "shell-session", "smlnj": "sml", "sol": "solidity", "sln": "solution-file", "rq": "sparql", "sclang": "supercollider", "t4": "t4-cs", "trickle": "tremor", "troy": "tremor", "trig": "turtle", "ts": "typescript", "tsconfig": "typoscript", "uscript": "unrealscript", "uc": "unrealscript", "url": "uri", "vb": "visual-basic", "vba": "visual-basic", "webidl": "web-idl", "mathematica": "wolfram", "nb": "wolfram", "wl": "wolfram", "xeoracube": "xeora", "yml": "yaml"}.toTable +const prismLangDependencies = { + "javascript": @["clike"], + "actionscript": @["javascript"], + "apex": @["clike", "sql"], + "arduino": @["cpp"], + "aspnet": @["markup", "csharp"], + "birb": @["clike"], + "bison": @["c"], + "c": @["clike"], + "csharp": @["clike"], + "cpp": @["c"], + "cfscript": @["clike"], + "chaiscript": @["clike", "cpp"], + "cilkc": @["c"], + "cilkcpp": @["cpp"], + "coffeescript": @["javascript"], + "crystal": @["ruby"], + "css-extras": @["css"], + "d": @["clike"], + "dart": @["clike"], + "django": @["markup-templating"], + "ejs": @["javascript", "markup-templating"], + "etlua": @["lua", "markup-templating"], + "erb": @["ruby", "markup-templating"], + "fsharp": @["clike"], + "firestore-security-rules": @["clike"], + "flow": @["javascript"], + "ftl": @["markup-templating"], + "gml": @["clike"], + "glsl": @["c"], + "go": @["clike"], + "gradle": @["clike"], + "groovy": @["clike"], + "haml": @["ruby"], + "handlebars": @["markup-templating"], + "haxe": @["clike"], + "hlsl": @["c"], + "idris": @["haskell"], + "java": @["clike"], + "javadoc": @["markup", "java", "javadoclike"], + "jolie": @["clike"], + "jsdoc": @["javascript", "javadoclike", "typescript"], + "js-extras": @["javascript"], + "json5": @["json"], + "jsonp": @["json"], + "js-templates": @["javascript"], + "kotlin": @["clike"], + "latte": @["clike", "markup-templating", "php"], + "less": @["css"], + "lilypond": @["scheme"], + "liquid": @["markup-templating"], + "markdown": @["markup"], + "markup-templating": @["markup"], + "mongodb": @["javascript"], + "n4js": @["javascript"], + "objectivec": @["c"], + "opencl": @["c"], + "parser": @["markup"], + "php": @["markup-templating"], + "phpdoc": @["php", "javadoclike"], + "php-extras": @["php"], + "plsql": @["sql"], + "processing": @["clike"], + "protobuf": @["clike"], + "pug": @["markup", "javascript"], + "purebasic": @["clike"], + "purescript": @["haskell"], + "qsharp": @["clike"], + "qml": @["javascript"], + "qore": @["clike"], + "racket": @["scheme"], + "cshtml": @["markup", "csharp"], + "jsx": @["markup", "javascript"], + "tsx": @["jsx", "typescript"], + "reason": @["clike"], + "ruby": @["clike"], + "sass": @["css"], + "scss": @["css"], + "scala": @["java"], + "shell-session": @["bash"], + "smarty": @["markup-templating"], + "solidity": @["clike"], + "soy": @["markup-templating"], + "sparql": @["turtle"], + "sqf": @["clike"], + "squirrel": @["clike"], + "stata": @["mata", "java", "python"], + "t4-cs": @["t4-templating", "csharp"], + "t4-vb": @["t4-templating", "vbnet"], + "tap": @["yaml"], + "tt2": @["clike", "markup-templating"], + "textile": @["markup"], + "twig": @["markup-templating"], + "typescript": @["javascript"], + "v": @["clike"], + "vala": @["clike"], + "vbnet": @["basic"], + "velocity": @["markup"], + "wiki": @["markup"], + "xeora": @["markup"], + "xml-doc": @["markup"], + "xquery": @["markup"] +}.toTable +const prismLangAliases = { + "html": "markup", + "xml": "markup", + "svg": "markup", + "mathml": "markup", + "ssml": "markup", + "atom": "markup", + "rss": "markup", + "js": "javascript", + "g4": "antlr4", + "ino": "arduino", + "arm-asm": "armasm", + "art": "arturo", + "adoc": "asciidoc", + "avs": "avisynth", + "avdl": "avro-idl", + "gawk": "awk", + "sh": "bash", + "shell": "bash", + "shortcode": "bbcode", + "rbnf": "bnf", + "oscript": "bsl", + "cs": "csharp", + "dotnet": "csharp", + "cfc": "cfscript", + "cilk-c": "cilkc", + "cilk-cpp": "cilkcpp", + "cilk": "cilkcpp", + "coffee": "coffeescript", + "conc": "concurnas", + "jinja2": "django", + "dns-zone": "dns-zone-file", + "dockerfile": "docker", + "gv": "dot", + "eta": "ejs", + "xlsx": "excel-formula", + "xls": "excel-formula", + "gamemakerlanguage": "gml", + "po": "gettext", + "gni": "gn", + "ld": "linker-script", + "go-mod": "go-module", + "hbs": "handlebars", + "mustache": "handlebars", + "hs": "haskell", + "idr": "idris", + "gitignore": "ignore", + "hgignore": "ignore", + "npmignore": "ignore", + "webmanifest": "json", + "kt": "kotlin", + "kts": "kotlin", + "kum": "kumir", + "tex": "latex", + "context": "latex", + "ly": "lilypond", + "emacs": "lisp", + "elisp": "lisp", + "emacs-lisp": "lisp", + "md": "markdown", + "moon": "moonscript", + "n4jsd": "n4js", + "nani": "naniscript", + "objc": "objectivec", + "qasm": "openqasm", + "objectpascal": "pascal", + "px": "pcaxis", + "pcode": "peoplecode", + "plantuml": "plant-uml", + "pq": "powerquery", + "mscript": "powerquery", + "pbfasm": "purebasic", + "purs": "purescript", + "py": "python", + "qs": "qsharp", + "rkt": "racket", + "razor": "cshtml", + "rpy": "renpy", + "res": "rescript", + "robot": "robotframework", + "rb": "ruby", + "sh-session": "shell-session", + "shellsession": "shell-session", + "smlnj": "sml", + "sol": "solidity", + "sln": "solution-file", + "rq": "sparql", + "sclang": "supercollider", + "t4": "t4-cs", + "trickle": "tremor", + "troy": "tremor", + "trig": "turtle", + "ts": "typescript", + "tsconfig": "typoscript", + "uscript": "unrealscript", + "uc": "unrealscript", + "url": "uri", + "vb": "visual-basic", + "vba": "visual-basic", + "webidl": "web-idl", + "mathematica": "wolfram", + "nb": "wolfram", + "wl": "wolfram", + "xeoracube": "xeora", + "yml": "yaml" +}.toTable when defined(js): - import std/jsffi const katexJs = staticRead(srcDir / "katex/katex.min.js") @@ -35,9 +237,13 @@ when defined(js): proc compileCivet*(src: string): string = xidocError "Civet compilation is currently not available when using JavaScript (how ironic)" - proc katexRenderToString(math: cstring, opts: JsObject): cstring {.importjs: "katex.renderToString(@)".} + proc katexRenderToString( + math: cstring, opts: JsObject + ): cstring {.importjs: "katex.renderToString(@)".} - proc renderMathKatex*(math: string, displayMode: bool, trust = false, renderer = mrKatexHtml): string = + proc renderMathKatex*( + math: string, displayMode: bool, trust = false, renderer = mrKatexHtml + ): string = if renderer == mrTemml: xidocError "Temml is currently not supported when using JavaScript" var opts = newJsObject() @@ -58,13 +264,12 @@ when defined(js): xidocError "JavaScript evaluation is currently not available when using JavaScript (how ironic)" else: - import pkg/rapidjs import std/sequtils import std/strformat - {.compile: "../katex/katex.c"} - {.compile: "../temml/temml.c"} + {.compile: "../katex/katex.c".} + {.compile: "../temml/temml.c".} let katexBin {.importc: "qjsc_katex_min_ptr".}: ptr uint8 @@ -92,7 +297,9 @@ else: xidocError &"Error while compiling Civet\n{ctx.exceptionMsg}" return res.to(string) - proc renderMathKatex*(math: string, displayMode: bool, trust = false, renderer = mrKatexHtml): string = + proc renderMathKatex*( + math: string, displayMode: bool, trust = false, renderer = mrKatexHtml + ): string = initCtx() case renderer of mrKatexHtml, mrKatexMathml: @@ -103,8 +310,10 @@ else: ctx.evalBin(temmlBin, temmlBinLen) let renderToString = case renderer - of mrKatexHtml, mrKatexMathml: ctx.eval("katex.renderToString") - of mrTemml: ctx.eval("temml.renderToString") + of mrKatexHtml, mrKatexMathml: + ctx.eval("katex.renderToString") + of mrTemml: + ctx.eval("temml.renderToString") let opts = (displayMode: displayMode, trust: trust, throwOnError: true).toJs(ctx) if renderer == mrKatexMathml: opts["output"] = "mathml" @@ -116,10 +325,14 @@ else: proc loadPrismLanguage(name: string) = var loaded {.global.}: seq[string] let name = - if name in prismLanguages: name - elif name in prismLangAliases: prismLangAliases[name] - else: xidocError &"Unknown language for syntax highlighting: {name}" - if name in loaded: return + if name in prismLanguages: + name + elif name in prismLangAliases: + prismLangAliases[name] + else: + xidocError &"Unknown language for syntax highlighting: {name}" + if name in loaded: + return for dep in prismLangDependencies.getOrDefault(name, @[]): loadPrismLanguage(dep) discard ctx.eval(prismLanguages[name]) diff --git a/src/xidocpkg/parser.nim b/src/xidocpkg/parser.nim index 1fa9cc5..58c9865 100644 --- a/src/xidocpkg/parser.nim +++ b/src/xidocpkg/parser.nim @@ -8,6 +8,7 @@ type xnkString xnkWhitespace xnkCommand + XidocNode* = object case kind*: XidocNodeKind of xnkString: @@ -18,6 +19,7 @@ type whole*: StringView name*: StringView arg*: StringView + XidocNodes* = seq[XidocNode] const nonTextChars = Whitespace + {'[', ']'} @@ -36,7 +38,8 @@ proc skipBalancedText(body: ref string, i: var int, stop: int) = if brackets == 0: break brackets.dec - else: discard + else: + discard i.inc proc parseXidocStringHelper(body: ref string, i: var int, stop: int): StringView = @@ -45,7 +48,7 @@ proc parseXidocStringHelper(body: ref string, i: var int, stop: int): StringView let start = i while i <= stop and body[][i] notin nonTextChars: i.inc - body.view(start.. stop: xidocError "Parse error: Unexpected end of file (did you forget to close a bracket?)" - result = XidocNode(kind: xnkCommand, whole: body.view(start..i), name: name, arg: body.view(argStart..".shouldRenderAs("std::vector<std::string&>") suite "\"Basic constructs\" commands": - test "[#]": "I [# don't] like xidoc.".shouldRenderAs("I like xidoc.") @@ -50,50 +47,64 @@ suite "\"Basic constructs\" commands": "[() this-is-not-a-command]".shouldRenderAs("[this-is-not-a-command]") test "[;]": - "[list This[;] is[;] only[;] one[;] item]".shouldRenderAs("") + "[list This[;] is[;] only[;] one[;] item]".shouldRenderAs( + "" + ) test "[space]": "[space]".shouldRenderAs(" ") "[bf [space]x[space]]".shouldRenderAs(" x ") test "[raw]": - "[raw [I can use [as many brackets [as I want]], but [they] still have to [[be balanced]]].]" - .shouldRenderAs("[I can use [as many brackets [as I want]], but [they] still have to [[be balanced]]].") + "[raw [I can use [as many brackets [as I want]], but [they] still have to [[be balanced]]].]".shouldRenderAs( + "[I can use [as many brackets [as I want]], but [they] still have to [[be balanced]]]." + ) test "[raw<]": """[raw< for word in ["This", "correctly", "removes", "indentation"]: - echo word]""" - .shouldRenderAs("for word in ["This", "correctly", "removes", "indentation"]:\n echo word") + echo word]""".shouldRenderAs( + "for word in ["This", "correctly", "removes", "indentation"]:\n echo word" + ) test "[pass]": - "[pass Haha! I'm in! '[;] DROP TABLE xidoc[;] Oh no, this is a static site…]".shouldRenderAs("Haha! I'm in! '; DROP TABLE xidoc; Oh no, this is a static site…") + "[pass Haha! I'm in! '[;] DROP TABLE xidoc[;] Oh no, this is a static site…]".shouldRenderAs( + "Haha! I'm in! '; DROP TABLE xidoc; Oh no, this is a static site…" + ) test "[pass-raw]": - "[pass-raw Haha! I'm in! '[;] DROP TABLE xidoc; Oh no, this is a static site…]".shouldRenderAs("Haha! I'm in! '[;] DROP TABLE xidoc; Oh no, this is a static site…") + "[pass-raw Haha! I'm in! '[;] DROP TABLE xidoc; Oh no, this is a static site…]".shouldRenderAs( + "Haha! I'm in! '[;] DROP TABLE xidoc; Oh no, this is a static site…" + ) test "[hide]": "[hide [def-global x; 0] PWNED][x]".shouldRenderAs("0") test "[render]": - "[render [raw [it This will be italic despite being inside [ms raw].]]]".shouldRenderAs("This will be italic despite being inside raw.") + "[render [raw [it This will be italic despite being inside [ms raw].]]]".shouldRenderAs( + "This will be italic despite being inside raw." + ) test "[add-to-head]": check "[add-to-head BOOM]".renderXidoc.contains("BOOM") suite "\"Inline formatting\" commands": - test "[bf]": "xidoc is [bf awesome]!".shouldRenderAs("xidoc is awesome!") test "[code]": - "[code [raw print(f\"The answer to the universe and stuff is {6 * 7}.\")]]" - .shouldRenderAs("print(f"The answer to the universe and stuff is {6 * 7}.")") - "[code python; [raw print(f\"The answer to the universe and stuff is {6 * 7}.\")]]" - .shouldRenderAs("print(f\"The answer to the universe and stuff is {6 * 7}.\")") + "[code [raw print(f\"The answer to the universe and stuff is {6 * 7}.\")]]".shouldRenderAs( + "print(f"The answer to the universe and stuff is {6 * 7}.")" + ) + + "[code python; [raw print(f\"The answer to the universe and stuff is {6 * 7}.\")]]".shouldRenderAs( + "print(f\"The answer to the universe and stuff is {6 * 7}.\")" + ) test "[color]": - "You can use [color red; names] or [color #00f; codes]!".shouldRenderAs("You can use names or codes!") + "You can use [color red; names] or [color #00f; codes]!".shouldRenderAs( + "You can use names or codes!" + ) test "[it]": "xidoc is [it fantastic]!".shouldRenderAs("xidoc is fantastic!") @@ -102,29 +113,35 @@ suite "\"Inline formatting\" commands": "[\" Hello!] [lang czech; [\" Ahoj!]]".shouldRenderAs("“Hello!” „Ahoj!“") test "[link]": - "[link xidoc; http://xidoc.nim.town/] is made in [link Nim; https://nim-lang.org/]." - .shouldRenderAs("xidoc is made in Nim.") + "[link xidoc; http://xidoc.nim.town/] is made in [link Nim; https://nim-lang.org/].".shouldRenderAs( + "xidoc is made in Nim." + ) test "[ms]": - "In HTML, this will produce [ms ]. In LaTeX, this will produce [ms \\texttt]." - .shouldRenderAs("In HTML, this will produce <code>. In LaTeX, this will produce \\texttt.") + "In HTML, this will produce [ms ]. In LaTeX, this will produce [ms \\texttt].".shouldRenderAs( + "In HTML, this will produce <code>. In LaTeX, this will produce \\texttt." + ) test "[term]": - "A [term group] is a monoid where every element has an inverse." - .shouldRenderAs("A group is a monoid where every element has an inverse.") + "A [term group] is a monoid where every element has an inverse.".shouldRenderAs( + "A group is a monoid where every element has an inverse." + ) test "[unit]": - "The radius of the Earth is [unit 6378; km].".shouldRenderAs("The radius of the Earth is 6378 km.") + "The radius of the Earth is [unit 6378; km].".shouldRenderAs( + "The radius of the Earth is 6378 km." + ) suite "\"Block formatting\" commands": - test "[block-quote]": - "[p The first rule in the Zen of Nim is:] [block-quote Copying bad design is not good design.]" - .shouldRenderAs("

The first rule in the Zen of Nim is:

Copying bad design is not good design.
") + "[p The first rule in the Zen of Nim is:] [block-quote Copying bad design is not good design.]".shouldRenderAs( + "

The first rule in the Zen of Nim is:

Copying bad design is not good design.
" + ) test "[checkboxes]": - "[checkboxes [v Kill the friend] [- Bury the body] [x Get caught by the police]]" - .shouldRenderAs("
  • Kill the friend
  • Bury the body
  • Get caught by the police
") + "[checkboxes [v Kill the friend] [- Bury the body] [x Get caught by the police]]".shouldRenderAs( + "
  • Kill the friend
  • Bury the body
  • Get caught by the police
" + ) test "[code-block]": """[code-block [raw< @@ -135,65 +152,85 @@ suite "\"Block formatting\" commands": } return result; } - ]]""" - .shouldRenderAs("""
const factorial = (n) => {
+    ]]""".shouldRenderAs(
+      """
const factorial = (n) => {
   let result = 1n;
   for (let i = 1; i <= n; i++) {
     result *= BigInt(i);
   }
   return result;
 }
-
""") +
""" + ) test "[figure]": "[figure IMAGE]".shouldRenderAs("
IMAGE
") - "[figure IMAGE; CAPTION]".shouldRenderAs("
IMAGE
CAPTION
") + "[figure IMAGE; CAPTION]".shouldRenderAs( + "
IMAGE
CAPTION
" + ) test "[lines]": - "[lines Roses are red; Violets are blue; Java is bad; JavaScript too]" - .shouldRenderAs("Roses are red
Violets are blue
Java is bad
JavaScript too") + "[lines Roses are red; Violets are blue; Java is bad; JavaScript too]".shouldRenderAs( + "Roses are red
Violets are blue
Java is bad
JavaScript too" + ) "[lines one line]".shouldRenderAs("one line") "[lines]".shouldRenderAs("") test "[link-image]": - "[link-image xidoc logo; logo.svg]".shouldRenderAs("\"xidoc") - "[link-image xidoc logo; logo.svg; https://xidoc.nim.town/]".shouldRenderAs("\"xidoc") + "[link-image xidoc logo; logo.svg]".shouldRenderAs( + "\"xidoc" + ) + "[link-image xidoc logo; logo.svg; https://xidoc.nim.town/]".shouldRenderAs( + "\"xidoc" + ) test "[list]": - "Supported targets: [list HTML; LaTeX; Gemtext]" - .shouldRenderAs("Supported targets:
  • HTML
  • LaTeX
  • Gemtext
") + "Supported targets: [list HTML; LaTeX; Gemtext]".shouldRenderAs( + "Supported targets:
  • HTML
  • LaTeX
  • Gemtext
" + ) test "[ordered-list]": - "TOP 5 LIST OF SMALLEST POSITIVE INTEGERS: [ordered-list 1; 2; 3; 4; 5]" - .shouldRenderAs("TOP 5 LIST OF SMALLEST POSITIVE INTEGERS:
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
") + "TOP 5 LIST OF SMALLEST POSITIVE INTEGERS: [ordered-list 1; 2; 3; 4; 5]".shouldRenderAs( + "TOP 5 LIST OF SMALLEST POSITIVE INTEGERS:
  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
" + ) test "[description-list]": - "[description-list Dog; Cute and loyal; Cat; Cute and entitled]" - .shouldRenderAs("
Dog
Cute and loyal
Cat
Cute and entitled
") + "[description-list Dog; Cute and loyal; Cat; Cute and entitled]".shouldRenderAs( + "
Dog
Cute and loyal
Cat
Cute and entitled
" + ) test "[p]": "[p PARAGRAPH]".shouldRenderAs("

PARAGRAPH

") test "[section]": - "[section Are we going too deep?]".shouldRenderAs("
Are we going too deep?
") - "[section Inception; Are we going too deep?]".shouldRenderAs("

Inception

Are we going too deep?
") - "[section Inception; inception; Are we going too deep?]".shouldRenderAs("

Inception

Are we going too deep?
") - "[section [section Inception; Are we going too deep?]]".shouldRenderAs("

Inception

Are we going too deep?
") + "[section Are we going too deep?]".shouldRenderAs( + "
Are we going too deep?
" + ) + "[section Inception; Are we going too deep?]".shouldRenderAs( + "

Inception

Are we going too deep?
" + ) + "[section Inception; inception; Are we going too deep?]".shouldRenderAs( + "

Inception

Are we going too deep?
" + ) + "[section [section Inception; Are we going too deep?]]".shouldRenderAs( + "

Inception

Are we going too deep?
" + ) test "[spoiler]": - "[spoiler In the series The Simpsons, the surname of the main characters is; Simpson]" - .shouldRenderAs("
In the series The Simpsons, the surname of the main characters isSimpson
") + "[spoiler In the series The Simpsons, the surname of the main characters is; Simpson]".shouldRenderAs( + "
In the series The Simpsons, the surname of the main characters isSimpson
" + ) test "[table], [row], [header-row]": - "[table [header-row a; b; c][row d; e; f]]" - .shouldRenderAs("
abc
def
") + "[table [header-row a; b; c][row d; e; f]]".shouldRenderAs( + "
abc
def
" + ) test "[title]": "[title TITLE]".shouldRenderAs("

TITLE

") check renderXidoc("[title TITLE]").contains("TITLE") suite "\"Unicode characters\" commands": - test "[\"]": "[\" Hello!]".shouldRenderAs("“Hello!”") "[lang czech; [\" Ahoj!]]".shouldRenderAs("„Ahoj!“") @@ -208,20 +245,22 @@ suite "\"Unicode characters\" commands": "[...]".shouldRenderAs("…") suite "\"Logos\" commands": - test "[LaTeX]": - "[LaTeX]".shouldRenderAs("LaTeX") + "[LaTeX]".shouldRenderAs( + "LaTeX" + ) test "[xidoc]": "[xidoc]".shouldRenderAs("ξ") suite "Custom commands": - test "no parameters": "[def foo; bar][foo]".shouldRenderAs("bar") test "parameters": - "[def greet; name; Hello, [arg name]!][greet reader]".shouldRenderAs("Hello, reader!") + "[def greet; name; Hello, [arg name]!][greet reader]".shouldRenderAs( + "Hello, reader!" + ) "[def greet; person; Hello, [arg name]!][greet reader]".shouldError test "lexical scoping": @@ -230,10 +269,10 @@ suite "Custom commands": "[() [def-global a; x]][a]".shouldRenderAs("[]x") suite "\"Target detection\" commands": - test "[if-html]": - "[if-html You can see this only if you're in HTML]" - .shouldRenderAs("You can see this only if you're in HTML") + "[if-html You can see this only if you're in HTML]".shouldRenderAs( + "You can see this only if you're in HTML" + ) test "[if-latex]": "[if-latex You can see this only if you're in LaTeX]".shouldRenderAs("") @@ -242,44 +281,52 @@ suite "\"Target detection\" commands": "[if-gemtext You can see this only if you're in Gemtext]".shouldRenderAs("") suite "\"List manipulation\" commands": - test "[for-each], [join]": - "[join [space]; [for-each lang; HTML LaTeX Gemtext; xidoc compiles to [lang].]]" - .shouldRenderAs("xidoc compiles to HTML. xidoc compiles to LaTeX. xidoc compiles to Gemtext.") + "[join [space]; [for-each lang; HTML LaTeX Gemtext; xidoc compiles to [lang].]]".shouldRenderAs( + "xidoc compiles to HTML. xidoc compiles to LaTeX. xidoc compiles to Gemtext." + ) test "[join], [split]": "[join [space]; [split *; I*HATE*ASTERISKS]]".shouldRenderAs("I HATE ASTERISKS") suite "\"Programming\" commands": - test "[janet-call]": - "[janet-call [raw (fn [radius] (describe (* 2 math/pi (scan-number radius))))]; 6]" - .shouldRenderAs("37.6991") + "[janet-call [raw (fn [radius] (describe (* 2 math/pi (scan-number radius))))]; 6]".shouldRenderAs( + "37.6991" + ) test "[janet-eval]": - "[janet-eval [raw (do (defn gcd [a b] (if (= b 0) a (gcd b (% a b)))) (describe (gcd (scan-number a) (scan-number b)))) ]; a;128 ; b;168]" - .shouldRenderAs("8") + "[janet-eval [raw (do (defn gcd [a b] (if (= b 0) a (gcd b (% a b)))) (describe (gcd (scan-number a) (scan-number b)))) ]; a;128 ; b;168]".shouldRenderAs( + "8" + ) test "[js-call]": - "[js-call [raw (radius) => Math.PI * radius * radius]; 6]".shouldRenderAs("113.09733552923255") + "[js-call [raw (radius) => Math.PI * radius * radius]; 6]".shouldRenderAs( + "113.09733552923255" + ) test "[js-eval]": - "[js-eval [raw { let discriminant = b*b - 4*a*c; let root1 = (-b + Math.sqrt(discriminant)) / (2 * a); let root2 = (-b - Math.sqrt(discriminant)) / (2 * a); root1 + \", \" + root2 }]; a;3 ; b;-18 ; c;24]" - .shouldRenderAs("4, 2") + "[js-eval [raw { let discriminant = b*b - 4*a*c; let root1 = (-b + Math.sqrt(discriminant)) / (2 * a); let root2 = (-b - Math.sqrt(discriminant)) / (2 * a); root1 + \", \" + root2 }]; a;3 ; b;-18 ; c;24]".shouldRenderAs( + "4, 2" + ) suite "\"Drawing\" commands": - test "[pikchr]": - "[pikchr box]".shouldRenderAs("\n\n\n") - "[pikchr 1rem; box]".shouldRenderAs("\n\n\n") + "[pikchr box]".shouldRenderAs( + "\n\n\n" + ) + "[pikchr 1rem; box]".shouldRenderAs( + "\n\n\n" + ) "[pikchr Pikachu]".shouldError test "[pikchr-raw]": - "[pikchr-raw box]".shouldRenderAs("\n\n\n") + "[pikchr-raw box]".shouldRenderAs( + "\n\n\n" + ) "[pikchr-raw Pikachu]".shouldError suite "HTML commands": - test "empty body": "[
]".shouldRenderAs("
") @@ -287,9 +334,9 @@ suite "HTML commands": "[ bold]".shouldRenderAs("bold") test "one argument": - "[ href=\"link\"; CLICK HERE]" - .shouldRenderAs("CLICK HERE") + "[ href=\"link\"; CLICK HERE]".shouldRenderAs("CLICK HERE") test "multiple arguments, classes": - "[ .click-here; href=\"link\"; CLICK HERE]" - .shouldRenderAs("CLICK HERE") + "[ .click-here; href=\"link\"; CLICK HERE]".shouldRenderAs( + "CLICK HERE" + ) diff --git a/tests/test_parser.nim b/tests/test_parser.nim index baa5ea5..5579442 100644 --- a/tests/test_parser.nim +++ b/tests/test_parser.nim @@ -22,10 +22,10 @@ func `==`(a: seq[StringView], b: seq[string]): bool = a.mapIt($it) == b suite "basic syntax": - test "string": check parseXidoc("abc") == @[XidocNode(kind: xnkString, str: "abc")] - check parseXidoc("áβç/\\;}┐") == @[XidocNode(kind: xnkString, str: "áβç/\\;}┐")] + check parseXidoc("áβç/\\;}┐") == + @[XidocNode(kind: xnkString, str: "áβç/\\;}┐")] test "whitespace without newline": check parseXidoc(" ") == @[XidocNode(kind: xnkWhitespace, newline: false)] @@ -34,36 +34,44 @@ suite "basic syntax": test "whitespace with newline": check parseXidoc("\n") == @[XidocNode(kind: xnkWhitespace, newline: true)] - check parseXidoc(" \n\t \t\n\t") == @[XidocNode(kind: xnkWhitespace, newline: true)] + check parseXidoc(" \n\t \t\n\t") == @[ + XidocNode(kind: xnkWhitespace, newline: true) + ] test "command": check parseXidoc("[foo]") == @[XidocNode(kind: xnkCommand, name: "foo", arg: "")] - check parseXidoc("[bar baz]") == @[XidocNode(kind: xnkCommand, name: "bar", arg: " baz")] + check parseXidoc("[bar baz]") == + @[XidocNode(kind: xnkCommand, name: "bar", arg: " baz")] test "combined": - check parseXidoc("q [uu]x") == @[ - XidocNode(kind: xnkString, str: "q"), - XidocNode(kind: xnkWhitespace, newline: false), - XidocNode(kind: xnkCommand, name: "uu", arg: ""), - XidocNode(kind: xnkString, str: "x"), - ] + check parseXidoc("q [uu]x") == + @[ + XidocNode(kind: xnkString, str: "q"), + XidocNode(kind: xnkWhitespace, newline: false), + XidocNode(kind: xnkCommand, name: "uu", arg: ""), + XidocNode(kind: xnkString, str: "x") + ] suite "basic syntax errors": - test "too many left brackets": - expect XidocError: discard parseXidoc("[a") - expect XidocError: discard parseXidoc("[this [is [very] nested]") + expect XidocError: + discard parseXidoc("[a") + expect XidocError: + discard parseXidoc("[this [is [very] nested]") test "too many right brackets": - expect XidocError: discard parseXidoc("a]") - expect XidocError: discard parseXidoc("this [is [very] nested]]") + expect XidocError: + discard parseXidoc("a]") + expect XidocError: + discard parseXidoc("this [is [very] nested]]") test "invalid command name": - expect XidocError: discard parseXidoc("[[a]]") - expect XidocError: discard parseXidoc("[i-love[] you]") + expect XidocError: + discard parseXidoc("[[a]]") + expect XidocError: + discard parseXidoc("[i-love[] you]") suite "argument syntax": - test "zero arguments": check parseXidocArguments("") == newSeq[string]() -- 2.45.2