From 4d2025091666a21b37c96a1a5f402ae1bbfd4ed3 Mon Sep 17 00:00:00 2001 From: Omikhleia Date: Thu, 27 Jul 2023 22:26:03 +0200 Subject: [PATCH] feat: Add predefined symbols and external metadata passing in Djot --- examples/sile-and-djot.dj | 28 ++++++ examples/sile-and-markdown-manual.sil | 2 +- inputters/djot.lua | 118 +++++++++++++++++++------- 3 files changed, 116 insertions(+), 32 deletions(-) diff --git a/examples/sile-and-djot.dj b/examples/sile-and-djot.dj index 1b2deaa..3db30a1 100644 --- a/examples/sile-and-djot.dj +++ b/examples/sile-and-djot.dj @@ -324,6 +324,8 @@ or "[1984 années]{ .decimal lang=fr }" in French, with appropriate separators. The `.nobreak` pseudo-class attribute on inline content ensures that line-breaking will not be applied there. Use it wisely around small pieces of text or you might end up with more serious justification issues! Yet, it might be useful for proper names, etc. +### Symbols as Djot extension + ### Templates and variable substitution [^:pumpernickel:]: Herbert _"Froggie"_ Pumpernickel{.smallcaps .nobreak} @@ -364,6 +366,27 @@ Of course, these pseudo-footnotes[^djot-pseudo-footnotes] can in turn contain sy [^djot-pseudo-footnotes]: You may still use them as regular footnotes. Whether this is a good idea is another question... +### Predefined symbols + +This renderer also comes with a few symbols predefined. + +- `:_TOC_:` must stand alone in its own paragraph. It inserts a table of contents. + Attributes on the symbol are passed through, e.g. `:_TOC_:{depth=3}`. +- `:U+xxxx:` (where `xxxx` is a hexadecimal value in upper case) is replaced by the corresponding Unicode character --- `:U+2122:` gives :U+2122:. + +The above variable substitution mechanism has precedence over these symbols, allowing you to possibly override them. + +{#djot-metadata-symbols} +### Templates and contextual metadata + +Some options passed to the renderer (see "[](#djot-configuration){.title}") are also available as symbols. + +The calling context (a wrapper document in SIL syntax, a resilient "master document", etc.) is responsible for defining the expected symbols. +Here, if this very document was processed from its expected master document, then `:title:` is available. +Let's check how it expands: _:title:._ + +Again, the variable substitution mechanism also has precedence over these symbols. + ### Attributed quotes (epigraphs) { rule="0.4pt" } @@ -388,6 +411,7 @@ Any option supported by the `\autodoc:package{resilient.epigraph}`{=sile} packag Be aware that this behavior is currently an extension. Other Djot renderers will therefore likely skip the caption. +{#djot-configuration} ### Configuration You can pass additional options to the `\autodoc:command{\include}`{=sile} command or the `\autodoc:environment[check=false]{raw}`{=sile} environment to tune the behavior of the renderer. @@ -404,3 +428,7 @@ For document classes supporting it, this feature also allows you to access level ``` \include[src=somefile.dj, shift_headings=-1] ``` + +Any other option starting with `meta:` is passed to the renderer (with this prefix removed). +See "[](#djot-metadata-symbols){.title}" above. + diff --git a/examples/sile-and-markdown-manual.sil b/examples/sile-and-markdown-manual.sil index 6b0a883..1d0b7b5 100644 --- a/examples/sile-and-markdown-manual.sil +++ b/examples/sile-and-markdown-manual.sil @@ -147,7 +147,7 @@ our package might not support them well. Its main focus is on Markdown parity wi \include[src=pandoc-tables.pandoc] -\include[src=sile-and-djot.dj] +\include[src=sile-and-djot.dj, meta:title=Markdown to PDF with SILE] \include[src=sile-and-pandoc.dj] diff --git a/inputters/djot.lua b/inputters/djot.lua index 064792a..1dc53c7 100644 --- a/inputters/djot.lua +++ b/inputters/djot.lua @@ -19,6 +19,16 @@ function Renderer:_init(options) self.footnotes = {} self.metadata = {} self.shift_headings = SU.cast("integer", options.shift_headings or 0) + for key, val in pairs(options) do + local meta = key:match("^meta:(.*)") + if meta then + if meta:match("[%w_+-]+") then + self.metadata[meta] = val + else + SU.warn("Invalid metadata key is skipped: "..meta) + end + end + end self.tight = false -- We do not use it currently, though! end @@ -524,15 +534,88 @@ function Renderer.en_dash (_) return("–") end +local predefinedSymbols = { + _TOC_ = { + standalone = true, + render = function (node) + return utils.createCommand("tableofcontents", node.attr) + end + }, + _FANCYTOC_ = { -- of course, requires having installed the fancytoc.sile module + -- We are not going to check that here, so I won't document it. + standalone = true, + render = function (node) + return { + utils.createCommand("use", { module = "packages.fancytoc" }), + utils.createCommand("fancytableofcontents", node.attr) + } + end + }, +} + function Renderer:symbol (node) - -- Let's look at fake footnotes to resolve the symbol. + -- Let's first look at fake footnotes to resolve the symbol. -- We just added unforeseen templating and recursive variable substitution to Djot. local label = ":" .. node.alias .. ":" local node_fake_metadata = self.footnotes[label] - if not node_fake_metadata then + + if node_fake_metadata then + if #node_fake_metadata.c > 1 and not node._standalone_ then + SU.error("Cannot use multi-paragraph metatada "..label.." as inline content") + end + + local content + if self.metadata[label] then -- use memoized + content = self.metadata[label] + else + if #node_fake_metadata.c == 1 and node_fake_metadata.c[1].t == "para" then + -- Skip a single para node. + content = self:render_children(node_fake_metadata.c[1]) + else + content = self:render_children(node_fake_metadata) + end + self.metadata[label] = content -- memoize + end + if node.attr then + if not node._standalone_ then + -- Add a span for attributes on the inline variant. + content = utils.createCommand("markdown:internal:span" , node.attr, content) + else + -- Those should rather come from the paragraph + SU.warn("Attributes ignored on block-like symbol '" .. node.alias .. "'") + end + end + return content + else + -- Let's then look for metadata passed to the renderer + if self.metadata[node.alias] then + local text = self.metadata[node.alias] + if node.attr then + -- Add a span for attributes + return utils.createCommand("markdown:internal:span" , node.attr, text) + end + return text + end + -- Let's finally look for predefined symbols + local symbol = predefinedSymbols[node.alias] + if symbol then + if symbol.standalone and not node._standalone_ then + SU.error("Cannot use " .. label .." as inline content") + end + return symbol.render(node) + end + if node.alias:match("U%+[0-9A-F]+") then + local content = { + utils.createCommand("use", { module = "packages.unichar" }), + utils.createCommand("unichar", {}, node.alias) + } + if node.attr then + -- Add a span for attributes + return utils.createCommand("markdown:internal:span" , node.attr, content) + end + return content + end SU.warn("Symbol '" .. node.alias .. "' was not expanded (no corresponding metadata found)") - -- TODO Let's not error, but what else could we do with these funny symbols? - -- Oh oh oh... Don't get me started... Nah you are not ready yet for more tricks... local text = ":" .. node.alias .. ":" if node.attr then -- Add a span for attributes @@ -540,33 +623,6 @@ function Renderer:symbol (node) end return text end - - if #node_fake_metadata.c > 1 and not node._standalone_ then - SU.error("Cannot use multi-paragraph metatada "..label.." as inline content") - end - - local content - if self.metadata[label] then -- use memoized - content = self.metadata[label] - else - if #node_fake_metadata.c == 1 and node_fake_metadata.c[1].t == "para" then - -- Skip a single para node. - content = self:render_children(node_fake_metadata.c[1]) - else - content = self:render_children(node_fake_metadata) - end - self.metadata[label] = content -- memoize - end - if node.attr then - if not node._standalone_ then - -- Add a span for attributes on the inline variant. - content = utils.createCommand("markdown:internal:span" , node.attr, content) - else - -- Those should rather come from the paragraph - SU.warn("Attributes ignored on block-like symbol '" .. node.alias .. "'") - end - end - return content end function Renderer.math (_, node)