Skip to content

Commit ce954b3

Browse files
committed
Ensure .to-source is respected as well.
1 parent 3aae12e commit ce954b3

File tree

1 file changed

+152
-141
lines changed

1 file changed

+152
-141
lines changed

_extensions/panelize/panelize.lua

+152-141
Original file line numberDiff line numberDiff line change
@@ -18,180 +18,191 @@
1818
-- Store metadata at module level
1919
---@type DocumentMetadata
2020
local document_metadata = {
21-
panelize = {},
22-
handler_added = false
21+
panelize = {},
22+
handler_added = false
2323
}
2424

2525
-- Helper function to detect language from a code block
2626
---@param block Block The code block to analyze
2727
---@return string|nil language The detected language
2828
local function detect_language(block)
29-
if block.attr.classes:includes("r") then
30-
return "r"
31-
elseif block.attr.classes:includes("python") then
32-
return "python"
33-
elseif block.text:match("^```{{r") then
34-
return "r"
35-
elseif block.text:match("^```{{python") then
36-
return "python"
37-
end
38-
return nil
29+
if block.attr.classes:includes("r") then
30+
return "r"
31+
elseif block.attr.classes:includes("python") then
32+
return "python"
33+
elseif block.text:match("^```{{r") then
34+
return "r"
35+
elseif block.text:match("^```{{python") then
36+
return "python"
37+
end
38+
return nil
3939
end
4040

4141
-- Helper function to clean code block text
4242
---@param block Block The code block to clean
4343
---@param language string The programming language
44+
---@param remove_fences boolean Whether to remove code fences and options
4445
---@return string cleaned_text The processed text
45-
local function clean_code_text(block, language)
46-
local text = block.text
47-
if text:match("^```{{" .. language .. "}") then
48-
text = text:gsub("```{{" .. language .. "}}\n", ""):gsub("\n```", "")
49-
end
50-
return text:gsub("#|.-\n", "")
46+
local function clean_code_text(block, language, remove_fences)
47+
local text = block.text
48+
if remove_fences then
49+
if text:match("^```{{" .. language .. "}") then
50+
text = text:gsub("```{{" .. language .. "}}\n", ""):gsub("\n```", "")
51+
end
52+
text = text:gsub("#|.-\n", "")
53+
end
54+
return text
5155
end
5256

5357
-- Helper function to extract cell content
5458
---@param cell_div Block The cell div block
5559
---@return Cell cell The processed cell content
5660
local function extract_cell_content(cell_div)
57-
local cell = {
58-
code_blocks = {},
59-
outputs = {},
60-
language = nil
61-
}
62-
63-
-- Process blocks in order to maintain sequence
64-
for _, block in ipairs(cell_div.content) do
65-
if block.t == "CodeBlock" and block.classes:includes("cell-code") then
66-
table.insert(cell.code_blocks, block)
67-
-- Detect language from first code block if not already set
68-
if not cell.language then
69-
cell.language = detect_language(block)
70-
end
71-
elseif block.t == "Div" and (
72-
block.classes:includes("cell-output") or
73-
block.classes:includes("cell-output-stdout") or
74-
block.classes:includes("cell-output-display")
75-
) then
76-
table.insert(cell.outputs, block)
77-
end
78-
end
79-
80-
return cell
61+
local cell = {
62+
blocks = {}, -- Will store alternating code blocks and their outputs
63+
language = nil
64+
}
65+
66+
-- Process blocks in sequence
67+
for _, block in ipairs(cell_div.content) do
68+
if block.t == "CodeBlock" and block.classes:includes("cell-code") then
69+
table.insert(cell.blocks, {type = "code", content = block})
70+
-- Detect language from first code block if not already set
71+
if not cell.language then
72+
cell.language = detect_language(block)
73+
end
74+
elseif block.t == "Div" and (
75+
block.classes:includes("cell-output") or
76+
block.classes:includes("cell-output-stdout") or
77+
block.classes:includes("cell-output-display")
78+
) then
79+
table.insert(cell.blocks, {type = "output", content = block})
80+
end
81+
end
82+
83+
return cell
8184
end
8285

