Skip to content

Commit

Permalink
Merge pull request #87 from Omikhleia/more-symbol-extensions
Browse files Browse the repository at this point in the history
feat: Add predefined symbols and external metadata passing in Djot
  • Loading branch information
Omikhleia authored Jul 27, 2023
2 parents feea326 + 4d20250 commit 40b4e6e
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 32 deletions.
28 changes: 28 additions & 0 deletions examples/sile-and-djot.dj
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -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" }
Expand All @@ -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.
Expand All @@ -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.

2 changes: 1 addition & 1 deletion examples/sile-and-markdown-manual.sil
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
118 changes: 87 additions & 31 deletions inputters/djot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -524,49 +534,95 @@ 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
return utils.createCommand("markdown:internal:span" , node.attr, text)
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)
Expand Down

0 comments on commit 40b4e6e

Please sign in to comment.