From 02c09b5682d1847fe0e7fbcc0761a47a994b5cc5 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 22 Sep 2025 13:45:26 +0200 Subject: [PATCH 1/4] send source file ranges over to the julia engine --- src/execute/julia.ts | 86 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/src/execute/julia.ts b/src/execute/julia.ts index a852b6e6e0c..e92a87a3649 100644 --- a/src/execute/julia.ts +++ b/src/execute/julia.ts @@ -3,7 +3,11 @@ import { join } from "../deno_ral/path.ts"; import { MappedString, mappedStringFromFile } from "../core/mapped-text.ts"; import { partitionMarkdown } from "../core/pandoc/pandoc-partition.ts"; import { readYamlFromMarkdown } from "../core/yaml.ts"; -import { asMappedString } from "../core/lib/mapped-text.ts"; +import { + asMappedString, + mappedIndexToLineCol, + mappedLines, +} from "../core/lib/mapped-text.ts"; import { ProjectContext } from "../project/types.ts"; import { DependenciesOptions, @@ -53,6 +57,12 @@ import { markdownFromJupyterPercentScript, } from "./jupyter/percent.ts"; +export interface SourceRange { + lines: [number, number]; + file?: string; + sourceLines?: [number, number]; +} + export interface JuliaExecuteOptions extends ExecuteOptions { oneShot: boolean; // if true, the file's worker process is closed before and after running } @@ -578,6 +588,62 @@ function getConsoleColumns(): number | null { } } +function buildSourceRanges(markdown: MappedString): Array { + const lines = mappedLines(markdown); + const sourceRanges: Array = []; + let currentRange: SourceRange | null = null; + + lines.forEach((line, index) => { + // Get mapping info directly from the line's MappedString + const mapResult = line.map(0, true); + if (mapResult) { + const { originalString } = mapResult; + const lineColFunc = mappedIndexToLineCol(originalString); + const lineCol = lineColFunc(mapResult.index); + const fileName = originalString.fileName; + const sourceLineNum = lineCol.line; + + // Check if this line continues the current range + if ( + currentRange && + currentRange.file === fileName && + fileName !== undefined && + currentRange.sourceLines && + currentRange.sourceLines[1] === sourceLineNum + ) { + // Extend current range + currentRange.lines[1] = index + 1; // +1 because lines are 1-indexed + currentRange.sourceLines[1] = sourceLineNum + 1; + } else { + // Start new range + if (currentRange) { + sourceRanges.push(currentRange); + } + currentRange = { + lines: [index + 1, index + 1], // +1 because lines are 1-indexed + }; + if (fileName !== undefined) { + currentRange.file = fileName; + currentRange.sourceLines = [sourceLineNum + 1, sourceLineNum + 1]; + } + } + } else { + // No mapping available - treat as separate range + if (currentRange) { + sourceRanges.push(currentRange); + currentRange = null; + } + } + }); + + // Don't forget the last range + if (currentRange) { + sourceRanges.push(currentRange); + } + + return sourceRanges; +} + async function executeJulia( options: JuliaExecuteOptions, ): Promise { @@ -600,9 +666,12 @@ async function executeJulia( ); } } + + const sourceRanges = buildSourceRanges(options.target.markdown); + const response = await writeJuliaCommand( conn, - { type: "run", content: { file, options } }, + { type: "run", content: { file, options, sourceRanges } }, transportOptions.key, options, (update: ProgressUpdate) => { @@ -643,7 +712,14 @@ interface ProgressUpdate { type empty = Record; type ServerCommand = - | { type: "run"; content: { file: string; options: JuliaExecuteOptions } } + | { + type: "run"; + content: { + file: string; + options: JuliaExecuteOptions; + sourceRanges: Array; + }; + } | { type: "close"; content: { file: string } } | { type: "forceclose"; content: { file: string } } | { type: "isopen"; content: { file: string } } @@ -854,7 +930,9 @@ function populateJuliaEngineCommand(command: Command) { "Get status information on the currently running Julia server process.", ).action(logStatus) .command("kill", "Kill server") - .description("Kill the control server if it is currently running. This will also kill all notebook worker processes.") + .description( + "Kill the control server if it is currently running. This will also kill all notebook worker processes.", + ) .action(killJuliaServer) .command("log", "Print julia server log") .description( From 9530fc1be5b2c528b22829c829e30040e850b38b Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 23 Sep 2025 11:49:26 +0200 Subject: [PATCH 2/4] always pass absolute file paths --- src/execute/julia.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/execute/julia.ts b/src/execute/julia.ts index e92a87a3649..f8af297f9bd 100644 --- a/src/execute/julia.ts +++ b/src/execute/julia.ts @@ -56,6 +56,7 @@ import { isJupyterPercentScript, markdownFromJupyterPercentScript, } from "./jupyter/percent.ts"; +import { resolve } from "path"; export interface SourceRange { lines: [number, number]; @@ -600,7 +601,9 @@ function buildSourceRanges(markdown: MappedString): Array { const { originalString } = mapResult; const lineColFunc = mappedIndexToLineCol(originalString); const lineCol = lineColFunc(mapResult.index); - const fileName = originalString.fileName; + const fileName = originalString.fileName + ? resolve(originalString.fileName) // resolve to absolute path using cwd + : undefined; const sourceLineNum = lineCol.line; // Check if this line continues the current range From 2e2ac58eb13806974d204fe132d49629af8584b8 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Wed, 24 Sep 2025 12:46:01 +0200 Subject: [PATCH 3/4] add test --- tests/docs/smoke-all/julia/_included.qmd | 3 +++ .../docs/smoke-all/julia/source-ranges-test.qmd | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/docs/smoke-all/julia/_included.qmd create mode 100644 tests/docs/smoke-all/julia/source-ranges-test.qmd diff --git a/tests/docs/smoke-all/julia/_included.qmd b/tests/docs/smoke-all/julia/_included.qmd new file mode 100644 index 00000000000..1be592fa71f --- /dev/null +++ b/tests/docs/smoke-all/julia/_included.qmd @@ -0,0 +1,3 @@ +```{julia} +"$(@__FILE__):$(@__LINE__)" +``` \ No newline at end of file diff --git a/tests/docs/smoke-all/julia/source-ranges-test.qmd b/tests/docs/smoke-all/julia/source-ranges-test.qmd new file mode 100644 index 00000000000..a1947ac784d --- /dev/null +++ b/tests/docs/smoke-all/julia/source-ranges-test.qmd @@ -0,0 +1,16 @@ +--- +title: "Test Julia source ranges with includes" +format: markdown +engine: julia +_quarto: + tests: + markdown: + ensureFileRegexMatches: + - ['source-ranges-test\.qmd:15', '_included\.qmd:2'] +--- + +{{< include _included.qmd >}} + +```{julia} +"$(@__FILE__):$(@__LINE__)" +``` From 7a7ed51bd394bf8f03ca1d7d371784521bdda8d5 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Wed, 24 Sep 2025 12:49:13 +0200 Subject: [PATCH 4/4] temporarily use dev branch for quartonotebookrunner --- src/resources/julia/Project.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/resources/julia/Project.toml b/src/resources/julia/Project.toml index 08c36b12cfb..1e6ef8fceab 100644 --- a/src/resources/julia/Project.toml +++ b/src/resources/julia/Project.toml @@ -1,5 +1,8 @@ [deps] QuartoNotebookRunner = "4c0109c6-14e9-4c88-93f0-2b974d3468f4" -[compat] -QuartoNotebookRunner = "=0.17.3" +# [compat] +# QuartoNotebookRunner = "=0.17.3" + +[sources] +QuartoNotebookRunner = {url = "https://github.com/PumasAI/QuartoNotebookRunner.jl", rev = "jk/source-ranges"}