Skip to content

Commit 369a58e

Browse files
authored
Merge pull request #13401 from PumasAI/jk/source-ranges
Send source file ranges over to the julia engine
2 parents ef529f6 + 7a7ed51 commit 369a58e

File tree

4 files changed

+109
-6
lines changed

4 files changed

+109
-6
lines changed

src/execute/julia.ts

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import { join } from "../deno_ral/path.ts";
33
import { MappedString, mappedStringFromFile } from "../core/mapped-text.ts";
44
import { partitionMarkdown } from "../core/pandoc/pandoc-partition.ts";
55
import { readYamlFromMarkdown } from "../core/yaml.ts";
6-
import { asMappedString } from "../core/lib/mapped-text.ts";
6+
import {
7+
asMappedString,
8+
mappedIndexToLineCol,
9+
mappedLines,
10+
} from "../core/lib/mapped-text.ts";
711
import { ProjectContext } from "../project/types.ts";
812
import {
913
DependenciesOptions,
@@ -52,6 +56,13 @@ import {
5256
isJupyterPercentScript,
5357
markdownFromJupyterPercentScript,
5458
} from "./jupyter/percent.ts";
59+
import { resolve } from "path";
60+
61+
export interface SourceRange {
62+
lines: [number, number];
63+
file?: string;
64+
sourceLines?: [number, number];
65+
}
5566

5667
export interface JuliaExecuteOptions extends ExecuteOptions {
5768
oneShot: boolean; // if true, the file's worker process is closed before and after running
@@ -578,6 +589,64 @@ function getConsoleColumns(): number | null {
578589
}
579590
}
580591

592+
function buildSourceRanges(markdown: MappedString): Array<SourceRange> {
593+
const lines = mappedLines(markdown);
594+
const sourceRanges: Array<SourceRange> = [];
595+
let currentRange: SourceRange | null = null;
596+
597+
lines.forEach((line, index) => {
598+
// Get mapping info directly from the line's MappedString
599+
const mapResult = line.map(0, true);
600+
if (mapResult) {
601+
const { originalString } = mapResult;
602+
const lineColFunc = mappedIndexToLineCol(originalString);
603+
const lineCol = lineColFunc(mapResult.index);
604+
const fileName = originalString.fileName
605+
? resolve(originalString.fileName) // resolve to absolute path using cwd
606+
: undefined;
607+
const sourceLineNum = lineCol.line;
608+
609+
// Check if this line continues the current range
610+
if (
611+
currentRange &&
612+
currentRange.file === fileName &&
613+
fileName !== undefined &&
614+
currentRange.sourceLines &&
615+
currentRange.sourceLines[1] === sourceLineNum
616+
) {
617+
// Extend current range
618+
currentRange.lines[1] = index + 1; // +1 because lines are 1-indexed
619+
currentRange.sourceLines[1] = sourceLineNum + 1;
620+
} else {
621+
// Start new range
622+
if (currentRange) {
623+
sourceRanges.push(currentRange);
624+
}
625+
currentRange = {
626+
lines: [index + 1, index + 1], // +1 because lines are 1-indexed
627+
};
628+
if (fileName !== undefined) {
629+
currentRange.file = fileName;
630+
currentRange.sourceLines = [sourceLineNum + 1, sourceLineNum + 1];
631+
}
632+
}
633+
} else {
634+
// No mapping available - treat as separate range
635+
if (currentRange) {
636+
sourceRanges.push(currentRange);
637+
currentRange = null;
638+
}
639+
}
640+
});
641+
642+
// Don't forget the last range
643+
if (currentRange) {
644+
sourceRanges.push(currentRange);
645+
}
646+
647+
return sourceRanges;
648+
}
649+
581650
async function executeJulia(
582651
options: JuliaExecuteOptions,
583652
): Promise<JupyterNotebook> {
@@ -600,9 +669,12 @@ async function executeJulia(
600669
);
601670
}
602671
}
672+
673+
const sourceRanges = buildSourceRanges(options.target.markdown);
674+
603675
const response = await writeJuliaCommand(
604676
conn,
605-
{ type: "run", content: { file, options } },
677+
{ type: "run", content: { file, options, sourceRanges } },
606678
transportOptions.key,
607679
options,
608680
(update: ProgressUpdate) => {
@@ -643,7 +715,14 @@ interface ProgressUpdate {
643715
type empty = Record<string | number | symbol, never>;
644716

645717
type ServerCommand =
646-
| { type: "run"; content: { file: string; options: JuliaExecuteOptions } }
718+
| {
719+
type: "run";
720+
content: {
721+
file: string;
722+
options: JuliaExecuteOptions;
723+
sourceRanges: Array<SourceRange>;
724+
};
725+
}
647726
| { type: "close"; content: { file: string } }
648727
| { type: "forceclose"; content: { file: string } }
649728
| { type: "isopen"; content: { file: string } }
@@ -854,7 +933,9 @@ function populateJuliaEngineCommand(command: Command) {
854933
"Get status information on the currently running Julia server process.",
855934
).action(logStatus)
856935
.command("kill", "Kill server")
857-
.description("Kill the control server if it is currently running. This will also kill all notebook worker processes.")
936+
.description(
937+
"Kill the control server if it is currently running. This will also kill all notebook worker processes.",
938+
)
858939
.action(killJuliaServer)
859940
.command("log", "Print julia server log")
860941
.description(

src/resources/julia/Project.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[deps]
22
QuartoNotebookRunner = "4c0109c6-14e9-4c88-93f0-2b974d3468f4"
33

4-
[compat]
5-
QuartoNotebookRunner = "=0.17.3"
4+
# [compat]
5+
# QuartoNotebookRunner = "=0.17.3"
6+
7+
[sources]
8+
QuartoNotebookRunner = {url = "https://github.com/PumasAI/QuartoNotebookRunner.jl", rev = "jk/source-ranges"}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```{julia}
2+
"$(@__FILE__):$(@__LINE__)"
3+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
title: "Test Julia source ranges with includes"
3+
format: markdown
4+
engine: julia
5+
_quarto:
6+
tests:
7+
markdown:
8+
ensureFileRegexMatches:
9+
- ['source-ranges-test\.qmd:15', '_included\.qmd:2']
10+
---
11+
12+
{{< include _included.qmd >}}
13+
14+
```{julia}
15+
"$(@__FILE__):$(@__LINE__)"
16+
```

0 commit comments

Comments
 (0)