Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/altdoc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
with:
packages: "."
repo-path: "_site"
webr-image: "ghcr.io/r-wasm/webr:v0.4.2"
webr-image: "ghcr.io/r-wasm/webr:v0.5.2"

# Upload the CRAN repository for use in the next step
# Make sure to set a retention day to avoid running into a cap
Expand Down Expand Up @@ -73,6 +73,14 @@ jobs:
- name: Dependencies
run: ./run.sh install_all

# Tagged quarto extension versions to work with webR ---
- name: Grab tagged versions of Quarto Extensions
run: |
cd altdoc
quarto add --no-prompt coatless/quarto-webr@0.4.3
quarto add --no-prompt coatless-quarto/panelize@0.0.2
cd ../

- name: Build site
run: |
# If parallel = TRUE in render_docs()
Expand Down
2 changes: 1 addition & 1 deletion altdoc/_extensions/coatless-quarto/panelize/_extension.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: panelize
title: Panelize code cells
author: James Joseph Balamuta
version: 0.0.2
version: 0.0.2-dev.1
quarto-required: ">=1.4.554"
contributes:
filters:
Expand Down
293 changes: 152 additions & 141 deletions altdoc/_extensions/coatless-quarto/panelize/panelize.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,180 +18,191 @@
-- Store metadata at module level
---@type DocumentMetadata
local document_metadata = {
panelize = {},
handler_added = false
panelize = {},
handler_added = false
}

-- Helper function to detect language from a code block
---@param block Block The code block to analyze
---@return string|nil language The detected language
local function detect_language(block)
if block.attr.classes:includes("r") then
return "r"
elseif block.attr.classes:includes("python") then
return "python"
elseif block.text:match("^```{{r") then
return "r"
elseif block.text:match("^```{{python") then
return "python"
end
return nil
if block.attr.classes:includes("r") then
return "r"
elseif block.attr.classes:includes("python") then
return "python"
elseif block.text:match("^```{{r") then
return "r"
elseif block.text:match("^```{{python") then
return "python"
end
return nil
end

-- Helper function to clean code block text
---@param block Block The code block to clean
---@param language string The programming language
---@param remove_fences boolean Whether to remove code fences and options
---@return string cleaned_text The processed text
local function clean_code_text(block, language)
local text = block.text
if text:match("^```{{" .. language .. "}") then
text = text:gsub("```{{" .. language .. "}}\n", ""):gsub("\n```", "")
end
return text:gsub("#|.-\n", "")
local function clean_code_text(block, language, remove_fences)
local text = block.text
if remove_fences then
if text:match("^```{{" .. language .. "}") then
text = text:gsub("```{{" .. language .. "}}\n", ""):gsub("\n```", "")
end
text = text:gsub("#|.-\n", "")
end
return text
end

-- Helper function to extract cell content
---@param cell_div Block The cell div block
---@return Cell cell The processed cell content
local function extract_cell_content(cell_div)
local cell = {
code_blocks = {},
outputs = {},
language = nil
}

-- Process blocks in order to maintain sequence
for _, block in ipairs(cell_div.content) do
if block.t == "CodeBlock" and block.classes:includes("cell-code") then
table.insert(cell.code_blocks, block)
-- Detect language from first code block if not already set
if not cell.language then
cell.language = detect_language(block)
end
elseif block.t == "Div" and (
block.classes:includes("cell-output") or
block.classes:includes("cell-output-stdout") or
block.classes:includes("cell-output-display")
) then
table.insert(cell.outputs, block)
end
end

return cell
local cell = {
blocks = {}, -- Will store alternating code blocks and their outputs
language = nil
}

-- Process blocks in sequence
for _, block in ipairs(cell_div.content) do
if block.t == "CodeBlock" and block.classes:includes("cell-code") then
table.insert(cell.blocks, {type = "code", content = block})
-- Detect language from first code block if not already set
if not cell.language then
cell.language = detect_language(block)
end
elseif block.t == "Div" and (
block.classes:includes("cell-output") or
block.classes:includes("cell-output-stdout") or
block.classes:includes("cell-output-display")
) then
table.insert(cell.blocks, {type = "output", content = block})
end
end

return cell
end

-- Helper function to create tab content
---@param cell Cell The cell content
---@param interactive boolean Whether this is an interactive tab
---@param tab_type string The type of tab ("result", "source", or "interactive")
---@return pandoc.List content The tab content
local function create_tab_content(cell, interactive)
local content = pandoc.List()

if interactive then
-- For interactive tab, combine all code blocks into one
local combined_code = table.concat(
pandoc.List(cell.code_blocks):map(function(block)
return clean_code_text(block, cell.language)
end),
"\n"
)

-- Create single code block with appropriate classes
local classes = cell.language == "r" and {"{webr-r}", "cell-code"} or {"{pyodide-python}", "cell-code"}
local attr = pandoc.Attr("", classes, {})
content:insert(pandoc.CodeBlock(combined_code, attr))
else
-- For result tab, keep original structure
for i, code_block in ipairs(cell.code_blocks) do
content:insert(code_block)
-- Add corresponding output if it exists
if cell.outputs[i] then
content:insert(cell.outputs[i])
end
end
end

