1818-- Store metadata at module level
1919--- @type DocumentMetadata
2020local 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
2828local 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
3939end
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
5155end
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
5660local 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
8184end
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
115127end
116128
117129-- Process metadata
118130function 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
125137end
126138
127139-- Main processing function for divs
128140function 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+ })
191202end
192203
193204-- Return the list of functions to register
194205return {
195- {Meta = Meta },
196- {Div = Div }
206+ {Meta = Meta },
207+ {Div = Div }
197208}
0 commit comments