8386
-- Helper function to create tab content
8487
---@param cell Cell The cell content
85-
---@param interactive boolean Whether this is an interactive tab
88+
---@param tab_type string The type of tab ("result", "source", or "interactive")
8689
---@return pandoc.List content The tab content
87-
local function create_tab_content(cell, interactive)
88-
local content = pandoc.List()
89-
90-
if interactive then
91-
-- For interactive tab, combine all code blocks into one
92-
local combined_code = table.concat(
93-
pandoc.List(cell.code_blocks):map(function(block)
94-
return clean_code_text(block, cell.language)
95-
end),
96-
"\n"
97-
)
98-
99-
-- Create single code block with appropriate classes
100-
local classes = cell.language == "r" and {"{webr-r}", "cell-code"} or {"{pyodide-python}", "cell-code"}
101-
local attr = pandoc.Attr("", classes, {})
102-
content:insert(pandoc.CodeBlock(combined_code, attr))
103-
else
104-
-- For result tab, keep original structure
105-
for i, code_block in ipairs(cell.code_blocks) do
106-
content:insert(code_block)
107-
-- Add corresponding output if it exists
108-
if cell.outputs[i] then
109-
content:insert(cell.outputs[i])
110-
end
111-
end
112-
end
113-
114-
return content
90+
local function create_tab_content(cell, tab_type)
91+
local content = pandoc.List()
92+
93+
if tab_type == "interactive" then
94+
-- For interactive tab, combine all code blocks into one
95+
local combined_code = table.concat(
96+
pandoc.List(cell.blocks)
97+
:filter(function(block) return block.type == "code" end)
98+
:map(function(block) return clean_code_text(block.content, cell.language, true) end),
99+
"\n"
100+
)
101+
102+
-- Create single code block with appropriate classes
103+
local classes = cell.language == "r" and {"{webr-r}", "cell-code"} or {"{pyodide-python}", "cell-code"}
104+
local attr = pandoc.Attr("", classes, {})
105+
content:insert(pandoc.CodeBlock(combined_code, attr))
106+
else
107+
-- For result and source tabs, process blocks in sequence
108+
for _, block in ipairs(cell.blocks) do
109+
if block.type == "code" then
110+
if tab_type == "result" then
111+
-- For result tab, clean code but keep language class
112+
local new_attr = block.content.attr:clone()
113+
new_attr.classes = pandoc.List({cell.language})
114+
local cleaned_text = clean_code_text(block.content, cell.language, true)
115+
content:insert(pandoc.CodeBlock(cleaned_text, new_attr))
116+
else
117+
-- For source tab, use original code block
118+
content:insert(block.content)
119+
end
120+
else -- output block
121+
content:insert(block.content)
122+
end
123+
end
124+
end
125+
126+
return content
115127
end
116128

117129
-- Process metadata
118130
function Meta(meta)
119-
if meta and meta.panelize then
120-
for key, value in pairs(meta.panelize) do
121-
document_metadata.panelize[key] = pandoc.utils.stringify(value)
122-
end
123-
end
124-
return meta
131+
if meta and meta.panelize then
132+
for key, value in pairs(meta.panelize) do
133+
document_metadata.panelize[key] = pandoc.utils.stringify(value)
134+
end
135+
end
136+
return meta
125137
end
126138