return content
local function create_tab_content(cell, tab_type)
local content = pandoc.List()

if tab_type == "interactive" then
-- For interactive tab, combine all code blocks into one
local combined_code = table.concat(
pandoc.List(cell.blocks)
:filter(function(block) return block.type == "code" end)
:map(function(block) return clean_code_text(block.content, cell.language, true) end),
"\n"
)

-- Create single code block with appropriate classes
local classes = cell.language == "r" and {"{webr-r}", "cell-code"} or {"{pyodide-python}", "cell-code"}
local attr = pandoc.Attr("", classes, {})
content:insert(pandoc.CodeBlock(combined_code, attr))
else
-- For result and source tabs, process blocks in sequence
for _, block in ipairs(cell.blocks) do
if block.type == "code" then
if tab_type == "result" then
-- For result tab, clean code but keep language class
local new_attr = block.content.attr:clone()
new_attr.classes = pandoc.List({cell.language})
local cleaned_text = clean_code_text(block.content, cell.language, true)
content:insert(pandoc.CodeBlock(cleaned_text, new_attr))
else
-- For source tab, use original code block
content:insert(block.content)
end
else -- output block
content:insert(block.content)
end
end
end

return content
end

-- Process metadata
function Meta(meta)
if meta and meta.panelize then
for key, value in pairs(meta.panelize) do
document_metadata.panelize[key] = pandoc.utils.stringify(value)
end
end
return meta
if meta and meta.panelize then
for key, value in pairs(meta.panelize) do
document_metadata.panelize[key] = pandoc.utils.stringify(value)
end
end
return meta
end

-- Main processing function for divs
function Div(div)
-- Check for required classes
local to_webr = div.classes:includes("to-webr")
local to_pyodide = div.classes:includes("to-pyodide")
local to_source = div.classes:includes("to-source")

if not (to_source or to_webr or to_pyodide) then
return div
end

-- Find cell div
local cell_div = nil
for _, block in ipairs(div.content) do
if block.t == "Div" and block.classes:includes("cell") then
cell_div = block
break
end
end

if not cell_div then
return div
end

-- Extract cell content
local cell = extract_cell_content(cell_div)

if not cell.language then
quarto.log.error("Please specify either R or Python code cells inside of the .to-* div.")
return div
end

-- Create tabs
local tabs = pandoc.List()

if to_webr or to_pyodide then
-- Interactive environment tabs
tabs:insert(quarto.Tab({
title = "Result",
content = pandoc.Blocks(create_tab_content(cell, false))
}))
tabs:insert(quarto.Tab({
title = "Interactive",
content = pandoc.Blocks(create_tab_content(cell, true))
}))
else
-- Source code tabs
tabs:insert(quarto.Tab({
title = "Result",
content = pandoc.Blocks(create_tab_content(cell, false))
}))
-- For source tab, show original code without execution
tabs:insert(quarto.Tab({
title = "Source",
content = pandoc.Blocks(pandoc.List(cell.code_blocks))
}))
end

-- Return just the tabset, replacing the original div
return quarto.Tabset({
level = 3,
tabs = tabs,
attr = pandoc.Attr("", {"panel-tabset"}, {})
})
-- Check for required classes
local to_webr = div.classes:includes("to-webr")
local to_pyodide = div.classes:includes("to-pyodide")
local to_source = div.classes:includes("to-source")

if not (to_source or to_webr or to_pyodide) then
return div
end

-- Find cell div
local cell_div = nil
for _, block in ipairs(div.content) do
if block.t == "Div" and block.classes:includes("cell") then
cell_div = block
break
end
end

if not cell_div then
return div
end

-- Extract cell content
local cell = extract_cell_content(cell_div)

if not cell.language then
quarto.log.error("Please specify either R or Python code cells inside of the .to-* div.")
return div
end

-- Create tabs
local tabs = pandoc.List()

if to_webr or to_pyodide then
-- Interactive environment tabs
tabs:insert(quarto.Tab({
title = "Result",
content = pandoc.Blocks(create_tab_content(cell, "result"))
}))
tabs:insert(quarto.Tab({
title = "Interactive",
content = pandoc.Blocks(create_tab_content(cell, "interactive"))
}))
else
-- Source code tabs
tabs:insert(quarto.Tab({
title = "Result",
content = pandoc.Blocks(create_tab_content(cell, "result"))
}))
tabs:insert(quarto.Tab({
title = "Source",
content = pandoc.Blocks(create_tab_content(cell, "source"))
}))
end

-- Return just the tabset, replacing the original div
return quarto.Tabset({
level = 3,
tabs = tabs,
attr = pandoc.Attr("", {"panel-tabset"}, {})
})
end

-- Return the list of functions to register
return {
{Meta = Meta},
{Div = Div}
{Meta = Meta},
{Div = Div}
}
2 changes: 1 addition & 1 deletion altdoc/_extensions/coatless/webr/webr.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ local hasDoneWebRSetup = false

--- Define a base compatibile version
---@type string
local baseVersionWebR = "0.4.0"
local baseVersionWebR = "0.5.2"

--- Define where webR can be found
---@type string
Expand Down
Loading