tlienart / Xranklin.jl

Experimental repo for a refactoring of Franklin.jl
https://tlienart.github.io/Xranklin.jl
MIT License
40 stars 1 forks source link

Feature request: Show line number when parsing failed #118

Open kdheepak opened 2 years ago

kdheepak commented 2 years ago

I got an error with the following that killed the development server:


[ Info: ⌛ processing introduction-to-julia/index.md
ERROR: FranklinParser.FranklinParserException(:BlockNotClosed, "An opening token 'RAW' was found but not closed.\n")
Stacktrace:
 [1] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, verbose::Bool, coreloopfun::Xranklin.var"#239#240"{OrderedCollections.LittleDict{Symbol, OrderedCollections.LittleDict{Pair{String, String}, Float64}, Vector{Symbol}, Vector{OrderedCollections.LittleDict{Pair{String, String}, Float64}}}}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
   @ LiveServer ~/.julia/packages/LiveServer/srpPq/src/server.jl:333
 [2] serve(d::String; dir::String, folder::String, clear::Bool, final::Bool, single::Bool, prepath::String, prefix::String, base_url_prefix::String, debug::Bool, cleanup::Bool, port::Int64, host::String, launch::Bool)
   @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/build/serve.jl:126
 [3] serve (repeats 2 times)
   @ ~/.julia/packages/Xranklin/xncHr/src/build/serve.jl:63 [inlined]
 [4] top-level scope
   @ none:1

caused by: FranklinParser.FranklinParserException(:BlockNotClosed, "An opening token 'RAW' was found but not closed.\n")
Stacktrace:
  [1] parser_exception
    @ ~/.julia/packages/FranklinParser/nI6nq/src/utils/errors.jl:6 [inlined]
  [2] _find_blocks!(blocks::Vector{FranklinParser.Block}, tokens::SubArray{FranklinParser.Token, 1, Vector{FranklinParser.Token}, Tuple{UnitRange{Int64}}, true}, templates::OrderedCollections.LittleDict{Symbol, FranklinParser.BlockTemplate, Vector{Symbol}, Vector{FranklinParser.BlockTemplate}}, is_active::Vector{Bool}; process_linereturn::Bool)
    @ FranklinParser ~/.julia/packages/FranklinParser/nI6nq/src/blocks/find_blocks.jl:148
  [3] _find_blocks!
    @ ~/.julia/packages/FranklinParser/nI6nq/src/blocks/find_blocks.jl:98 [inlined]
  [4] find_blocks(tokens::SubArray{FranklinParser.Token, 1, Vector{FranklinParser.Token}, Tuple{UnitRange{Int64}}, true}; is_md::Bool)
    @ FranklinParser ~/.julia/packages/FranklinParser/nI6nq/src/blocks/find_blocks.jl:21
  [5] (::FranklinParser.var"#66#67")(t::Vector{FranklinParser.Token})
    @ FranklinParser ~/.julia/packages/FranklinParser/nI6nq/src/partition.jl:108
  [6] partition(s::SubString{String}, tokenizer::FranklinParser.var"#64#65"{OrderedCollections.LittleDict{Char, Vector{Pair{FranklinParser.TokenFinder, Symbol}}, Vector{Char}, Vector{Vector{Pair{FranklinParser.TokenFinder, Symbol}}}}}, blockifier::FranklinParser.var"#66#67"; tokens::SubArray{FranklinParser.Token, 1, Vector{FranklinParser.Token}, Tuple{UnitRange{Int64}}, true}, disable::Vector{Symbol}, postproc::typeof(FranklinParser.default_md_postproc!))
    @ FranklinParser ~/.julia/packages/FranklinParser/nI6nq/src/partition.jl:40
  [7] #md_partition#70
    @ ~/.julia/packages/FranklinParser/nI6nq/src/partition.jl:111 [inlined]
  [8] md_partition
    @ ~/.julia/packages/FranklinParser/nI6nq/src/partition.jl:111 [inlined]
  [9] convert_md(md::SubString{String}, c::Xranklin.LocalContext; tohtml::Bool, nop::Bool, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/convert/markdown/md_core.jl:62
 [10] convert_md
    @ ~/.julia/packages/Xranklin/xncHr/src/convert/markdown/md_core.jl:51 [inlined]
 [11] #html#109
    @ ~/.julia/packages/Xranklin/xncHr/src/convert/markdown/md_core.jl:126 [inlined]
 [12] html
    @ ~/.julia/packages/Xranklin/xncHr/src/convert/markdown/md_core.jl:126 [inlined]
 [13] #html#110
    @ ~/.julia/packages/Xranklin/xncHr/src/convert/markdown/md_core.jl:130 [inlined]
 [14] html
    @ ~/.julia/packages/Xranklin/xncHr/src/convert/markdown/md_core.jl:130 [inlined]
 [15] _process_md_file_html(lc::Xranklin.LocalContext, page_content_md::String; skip::Bool)
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/process/md.jl:405
 [16] process_md_file_io!(io::IOBuffer, gc::Xranklin.GlobalContext{Xranklin.LocalContext}, fpath::String; opath::String, initial_pass::Bool, tohtml::Bool)
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/process/md.jl:331
 [17] process_md_file(gc::Xranklin.GlobalContext{Xranklin.LocalContext}, fpath::String, opath::String; initial_pass::Bool, kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/process/md.jl:60
 [18] process_file(gc::Xranklin.GlobalContext{Xranklin.LocalContext}, fpair::Pair{String, String}, case::Symbol, t::Float64; skip_files::Vector{Pair{String, String}}, initial_pass::Bool, final::Bool, reproc::Bool)
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/process/process.jl:68
 [19] process_file(gc::Xranklin.GlobalContext{Xranklin.LocalContext}, fpair::Pair{String, String}, case::Symbol, t::Float64)
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/process/process.jl:35
 [20] build_loop(cycle_counter::Int64, #unused#::LiveServer.SimpleWatcher, watched_files::OrderedCollections.LittleDict{Symbol, OrderedCollections.LittleDict{Pair{String, String}, Float64}, Vector{Symbol}, Vector{OrderedCollections.LittleDict{Pair{String, String}, Float64}}})
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/build/build_loop.jl:111
 [21] #239
    @ ~/.julia/packages/Xranklin/xncHr/src/build/serve.jl:124 [inlined]
 [22] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, verbose::Bool, coreloopfun::Xranklin.var"#239#240"{OrderedCollections.LittleDict{Symbol, OrderedCollections.LittleDict{Pair{String, String}, Float64}, Vector{Symbol}, Vector{OrderedCollections.LittleDict{Pair{String, String}, Float64}}}}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
    @ LiveServer ~/.julia/packages/LiveServer/srpPq/src/server.jl:326
 [23] serve(d::String; dir::String, folder::String, clear::Bool, final::Bool, single::Bool, prepath::String, prefix::String, base_url_prefix::String, debug::Bool, cleanup::Bool, port::Int64, host::String, launch::Bool)
    @ Xranklin ~/.julia/packages/Xranklin/xncHr/src/build/serve.jl:126
 [24] serve (repeats 2 times)
    @ ~/.julia/packages/Xranklin/xncHr/src/build/serve.jl:63 [inlined]
 [25] top-level scope
    @ none:1
make: *** [develop] Error 1

The error message did not contain the line number from the source markdown file.

The issue was I had What??? in my markdown file, and I believe ??? is the beginning of a code block in this version of Franklin (?). This took me a long time to find because every time I had to restart the serve function, and that just takes a really long time for the repository I'm currently working on. I had to create a test site and copy over the markdown file into there to debug this.

This would have been a lot easier if the parser exception returns the line number from the source which I guess requires a source map.

Related feature request:

I'd like to serve just one file while developing. The serve process takes upwards of 15 minutes for me with all the other libraries and code block executions it has to do in other files, and restarting the server is an expensive operation and when exceptions occur it completely breaks by workflow.

Currently, my plan to deal with this is delete the files I'm not developing, and once I'm ready to deploy, git restore the files and then make a push. I'm pretty sure colleagues of mine who are not super familiar with git will not do this.

It would be nice if there was a way to run serve(ignore="completed-*.md") or serve(only="filename.md").

tlienart commented 2 years ago

I'll need to come back to this to reply in full but in short:

Modifications to Utils is one such reason as it can affect the whole site. You can help indicate whether pieces of code should not be reevaluated by marking them as fully independent from the test using the # indep marker in the code block, this will allow Xranklin to skip the code if it has a code representation already available and the code hasn't changed.

So in a REPL, you should very seldom have to pay for the full build price unless you're changing utils all the time.

Btw I recommend you call it from a REPL and not using Julia -e otherwise when it does crash you not only get kicked out of the server but you also lose all the hot loading of packages.

kdheepak commented 2 years ago

Thanks for the quick reply. I didn't realize I should be starting it from the REPL. That helps, I'll do that going forward.

Definitely what's possible is to give the surrounding string which helps locate the source

I think this would have been more than sufficient to help me find the issue, and I imagine it'll be as useful as having a line number in the error message.

kdheepak commented 2 years ago

Screen Shot 2022-03-09 at 1 31 47 AM

I think similarly it would be nice if the error message here showed the output of the codeblock, or a more detailed stacktrace. Maybe this is not as important since this is only an issue when issue unnamed code blocks i.e. ```! and one can just look at the output in the rendered page to see which block errored.

tlienart commented 2 years ago

what would you have liked to see here instead of this? (and maybe could you open this in a separate issue?)

Stacktrace handling is not the easiest, but I was under the impression this is ok, the warning in the REPL might alert the user and the full message is displayed in stderr on the webpage?

kdheepak commented 2 years ago

The only times this has been a problem is when I run the file for the first time and there’s lots of code execution blocks, so it takes a while. And I know there’s an error but don’t know where so I have to go hunting. Once live reload works, it’s painless to open the browser to see the error.

Maybe the best thing is to just add a warning message saying the error / stacktrace is on the page.