diff --git a/src/Literate.jl b/src/Literate.jl index 2727cab..db79f01 100644 --- a/src/Literate.jl +++ b/src/Literate.jl @@ -46,7 +46,7 @@ CodeChunk() = CodeChunk(String[], false) ismdline(line) = (occursin(r"^\h*#$", line) || occursin(r"^\h*# .*$", line)) && !occursin(r"^\h*##", line) -function parse(content; allow_continued = true) +function parse(flavor::AbstractFlavor, content; allow_continued = true) lines = collect(eachline(IOBuffer(content))) chunks = Chunk[] @@ -76,9 +76,14 @@ function parse(content; allow_continued = true) if !(chunks[end] isa CodeChunk) push!(chunks, CodeChunk()) end - # remove "## " and "##\n", strip the leading "#" from "## xyz" and "##| xyz" - # Note: accepts only standard space character (not no-break space U+00A0) - line = replace(replace(line, r"^(\h*)#(#(:? |\|).*)$" => s"\1\2"), r"^(\h*#)#$" => s"\1") + # remove "## " and "##\n" (strips leading "#" for code comments) + if flavor isa QuartoFlavor + # for Quarto, strip leading "#" from code cell commands, eg, "##| echo: true" -> "#| echo: true" + line = replace(replace(line, r"^(\h*)#(#(:? |\|).*)$" => s"\1\2"), r"^(\h*#)#$" => s"\1") + else + # all other flavors + line = replace(replace(line, r"^(\h*)#(# .*)$" => s"\1\2"), r"^(\h*#)#$" => s"\1") + end push!(chunks[end].lines, line) end end @@ -285,12 +290,12 @@ function edit_commit(inputfile, user_config) end # Default to DefaultFlavor() setting -pick_codefence(flavor::AbstractFlavor,execute::Bool,name::AbstractString)=pick_codefence(DefaultFlavor(),execute,name) -pick_codefence(flavor::DefaultFlavor,execute::Bool,name::AbstractString)=("````julia" => "````") -pick_codefence(flavor::DocumenterFlavor,execute::Bool,name::AbstractString)=(execute ? - pick_codefence(DefaultFlavor(),execute,name) : ("````@example $(name)" => "````") +pick_codefence(flavor::AbstractFlavor, execute::Bool, name::AbstractString)=pick_codefence(DefaultFlavor(), execute,name) +pick_codefence(flavor::DefaultFlavor, execute::Bool, name::AbstractString)=("````julia" => "````") +pick_codefence(flavor::DocumenterFlavor,execute::Bool, name::AbstractString)=(execute ? + pick_codefence(DefaultFlavor(), execute,name) : ("````@example $(name)" => "````") ) -pick_codefence(flavor::QuartoFlavor,execute::Bool,name::AbstractString)=(execute ? +pick_codefence(flavor::QuartoFlavor, execute::Bool, name::AbstractString)=(execute ? error("QuartoFlavor does not support argument execute=true!") : ("```{julia}" => "```") ) @@ -490,7 +495,7 @@ function preprocessor(inputfile, outputdir; user_config, user_kwargs, type) content = replace_default(content, type; config=config) # parse the content into chunks - chunks = parse(content; allow_continued = type !== :nb) + chunks = parse(config["flavor"], content; allow_continued = type !== :nb) return chunks, config end diff --git a/test/runtests.jl b/test/runtests.jl index 17af53f..c932767 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ import Literate, JSON import Literate: Chunk, MDChunk, CodeChunk -import Literate: pick_codefence +import Literate: pick_codefence, DefaultFlavor, QuartoFlavor using Test # compare content of two parsed chunk vectors @@ -109,6 +109,8 @@ end ## Line 77 ## ## Line 79 + # Line 80: Quarto Specific + ##| Line 81 """ expected_chunks = Chunk[ MDChunk(["" => "Line 1"]), @@ -146,10 +148,55 @@ end CodeChunk(["Line 64", " # Line 65", " Line 66", "Line 67"], false), CodeChunk(["# Line 73", "#", "# Line 75"], false), CodeChunk([" # Line 77", " #", " # Line 79"], false), - ] - parsed_chunks = Literate.parse(content) + MDChunk(["" => "Line 80: Quarto Specific"]), + CodeChunk(["##| Line 81"], false) + ] + parsed_chunks = Literate.parse(DefaultFlavor(), content) compare_chunks(parsed_chunks, expected_chunks) + # QuartoFlavor parsing semantics + expected_chunks_quarto = Chunk[ + MDChunk(["" => "Line 1"]), + CodeChunk(["Line 2"], false), + MDChunk(["" => "Line 3", "" => "","" => "Line 5"]), + CodeChunk(["Line 6", "","Line 8"], false), + MDChunk(["" => "Line 9"]), + MDChunk(["" => "Line 11"]), + CodeChunk(["Line 12"], false), + CodeChunk(["Line 14"], false), + MDChunk(["" => "Line 15"]), + MDChunk(["" => "Line 17"]), + CodeChunk(["Line 18"], false), + CodeChunk(["Line 20"], false), + MDChunk(["" => "Line 21"]), + CodeChunk(["Line 22", " Line 23", "Line 24"], false), + CodeChunk(["Line 26", " Line 27"], true), + CodeChunk(["Line 29"], false), + CodeChunk(["Line 31", " Line 32"], true), + MDChunk(["" => "Line 33"]), + CodeChunk(["Line 34"], false), + CodeChunk(["Line 36"], true), + CodeChunk([" Line 38"], true), + CodeChunk(["Line 40"], false), + CodeChunk(["Line 42", " Line 43"], true), + MDChunk(["" => "Line 44"]), + CodeChunk([" Line 45"], true), + MDChunk(["" => "Line 46"]), + CodeChunk(["Line 47"], false), + MDChunk(["" => "Line 48"]), + CodeChunk(["#Line 49", "Line 50"], false), + MDChunk(["" => "Line 53"]), + CodeChunk(["# Line 57", "Line 58", "# Line 59", "##Line 60"], false), + MDChunk([" " => "Line 62", " " => "# Line 63"]), + CodeChunk(["Line 64", " # Line 65", " Line 66", "Line 67"], false), + CodeChunk(["# Line 73", "#", "# Line 75"], false), + CodeChunk([" # Line 77", " #", " # Line 79"], false), + MDChunk(["" => "Line 80: Quarto Specific"]), + CodeChunk(["#| Line 81"], false) # parses correctly as code cell command + ] + parsed_chunks = Literate.parse(QuartoFlavor(), content) + compare_chunks(parsed_chunks, expected_chunks_quarto) + # test leading/trailing whitespace removal io = IOBuffer() iows = IOBuffer() @@ -166,7 +213,7 @@ end foreach(x -> println(iows), 1:rand(2:5)) end - compare_chunks(Literate.parse(String(take!(io))), Literate.parse(String(take!(iows)))) + compare_chunks(Literate.parse(DefaultFlavor(), String(take!(io))), Literate.parse(DefaultFlavor(), String(take!(iows)))) end # testset parser @@ -202,7 +249,6 @@ content = """ Source code only #src ## # Comment ## another comment - ##| echo: false Quarto parameters #- for i in 1:10 print(i) @@ -324,7 +370,6 @@ const GITLAB_ENV = Dict( x + 3 # # Comment # another comment - #| echo: false Quarto parameters for i in 1:10 print(i) @@ -556,7 +601,6 @@ end end x * 3 # # Comment # another comment - #| echo: false Quarto parameters ```` ````@example inputfile; continued = true @@ -1022,8 +1066,7 @@ end end "x * 3\\n", "x * 3\\n", "# # Comment\\n", - "# another comment\\n", - "#| echo: false Quarto parameters" + "# another comment" ], """, @@ -1419,16 +1462,16 @@ end end @test occursin("Link to binder: www.example3.com/file.jl", script) # Test pick_codefence function - default_codefence=pick_codefence(Literate.DefaultFlavor(),true,"testname") + default_codefence=pick_codefence(Literate.DefaultFlavor(), true, "testname") @test default_codefence == ("````julia" => "````") - @test default_codefence == pick_codefence(Literate.FranklinFlavor(),true,"testname") - @test default_codefence == pick_codefence(Literate.DocumenterFlavor(),true,"testname") + @test default_codefence == pick_codefence(Literate.FranklinFlavor(), true, "testname") + @test default_codefence == pick_codefence(Literate.DocumenterFlavor(), true, "testname") documenter_codefence = ("````@example testname" => "````") - @test documenter_codefence == pick_codefence(Literate.DocumenterFlavor(),false,"testname") + @test documenter_codefence == pick_codefence(Literate.DocumenterFlavor(), false, "testname") let expected_exception=ErrorException("QuartoFlavor does not support argument execute=true!") - @test_throws expected_exception pick_codefence(Literate.QuartoFlavor(),true,"testname") + @test_throws expected_exception pick_codefence(Literate.QuartoFlavor(), true, "testname") end - @test ("```{julia}" => "```") == pick_codefence(Literate.QuartoFlavor(),false,"testname") + @test ("```{julia}" => "```") == pick_codefence(Literate.QuartoFlavor(), false, "testname") # Misc default configs create(; type, kw...) = Literate.create_configuration(inputfile; user_config=Dict(), user_kwargs=kw, type=type)