@@ -66,6 +66,21 @@ mutable struct File
6666 end
6767end
6868
69+ struct SourceRange
70+ file:: Union{String,Nothing}
71+ lines:: UnitRange{Int}
72+ source_line:: Union{Nothing,Int} # first line in source
73+ end
74+
75+ function SourceRange (file, lines, source_lines:: UnitRange )
76+ if length (lines) != length (source_lines)
77+ error (
78+ " Mismatching lengths of lines $lines ($(length (lines)) ) and source_lines $source_lines ($(length (source_lines)) )" ,
79+ )
80+ end
81+ SourceRange (file, lines, first (source_lines))
82+ end
83+
6984function _has_juliaup ()
7085 try
7186 success (` juliaup --version` ) && success (` julia --version` )
@@ -339,13 +354,15 @@ function evaluate!(
339354 options:: Union{String,Dict{String,Any}} = Dict {String,Any} (),
340355 chunk_callback = (i, n, c) -> nothing ,
341356 markdown:: Union{String,Nothing} = nothing ,
357+ source_ranges:: Union{Nothing,Vector} = nothing ,
342358)
343359 _check_output_dst (output)
344360
345361 options = _parsed_options (options)
346362 path = abspath (f. path)
347363 if isfile (path)
348- source_code_hash, raw_chunks, file_frontmatter = raw_text_chunks (f, markdown)
364+ source_code_hash, raw_chunks, file_frontmatter =
365+ raw_text_chunks (f, markdown; source_ranges)
349366 merged_options = _extract_relevant_options (file_frontmatter, options)
350367
351368 # A change of parameter values must invalidate the source code hash.
@@ -583,9 +600,9 @@ write_json(::Nothing, data) = data
583600Return a vector of raw markdown and code chunks from `file` ready for evaluation
584601by `evaluate_raw_cells!`.
585602"""
586- raw_text_chunks (file:: File , :: Nothing ) = raw_text_chunks (file. path)
587- raw_text_chunks (file:: File , markdown:: String ) =
588- raw_markdown_chunks_from_string (file. path, markdown)
603+ raw_text_chunks (file:: File , :: Nothing ; source_ranges = nothing ) = raw_text_chunks (file. path)
604+ raw_text_chunks (file:: File , markdown:: String ; source_ranges = nothing ) =
605+ raw_markdown_chunks_from_string (file. path, markdown; source_ranges )
589606
590607function raw_text_chunks (path:: String )
591608 endswith (path, " .qmd" ) && return raw_markdown_chunks (path)
@@ -607,7 +624,27 @@ raw_markdown_chunks(path::String) =
607624
608625struct Unset end
609626
610- function raw_markdown_chunks_from_string (path:: String , markdown:: String )
627+ function compute_line_file_lookup (nlines, path, source_ranges)
628+ nlines_ranges = maximum (r -> r. lines. stop, source_ranges) # number of lines reported might be different from the markdown string due to quarto bugs
629+ lookup = fill ((; file = " unknown" , line = 0 ), nlines_ranges)
630+ for source_range in source_ranges
631+ file:: String = something (source_range. file, " unknown" )
632+ for line in source_range. lines
633+ source_line = line - first (source_range. lines) + source_range. source_line
634+ lookup[line] = (; file, line = source_line)
635+ end
636+ end
637+ return lookup
638+ end
639+ function compute_line_file_lookup (nlines, path, source_ranges:: Nothing )
640+ return [(; file = path, line) for line = 1 : nlines]
641+ end
642+
643+ function raw_markdown_chunks_from_string (
644+ path:: String ,
645+ markdown:: String ;
646+ source_ranges = nothing ,
647+ )
611648 raw_chunks = []
612649 source_code_hash = hash (VERSION )
613650 pars = Parser ()
@@ -616,6 +653,9 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
616653 source_code_hash = hash (file_fromtmatter, source_code_hash)
617654 source_lines = collect (eachline (IOBuffer (markdown)))
618655 terminal_line = 1
656+
657+ line_file_lookup = compute_line_file_lookup (length (source_lines), path, source_ranges)
658+
619659 code_cells = false
620660 for (node, enter) in ast
621661 if enter &&
@@ -625,7 +665,7 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
625665 md = join (source_lines[terminal_line: (line- 1 )], " \n " )
626666 push! (
627667 raw_chunks,
628- (type = :markdown , source = md, file = path, line = terminal_line),
668+ (; type = :markdown , source = md, line_file_lookup[ terminal_line] . .. ),
629669 )
630670 if contains (md, r" `{(?:julia|python|r)} " )
631671 source_code_hash = hash (md, source_code_hash)
@@ -636,7 +676,7 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
636676 # this option could in the future also include a vector of line numbers, which knitr supports.
637677 # all other options seem to be quarto-rendering related, like where to put figure captions etc.
638678 source = node. literal
639- cell_options = extract_cell_options (source; file = path, line = line )
679+ cell_options = extract_cell_options (source; line_file_lookup[ line] . .. )
640680 evaluate = get (cell_options, " eval" , Unset ())
641681 if ! (evaluate isa Union{Bool,Unset})
642682 error (
@@ -649,12 +689,11 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
649689 is_r_toplevel (node) ? :r : error (" Unhandled code block language" )
650690 push! (
651691 raw_chunks,
652- (
692+ (;
653693 type = :code ,
654694 language = language,
655695 source,
656- file = path,
657- line,
696+ line_file_lookup[line]. .. ,
658697 evaluate,
659698 cell_options,
660699 ),
@@ -666,7 +705,7 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
666705 md = join (source_lines[terminal_line: end ], " \n " )
667706 push! (
668707 raw_chunks,
669- (type = :markdown , source = md, file = path, line = terminal_line),
708+ (; type = :markdown , source = md, line_file_lookup[ terminal_line] . .. ),
670709 )
671710 if contains (md, r" `{(?:julia|python|r)} " )
672711 source_code_hash = hash (md, source_code_hash)
@@ -690,7 +729,7 @@ function raw_markdown_chunks_from_string(path::String, markdown::String)
690729 if raw_chunks[end ]. type == :code
691730 push! (
692731 raw_chunks,
693- (type = :markdown , source = " " , file = path, line = terminal_line),
732+ (; type = :markdown , source = " " , file = path, line = terminal_line),
694733 )
695734 end
696735
@@ -1499,6 +1538,7 @@ function run!(
14991538 showprogress:: Bool = true ,
15001539 options:: Union{String,Dict{String,Any}} = Dict {String,Any} (),
15011540 chunk_callback = (i, n, c) -> nothing ,
1541+ source_ranges:: Union{Nothing,Vector} = nothing ,
15021542)
15031543 try
15041544 borrow_file! (server, path; options, optionally_create = true ) do file
@@ -1519,7 +1559,15 @@ function run!(
15191559
15201560 result_task = Threads. @spawn begin
15211561 try
1522- evaluate! (file, output; showprogress, options, markdown, chunk_callback)
1562+ evaluate! (
1563+ file,
1564+ output;
1565+ showprogress,
1566+ options,
1567+ markdown,
1568+ chunk_callback,
1569+ source_ranges,
1570+ )
15231571 finally
15241572 put! (file. run_decision_channel, :evaluate_finished )
15251573 end
0 commit comments