Skip to content

Commit

Permalink
Merge pull request #94 from Omikhleia/small-fixes
Browse files Browse the repository at this point in the history
Small fixes
  • Loading branch information
Omikhleia authored Sep 2, 2023
2 parents f13ff0a + d9a3ff5 commit ead5e86
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 36 deletions.
71 changes: 55 additions & 16 deletions inputters/djot.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
-- @copyright License: MIT (c) 2023 Omikhleia
-- @module inputters.djot
--
local utils = require("packages.markdown.utils")
local ast = require("silex.ast")
local createCommand, createStructuredCommand
= ast.createCommand, ast.createStructuredCommand
Expand All @@ -19,8 +20,8 @@ local Renderer = pl.class()
function Renderer:_init(options)
self.references = {}
self.footnotes = {}
self.metadata = {}
self.shift_headings = SU.cast("integer", options.shift_headings or 0)
self.metadata = {}
for key, val in pairs(options) do
local meta = key:match("^meta:(.*)")
if meta then
Expand Down Expand Up @@ -60,7 +61,19 @@ function Renderer:render_children(node)
self.tight = node.tight
end
for i=1,#node.c do
out[#out+1] = self[node.c[i].t](self, node.c[i])
local content = self[node.c[i].t](self, node.c[i])
-- Simplify outputs by collating strings
if type(content) == "string" and type(out[#out]) == "string" then
out[#out] = out[#out] .. content
else
-- Simplify out by removing empty elements
if type(content) ~= "table" or content.command or #content > 1 then
out[#out+1] = content
elseif #content == 1 then
-- Simplify out by removing useless grouping
out[#out+1] = content[1]
end
end
end
if node.tight ~= nil then
self.tight = oldtight
Expand Down Expand Up @@ -161,6 +174,19 @@ end
function Renderer:code_block (node)
local options = node.attr or {}
options.class = node.lang and ((options.class and (options.class.." ") or "") .. node.lang) or options.class
if utils.hasClass(options, "djot") or utils.hasClass(options, "markdown") then
options.shift_headings = self.shift_headings
self:resolveAllUserDefinedSymbols()
-- Parent metadata just have a label xxx
-- User-defined memoized metadata have the symbol (:xxx:) as key
-- So we sort the keys to get the user-defined metadata first as overrides.
for key, val in SU.sortedpairs(self.metadata) do
local name = key:match("^:([%w_+-]+):$") or key -- remove leading and trailing colons
if not options["meta:" .. name] then
options["meta:" .. name] = val
end
end
end
return createCommand("markdown:internal:codeblock", options, node.s, self:render_pos(node))
end

Expand Down Expand Up @@ -351,7 +377,7 @@ function Renderer.softbreak (_)
end

function Renderer.hardbreak (_)
return createCommand("cr")
return createCommand("markdown:internal:hardbreak")
end

function Renderer:nbsp (node)
Expand Down Expand Up @@ -576,6 +602,31 @@ local predefinedSymbols = {
},
}

function Renderer:getUserDefinedSymbol (label, node_fake_metadata)
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
return content
end

function Renderer:resolveAllUserDefinedSymbols ()
-- Ensure all fake footnotes are rendered and memoized, even unused ones.
for label, node_fake_metadata in pairs(self.footnotes) do
if label:match("^:([%w_+-]+):$") then
self:getUserDefinedSymbol(label, node_fake_metadata)
end
end
end

function Renderer:symbol (node)
-- Let's first look at fake footnotes to resolve the symbol.
-- We just added unforeseen templating and recursive variable substitution to Djot.
Expand All @@ -586,19 +637,7 @@ function Renderer:symbol (node)
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
local content = self:getUserDefinedSymbol(label, node_fake_metadata)
if node.attr then
if not node._standalone_ then
-- Add a span for attributes on the inline variant.
Expand Down
19 changes: 18 additions & 1 deletion inputters/markdown.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ local function SileAstWriter (writerOps, renderOps)
local generic = require("lunamark.writer.generic")
local writer = generic.new(writerOps or {})
local shift_headings = SU.cast("integer", renderOps.shift_headings or 0)
local parentmetadata = {}
for key, val in pairs(renderOps) do
local meta = key:match("^meta:(.*)")
if meta then
if meta:match("[%w_+-]+") then
-- We don't use them in this renderer, but we can pass them through
-- to embedded djot documents.
parentmetadata[key] = val
else
SU.warn("Invalid metadata key is skipped: "..meta)
end
end
end

-- Simple one-to-one mappings between lunamark AST and SILE

Expand All @@ -86,7 +99,7 @@ local function SileAstWriter (writerOps, renderOps)
writer.blockquote = simpleCommandWrapper("markdown:internal:blockquote")
writer.verbatim = simpleCommandWrapper("verbatim")
writer.listitem = simpleCommandWrapper("item")
writer.linebreak = simpleCommandWrapper("cr")
writer.linebreak = simpleCommandWrapper("markdown:internal:hardbreak")
writer.singlequoted = simpleCommandWrapper("singlequoted")
writer.doublequoted = simpleCommandWrapper("doublequoted")

Expand Down Expand Up @@ -148,6 +161,10 @@ local function SileAstWriter (writerOps, renderOps)

writer.fenced_code = function (content, infostring, attr)
local opts = attr or { class = infostring }
if utils.hasClass(opts, "djot") or utils.hasClass(opts, "markdown") then
opts = pl.tablex.union(parentmetadata, opts)
opts.shift_headings = shift_headings
end
return createCommand("markdown:internal:codeblock", opts, content)
end

Expand Down
25 changes: 21 additions & 4 deletions inputters/pandocast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ local Renderer = pl.class()

function Renderer:_init(options)
self.shift_headings = SU.cast("integer", options.shift_headings or 0)
self.parentmetadata = {}
for key, val in pairs(options) do
local meta = key:match("^meta:(.*)")
if meta then
if meta:match("[%w_+-]+") then
-- We don't use them in this renderer, but we can pass them through
-- to embedded djot documents.
self.parentmetadata[key] = val
else
SU.warn("Invalid metadata key is skipped: "..meta)
end
end
end
end

-- Allows unpacking tables on some Pandoc AST nodes so as to map them to methods
Expand Down Expand Up @@ -77,8 +90,8 @@ local function addNodes(out, elements)
out[#out] = out[#out] .. elements
else
-- Simplify out by removing empty elements
if type(elements) ~= "table" or elements.command or #elements > -1 then
out [#out+1] = elements
if type(elements) ~= "table" or elements.command or #elements > 0 then
out[#out+1] = elements
end
end
end
Expand Down Expand Up @@ -202,8 +215,12 @@ end

-- CodeBlock Attr Text
-- Code block (literal) with attributes
function Renderer.CodeBlock (_, attributes, text)
function Renderer:CodeBlock (attributes, text)
local options = pandocAttributes(attributes)
if utils.hasClass(options, "djot") or utils.hasClass(options, "markdown") then
options = pl.tablex.union(self.parentmetadata, options)
options.shift_headings = self.shift_headings
end
return createCommand("markdown:internal:codeblock", options, text)
end

Expand Down Expand Up @@ -481,7 +498,7 @@ end
-- LineBreak
-- Hard line break
function Renderer.LineBreak (_)
return createCommand("cr")
return createCommand("markdown:internal:hardbreak")
end

-- Math MathType Text
Expand Down
40 changes: 25 additions & 15 deletions packages/markdown/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
--
require("silex.lang")
local utils = require("packages.markdown.utils")
local hasClass = utils.hasClass
local ast = require("silex.ast")
local createCommand, createStructuredCommand,
extractFromTree, subContent
Expand Down Expand Up @@ -60,14 +61,6 @@ local function getSectioningCommand (level)
return "markdown:fallback:header"
end

local function hasClass (options, classname)
-- N.B. we want a true boolean here
if options.class and string.match(' ' .. options.class .. ' ',' '..classname..' ') then
return true
end
return false
end

local function hasLinkContent(tree)
if type(tree) == "table" then
return #tree > 1 or hasLinkContent(tree[1])
Expand Down Expand Up @@ -242,18 +235,18 @@ function package:registerCommands ()
elseif hasClass(options, "bigrule") then
SILE.call("center", {}, function ()
SILE.call("raise", { height = "0.5ex" }, function ()
SILE.call("hrule", { width = "33%lw" })
SILE.call("hrule", { width = "33%lw", height = "0.4pt" })
end)
end)
elseif hasClass(options, "fullrule") and self:hasCouyards() then
SILE.call("fullrule")
SILE.call("fullrule", { thickness = "0.4pt" })
elseif hasClass(options, "pendant") and self:hasCouyards() then
SILE.call("smallskip")
SILE.call("couyard", { type = 6, width = "default" })
elseif not hasClass(options, "none") then
SILE.call("center", {}, function ()
SILE.call("raise", { height = "0.5ex" }, function ()
SILE.call("hrule", { width = "20%lw" })
SILE.call("hrule", { width = "20%lw", height = "0.4pt" })
end)
end)
end
Expand All @@ -271,13 +264,13 @@ function package:registerCommands ()
elseif options.separator == "---" then
SILE.call("center", {}, function ()
SILE.call("raise", { height = "0.5ex" }, function ()
SILE.call("hrule", { width = "20%lw" })
SILE.call("hrule", { width = "20%lw", height = "0.4pt" })
end)
end)
elseif options.separator == "----" then
SILE.call("center", {}, function ()
SILE.call("raise", { height = "0.5ex" }, function ()
SILE.call("hrule", { width = "33%lw" })
SILE.call("hrule", { width = "33%lw", height = "0.4pt" })
end)
end)
elseif options.separator == "- - - -" and self:hasCouyards() then
Expand All @@ -286,7 +279,7 @@ function package:registerCommands ()
elseif options.separator == "--------------" then -- Page break
SILE.call("eject")
else
SILE.call("fullrule")
SILE.call("fullrule", { thickness = "0.4pt" })
end
end, "Horizontal rule in Markdown (internal)")

Expand Down Expand Up @@ -512,13 +505,30 @@ Please consider using a resilient-compatible class!]])
SILE.processString(rawtext, "lua")
elseif format == "html" then
if rawtext:match("^<br[>/%s]") then
SILE.call("cr")
SILE.call("markdown:internal:hardbreak")
elseif rawtext:match("^<wbr[>/%s]") then
SILE.call("penalty", { penalty = 100 })
end
end
end, "Raw native inline content in Markdown (internal)")

self:registerCommand("markdown:internal:hardbreak", function (_, _)
-- We don't want to use a cr here, because it would affect parindents,
-- insert a parskip, and maybe other things.
-- It's a bit tricky to handle a hardbreak depending on depending on the
-- alignment of the paragraph:
-- justified = we can't use a break, a cr (hfill+break) would work
-- ragged left = we can't use a cr
-- centered = we can't use a cr
-- ragged right = we don't care, a break is sufficient
-- Knowning the alignment is not obvious, neither guessing it from the skips.
-- Using a parfillskip seems to do the trick, but it's maybe a bit hacky.
-- This is nevertheless what would have occurred with a par in the same
-- situation.
SILE.typesetter:pushGlue(SILE.settings:get("typesetter.parfillskip"))
SILE.call("break")
end, "Hard break in Markdown (internal)")

self:registerCommand("markdown:internal:rawblock", function (options, content)
local format = SU.required(options, "format", "rawcontent")
if format == "sile" or format == "sile-lua" then
Expand Down
9 changes: 9 additions & 0 deletions packages/markdown/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,17 @@ local function nbspFilter (str)
return #t == 1 and t[1] or t
end

local function hasClass (options, classname)
-- N.B. we want a true boolean here
if options.class and string.match(' ' .. options.class .. ' ',' '..classname..' ') then
return true
end
return false
end

--- @export
return {
getFileExtension = getFileExtension,
nbspFilter = nbspFilter,
hasClass = hasClass
}

0 comments on commit ead5e86

Please sign in to comment.