-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Michael Schramm
committed
Mar 14, 2023
1 parent
e45fedd
commit 34325c3
Showing
1 changed file
with
164 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
-- lua filter for RST-like list-tables in Markdown. | ||
-- Copyright (C) 2021 Martin Fischer, released under MIT license | ||
|
||
if PANDOC_VERSION and PANDOC_VERSION.must_be_at_least then | ||
PANDOC_VERSION:must_be_at_least("2.11") | ||
else | ||
error("pandoc version >=2.11 is required") | ||
end | ||
|
||
-- Get the list of cells in a row. | ||
local row_cells = function (row) return row.cells end | ||
|
||
-- "Polyfill" for older pandoc versions. | ||
if PANDOC_VERSION <= '2.16.2' then | ||
-- previous pandoc versions used simple Attr/list pairs | ||
pandoc.Row = function (cells) return {{}, cells} end | ||
pandoc.TableHead = function (rows) return {{}, rows or {}} end | ||
pandoc.TableFoot = function (rows) return {{}, rows or {}} end | ||
pandoc.Cell = function (contents, align, rowspan, colspan, attr) | ||
return { | ||
attr = attr or pandoc.Attr(), | ||
alignment = align or pandoc.AlignDefault, | ||
contents = contents or {}, | ||
col_span = colspan or 1, | ||
row_span = rowspan or 1 | ||
} | ||
end | ||
row_cells = function (row) return row[2] end | ||
end | ||
|
||
local alignments = { | ||
d = 'AlignDefault', | ||
l = 'AlignLeft', | ||
r = 'AlignRight', | ||
c = 'AlignCenter' | ||
} | ||
|
||
local function get_colspecs(div_attributes, column_count) | ||
-- list of (align, width) pairs | ||
local colspecs = {} | ||
|
||
for i = 1, column_count do | ||
table.insert(colspecs, {pandoc.AlignDefault, nil}) | ||
end | ||
|
||
if div_attributes.aligns then | ||
local i = 1 | ||
for a in div_attributes.aligns:gmatch('[^,]') do | ||
assert(alignments[a] ~= nil, | ||
"unknown column alignment " .. tostring(a)) | ||
colspecs[i][1] = alignments[a] | ||
i = i + 1 | ||
end | ||
div_attributes.aligns = nil | ||
end | ||
|
||
if div_attributes.widths then | ||
local total = 0 | ||
local widths = {} | ||
for w in div_attributes.widths:gmatch('[^,]') do | ||
table.insert(widths, tonumber(w)) | ||
total = total + tonumber(w) | ||
end | ||
for i = 1, column_count do | ||
colspecs[i][2] = widths[i] / total | ||
end | ||
div_attributes.widths = nil | ||
end | ||
|
||
return colspecs | ||
end | ||
|
||
local function new_table_body(rows, header_col_count) | ||
return { | ||
attr = {}, | ||
body = rows, | ||
head = {}, | ||
row_head_columns = header_col_count | ||
} | ||
end | ||
|
||
local function new_cell(contents) | ||
local attr = {} | ||
local colspan = 1 | ||
local rowspan = 1 | ||
local align = pandoc.AlignDefault | ||
|
||
-- At the time of writing this Pandoc does not support attributes | ||
-- on list items, so we use empty spans as a workaround. | ||
if contents[1] and contents[1].content then | ||
if contents[1].content[1] and contents[1].content[1].t == "Span" then | ||
if #contents[1].content[1].content == 0 then | ||
attr = contents[1].content[1].attr | ||
table.remove(contents[1].content, 1) | ||
colspan = attr.attributes.colspan or 1 | ||
attr.attributes.colspan = nil | ||
rowspan = attr.attributes.rowspan or 1 | ||
attr.attributes.rowspan = nil | ||
align = alignments[attr.attributes.align] or pandoc.AlignDefault | ||
attr.attributes.align = nil | ||
end | ||
end | ||
end | ||
|
||
return pandoc.Cell(contents, align, rowspan, colspan, attr) | ||
end | ||
|
||
local function process(div) | ||
if div.attr.classes[1] ~= "list-table" then return nil end | ||
table.remove(div.attr.classes, 1) | ||
|
||
local caption = {} | ||
|
||
if div.content[1].t == "Para" then | ||
local para = table.remove(div.content, 1) | ||
caption = {pandoc.Plain(para.content)} | ||
end | ||
|
||
assert(div.content[1].t == "BulletList", | ||
"expected bullet list, found " .. div.content[1].t) | ||
local list = div.content[1] | ||
|
||
local rows = {} | ||
|
||
for i = 1, #list.content do | ||
assert(#list.content[i] == 1, "expected item to contain only one block") | ||
assert(list.content[i][1].t == "BulletList", | ||
"expected bullet list, found " .. list.content[i][1].t) | ||
local cells = {} | ||
for _, cell_content in pairs(list.content[i][1].content) do | ||
table.insert(cells, new_cell(cell_content)) | ||
end | ||
local row = pandoc.Row(cells) | ||
table.insert(rows, row) | ||
end | ||
|
||
local header_row_count = tonumber(div.attr.attributes['header-rows']) or 1 | ||
div.attr.attributes['header-rows'] = nil | ||
|
||
local header_col_count = tonumber(div.attr.attributes['header-cols']) or 0 | ||
div.attr.attributes['header-cols'] = nil | ||
|
||
local column_count = 0 | ||
for i = 1, #row_cells(rows[1] or {}) do | ||
column_count = column_count + row_cells(rows[1])[i].col_span | ||
end | ||
|
||
local colspecs = get_colspecs(div.attr.attributes, column_count) | ||
local thead_rows = {} | ||
for i = 1, header_row_count do | ||
table.insert(thead_rows, table.remove(rows, 1)) | ||
end | ||
|
||
return pandoc.Table( | ||
{long = caption, short = {}}, | ||
colspecs, | ||
pandoc.TableHead(thead_rows), | ||
{new_table_body(rows, header_col_count)}, | ||
pandoc.TableFoot(), | ||
div.attr | ||
) | ||
end | ||
|
||
return {{Div = process}} |