– Re:VIEW Writer for Pandoc – Copyright 2020 Kenshi Muto
– config local config = {
use_header_id = "true", use_hr = "true", use_table_align = "true", bold = "b", italic = "i", code = "tt", strike = "del", underline = "u", lineblock = "source", --- XXX: Re:VIEW doesn't provide poem style by default
}
– counter local table_num = 0 local list_num = 0 local fig_num = 0 local note_num = 0 local footnotes = {}
– internal local metadata = nil local stringify = (require “pandoc.utils”).stringify local inline_commands = {
-- processed if given as classes of Span elements -- true if syntax is `@<command>{string}` --- formats kw = true, bou = true, ami = true, u = true, b = true, i = true, strong = true, em = true, tt = true, tti = true, ttb = true, code = true, tcy = true, --- ref chap = true, title = true, chapref = true, list = true, img = true, table = true, eq = true, hd = true, column = true, --- others ruby = false, br = false, uchar = true, href = false, icon = true, m = true, w = true, wb = true, raw = false, embed = false, idx = true, hidx = true, balloon = true,
}
local function try_catch(what)
-- ref: http://bushimichi.blogspot.com/2016/11/lua-try-catch.html local status, result = pcall(what.try) if not status then what.catch(result) end return result
end
local function log(s)
io.stderr:write(s)
end
local function surround_inline(s)
if (string.match(s, "{") or string.match(s, "}")) then if (string.match(s, "%$")) then -- use % for regexp escape if (string.match(s, "|")) then -- give up. escape } by \} return "{" .. string.gsub(s, "}", "\\}") .. "}" else -- surround by || return "|" .. s .. "|" end else -- surround by $$ return "$" .. s .. "$" end end return "{" .. s .. "}"
end
local function format_inline(fmt, s)
return string.format("@<%s>%s", fmt, surround_inline(s))
end
local function html_align(align)
if align == "AlignLeft" then return "" elseif align == "AlignRight" then return "right" elseif align == "AlignCenter" then return "center" else return "" end
end
function Blocksep()
return "\n\n"
end
function Doc(body, metadata, variables)
local buffer = {} local function add(s) table.insert(buffer, s) end add(body) if (#footnotes > 0) then add("\n" .. table.concat(footnotes, "\n")) end return table.concat(buffer, "\n")
end
function Str(s)
return s
end
function Space()
return " "
end
function LineBreak()
return "@<br>{}"
end
function SoftBreak(s)
if (metadata.softbreak) then return " " else return "<P2RBR/>" end
end
function Plain(s)
return s
end
function Para(s)
return s
end
local function attr_val(attr, key)
local attr_table = {} for k, v in pairs(attr) do if (k == key and v and v ~= "") then return v end end return ""
end
local function attr_classes(attr)
local classes = {} for cls in attr_val(attr, "class"):gmatch("[^%s]+") do classes[cls] = true end return classes
end
local function attr_scale(attr, key) – a helper for CaptionedImage
scale = attr_val(attr, key) if (scale == "") or (key == "scale") then return scale end scale, count = scale:gsub("%%$", "") if count == 0 then log("WARNING: Units must be % for `" .. key .. "` of Image. Ignored.\n") return "" end return tonumber(scale) / 100
end
function Header(level, s, attr)
local headmark = "" for i = 1, level do headmark = headmark .. "=" end local classes = attr_classes(attr) headmark = headmark .. ( -- Re:VIEW's behavior classes["column"] and "[column]" or ( classes["nonum"] and "[nonum]" or ( classes["nodisp"] and "[nodisp]" or ( classes["notoc"] and "[notoc]" or ( -- Pandoc's behavior classes["unnumbered"] and ( classes["unlisted"] and "[notoc]" or "[nonum]") or ( -- None ""))))) ) if ((config.use_header_id == "true") and attr.id ~= "" and attr.id ~= s) then headmark = headmark .. "{" .. attr.id .. "}" end return headmark .. " " .. s
end
function HorizontalRule()
if (config.use_hr == "true") then return "//hr" else return "" end
end
local function lint_list(s)
return s:gsub("\n+(//beginchild)\n+", '\n\n%1\n\n' ):gsub("\n+(//endchild)\n+", '\n\n%1\n\n' ):gsub("\n+(//endchild)\n*$", "\n\n%1")
end
function BulletList(items)
local buffer = {} for _, item in pairs(items) do if (item == "//beginchild") or (item == "//endchild") then table.insert(buffer, item) else table.insert(buffer, " * " .. item) end end return lint_list(table.concat(buffer, "\n"))
end
function OrderedList(items, start)
local buffer = {} local n = start for _, item in pairs(items) do if (item == "//beginchild") or (item == "//endchild") then table.insert(buffer, item) else table.insert(buffer, " " .. n .. ". " .. item) n = n + 1 end end return lint_list(table.concat(buffer, "\n"))
end
function DefinitionList(items)
local buffer = {} for _, item in pairs(items) do for k, v in pairs(item) do table.insert(buffer, " : " .. k .. "\n\t" .. table.concat(v, "\n")) end end return lint_list(table.concat(buffer, "\n") .. "\n")
end
function BlockQuote(s)
return "//quote{\n" .. s .. "\n//}"
end
function CodeBlock(s, attr)
local classes = attr_classes(attr) local command = nil for k,v in pairs({cmd = "cmd", source = "source", quote = "source"}) do if classes[k] then command = v break end end command = command or "list" is_list = command == "list" local num = (is_list == false) and "" or ( (classes["numberLines"] or classes["number-lines"] or classes["num"]) and "num" or "" ) local firstlinenum = "" if is_list and (num == "num") then for _, key in ipairs({"startFrom", "start-from", "firstlinenum"}) do firstlinenum = attr_val(attr, key) if firstlinenum ~= "" then firstlinenum = "//firstlinenum[" .. firstlinenum .. "]\n" break end end end local lang = "" local not_lang = {numberLines = true, num = true, em = true, source = true} not_lang["number-lines"] = true if is_list or (command == "source") then for key,_ in pairs(classes) do if not_lang[key] ~= true then lang = "[" .. key .. "]" break end end end local caption = (command == "cmd") and "" or attr_val(attr, "caption") local identifier = "" local em = is_list and classes["em"] and "em" or "" if (caption ~= "") then if is_list and (em == "") then if (attr.id ~= "") then identifier = "[" .. attr.id .. "]" else list_num = list_num + 1 identifier = "[list" .. list_num .. "]" end end caption = "[" .. caption .. "]" else if is_list then em = "em" end if lang ~= "" then caption = "[" .. caption .. "]" end end return ( firstlinenum .. "//" .. em .. command .. num .. identifier .. caption .. lang .. "{\n" .. s .. "\n//}" )
end
function LineBlock(s)
-- | block return "//" .. config.lineblock .. "{\n" .. table.concat(s, "\n") .. "\n//}"
end
function Link(s, src, tit)
if (src == s) then return format_inline("href", src) else return format_inline("href", src .. "," .. s) end
end
function Code(s, attr)
-- ignore attr return format_inline(config.code, s)
end
function Emph(s)
return format_inline(config.italic, s)
end
function Strong(s)
return format_inline(config.bold, s)
end
function Strikeout(s)
return format_inline(config.strike, s)
end
function Underline(s)
return format_inline(config.underline, s)
end
function Subscript(s)
return format_inline("sub", s)
end
function Superscript(s)
return format_inline("sup", s)
end
function InlineMath(s)
return format_inline("m", s)
end
function DisplayMath(s)
return format_inline("m", "\\displaystyle{}" .. s)
end
function Table(caption, aligns, widths, headers, rows)
local buffer = {} local function add(s) table.insert(buffer, s) end if caption ~= "" then table_num = table_num + 1 add("//table[table" .. table_num .. "][" .. caption .. "]{") else add("//table{") end local tmp = {} for i, h in pairs(headers) do align = html_align(aligns[i]) if (config.use_table_align == "true") and (align ~= "") then h = format_inline("dtp", "table align=" .. align) .. h end table.insert(tmp, h) end add(table.concat(tmp, "\t")) add("--------------") for _, row in pairs(rows) do tmp = {} for i, c in pairs(row) do align = html_align(aligns[i]) if (config.use_table_align == "true") and (align ~= "") then c = format_inline("dtp", "table align=" .. align) .. c end table.insert(tmp, c) end add(table.concat(tmp, "\t")) end add("//}") return table.concat(buffer, "\n")
end
function Image(s, src, tit)
-- Re:VIEW @<icon> ignores caption and title local id = string.gsub(src, "%.%w+$", "") id = string.gsub(id, "^images/", "") return format_inline("icon", id)
end
function CaptionedImage(s, src, tit, attr)
local path = "[" .. s:gsub("%.%w+$", ""):gsub("^images/", "") .. "]" local comment = src:gsub("^fig:", ""):gsub("(.+)", "\n%1") local scale = attr_scale(attr, "scale") if scale == "" then local width = attr_scale(attr, "width") local height = attr_scale(attr, "height") if (width ~= "") then if (height ~= "") and (width ~= height) then log("WARNING: Image width and height must be same. Using width.\n") end scale = width else scale = height end end if scale ~= "" then scale = "[scale=" .. scale .. "]" end local command = "//image" local caption = "" if (tit == "") then command = "//indepimage" else caption = "[" .. tit .. "]" end return ( command .. path .. caption .. scale .. "{" .. comment .. "\n//}" )
end
function Note(s)
note_num = note_num + 1 table.insert(footnotes, "//footnote[fn" .. note_num .. "][" .. s .. "]") return format_inline("fn", "fn" .. note_num)
end
function Cite(s, cs)
-- use @ as is. return s
end
function Quoted(quotetype, s)
if (quotetype == "SingleQuote") then return SingleQuoted(s) end if (quotetype == "DoubleQuote") then return DoubleQuoted(s) end
end
function SingleQuoted(s)
return "'" .. s .. "'"
end
function DoubleQuoted(s)
return '"' .. s .. '"'
end
function SmallCaps(s)
return "◆→SMALLCAPS:" .. s .. "←◆"
end
function Div(s, attr)
local blankline = attr_val(attr, "blankline") if blankline ~= "" then local buffer = {} for i = 1, tonumber(blankline) do table.insert(buffer, "//blankline") end return table.concat(buffer, "\n") end local classes = attr_classes(attr) if next(classes) == nil then if metadata.stripemptydev then return s else return "//{\n" .. s .. "\n//}" end end if classes["review-internal"] then s, _ = s:gsub( "%]{<P2RREMOVEBELOW/>\n", "]{" ):gsub( "\n<P2RREMOVEABOVE/>//}", "//}" ) return s end for cls,_ in pairs(classes) do s = "//" .. cls .. "{\n" .. s .. "\n//}" end return s
end
function Span(s, attr)
-- ruby and kw with a supplement local a = "" for _, cmd in ipairs({"ruby", "kw"}) do a = attr_val(attr, cmd) if a ~= "" then s = format_inline(cmd, s .. "," .. a) end end -- inline format for cmd in attr_val(attr, "class"):gmatch("[^%s]+") do if inline_commands[cmd] then s = format_inline(cmd, s) end end return s
end
function RawInline(format, text)
if (format == "review") then return text end if (metadata.hideraw) then return "" end if (format == "tex") then return format_inline("embed", "|latex|" .. text) else return format_inline("embed", "|" .. format .. "|", text) end
end
function RawBlock(format, text)
if (format == "review") then return text end if (metadata.hideraw) then return "" end if (format == "tex") then return "//embed[latex]{\n" .. text .. "\n//}" else return "//embed[" .. format .. "]{\n" .. text .. "\n//}" end
end
try_catch {
try = function() metadata = PANDOC_DOCUMENT.meta end, catch = function(error) log("Due to your pandoc version is too old, config.yml loader is disabled.\n") end
}
if (metadata) then
-- Load config from YAML for k,v in pairs(config) do if metadata[k] ~= nil then config[k] = stringify(metadata[k]) end end
end
local meta = {} meta.__index =
function(_, key) log(string.format("WARNING: Undefined function '%s'\n", key)) return function() return "" end end
setmetatable(_G, meta)