Capture Fennel source line for each CI job
mlua stack inspection in the `job` UserData callback grabs the
calling line; Fennel's `correlate` opt keeps Lua and Fennel line
numbers aligned. Sets up source-span diagnostics for upcoming
per-job validation errors.
Assisted-by: Claude Opus 4.7 via Claude Code
diff --git a/src/ci/pipeline.rs b/src/ci/pipeline.rs
index c656124..4a03120 100644
--- a/src/ci/pipeline.rs
+++ b/src/ci/pipeline.rs
@@ -12,6 +12,9 @@ use crate::fennel::Fennel;
pub struct Job {
pub id: String,
pub inputs: Vec<String>,
+ /// 1-indexed line in the source where `(ci:job …)` was called.
+ /// `0` means the line could not be determined.
+ pub line: u32,
/// The job's run function from the Lua VM.
/// Stored for future execution — not yet called.
#[expect(dead_code)]
@@ -50,8 +53,18 @@ impl UserData for CiModule {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_method(
"job",
- |_, this, (id, inputs, run_fn): (String, Vec<String>, mlua::Function)| {
- this.jobs.borrow_mut().push(Job { id, inputs, run_fn });
+ |lua, this, (id, inputs, run_fn): (String, Vec<String>, mlua::Function)| {
+ let line = lua
+ .inspect_stack(1, |d| d.current_line())
+ .flatten()
+ .map(|l| l as u32)
+ .unwrap_or(0);
+ this.jobs.borrow_mut().push(Job {
+ id,
+ inputs,
+ line,
+ run_fn,
+ });
Ok(())
},
);
@@ -239,6 +252,20 @@ mod tests {
assert_eq!(jobs[1].inputs, vec!["build"]);
}
+ #[test]
+ fn load_captures_source_line() {
+ let f = fennel();
+ let source = "(local ci (require :quire.ci))
+(ci:job :first [:quire/push] (fn [_] nil))
+(ci:job :second [:quire/push] (fn [_] nil))
+
+
+(ci:job :sixth [:quire/push] (fn [_] nil))";
+ let pipeline = load(&f, source, "ci.fnl", "ci.fnl").expect("load should succeed");
+ let lines: Vec<u32> = pipeline.jobs().iter().map(|j| j.line).collect();
+ assert_eq!(lines, vec![2, 3, 6]);
+ }
+
#[test]
fn load_errors_on_bad_fennel() {
let f = fennel();
diff --git a/src/fennel.rs b/src/fennel.rs
index 162a09e..3b9e20d 100644
--- a/src/fennel.rs
+++ b/src/fennel.rs
@@ -96,6 +96,9 @@ impl Fennel {
let opts = self.lua.create_table()?;
opts.set("filename", filename)?;
+ // Align Lua line numbers with Fennel source lines so debug
+ // info points back at the user's `.fnl`.
+ opts.set("correlate", true)?;
let result = eval
.call::<mlua::Value>((source, opts))