127139
-- Main processing function for divs
128140
function Div(div)
129-
-- Check for required classes
130-
local to_webr = div.classes:includes("to-webr")
131-
local to_pyodide = div.classes:includes("to-pyodide")
132-
local to_source = div.classes:includes("to-source")
133-
134-
if not (to_source or to_webr or to_pyodide) then
135-
return div
136-
end
137-
138-
-- Find cell div
139-
local cell_div = nil
140-
for _, block in ipairs(div.content) do
141-
if block.t == "Div" and block.classes:includes("cell") then
142-
cell_div = block
143-
break
144-
end
145-
end
146-
147-
if not cell_div then
148-
return div
149-
end
150-
151-
-- Extract cell content
152-
local cell = extract_cell_content(cell_div)
153-
154-
if not cell.language then
155-
quarto.log.error("Please specify either R or Python code cells inside of the .to-* div.")
156-
return div
157-
end
158-
159-
-- Create tabs
160-
local tabs = pandoc.List()
161-
162-
if to_webr or to_pyodide then
163-
-- Interactive environment tabs
164-
tabs:insert(quarto.Tab({
165-
title = "Result",
166-
content = pandoc.Blocks(create_tab_content(cell, false))
167-
}))
168-
tabs:insert(quarto.Tab({
169-
title = "Interactive",
170-
content = pandoc.Blocks(create_tab_content(cell, true))
171-
}))
172-
else
173-
-- Source code tabs
174-
tabs:insert(quarto.Tab({
175-
title = "Result",
176-
content = pandoc.Blocks(create_tab_content(cell, false))
177-
}))
178-
-- For source tab, show original code without execution
179-
tabs:insert(quarto.Tab({
180-
title = "Source",
181-
content = pandoc.Blocks(pandoc.List(cell.code_blocks))
182-
}))
183-
end
184-
185-
-- Return just the tabset, replacing the original div
186-
return quarto.Tabset({
187-
level = 3,
188-
tabs = tabs,
189-
attr = pandoc.Attr("", {"panel-tabset"}, {})
190-
})
141+
-- Check for required classes
142+
local to_webr = div.classes:includes("to-webr")
143+
local to_pyodide = div.classes:includes("to-pyodide")
144+
local to_source = div.classes:includes("to-source")
145+
146+
if not (to_source or to_webr or to_pyodide) then
147+
return div
148+
end
149+
150+
-- Find cell div
151+
local cell_div = nil
152+
for _, block in ipairs(div.content) do
153+
if block.t == "Div" and block.classes:includes("cell") then
154+
cell_div = block
155+
break
156+
end
157+
end
158+
159+
if not cell_div then
160+
return div
161+
end
162+
163+
-- Extract cell content
164+
local cell = extract_cell_content(cell_div)
165+
166+
if not cell.language then
167+
quarto.log.error("Please specify either R or Python code cells inside of the .to-* div.")
168+
return div
169+
end
170+
171+
-- Create tabs
172+
local tabs = pandoc.List()
173+
174+
if to_webr or to_pyodide then
175+
-- Interactive environment tabs
176+
tabs:insert(quarto.Tab({
177+
title = "Result",
178+
content = pandoc.Blocks(create_tab_content(cell, "result"))
179+
}))
180+
tabs:insert(quarto.Tab({
181+
title = "Interactive",
182+
content = pandoc.Blocks(create_tab_content(cell, "interactive"))
183+
}))
184+
else
185+
-- Source code tabs
186+
tabs:insert(quarto.Tab({
187+
title = "Result",
188+
content = pandoc.Blocks(create_tab_content(cell, "result"))
189+
}))
190+
tabs:insert(quarto.Tab({
191+
title = "Source",
192+
content = pandoc.Blocks(create_tab_content(cell, "source"))
193+
}))
194+
end
195+
196+
-- Return just the tabset, replacing the original div
197+
return quarto.Tabset({
198+
level = 3,
199+
tabs = tabs,
200+
attr = pandoc.Attr("", {"panel-tabset"}, {})
201+
})
191202
end
192203

193204
-- Return the list of functions to register
194205
return {
195-
{Meta = Meta},
196-
{Div = Div}
206+
{Meta = Meta},
207+
{Div = Div}
197208
}

0 commit comments

Comments
 (0)