Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
405 changes: 405 additions & 0 deletions claude-notes/plans/2026-09-10-frontmatter-delimiter-split-gh671.md

Large diffs are not rendered by default.

2,636 changes: 1,318 additions & 1,318 deletions crates/pampa/resources/error-corpus/_autogen-table.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/pampa/snapshots/json/002.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
source: crates/pampa/tests/integration/test.rs
expression: output
---
{"blocks":[{"a":{"classes":[1],"id":null,"kvs":[]},"c":[["",["hello"],[]],[]],"s":0,"t":"Div"}],"meta":{"nested":{"c":[{"c":"meta","s":7,"t":"Str"}],"s":4,"t":"MetaInlines"},"title":{"c":[{"c":"metadata1","s":13,"t":"Str"}],"s":10,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,20,24,25,35,36,40,53,57,58,62],"name":"tests/snapshots/json/002.qmd","total_length":63}],"metaTopLevelKeySources":{"nested":15,"title":17},"p":[{"d":0,"r":[26,63],"t":0},{"d":0,"r":[30,35],"t":0},{"d":0,"r":[37,58],"t":0},{"d":2,"r":[4,16],"t":1},{"d":3,"r":[8,12],"t":1},{"d":2,"r":[4,16],"t":1},{"d":5,"r":[8,12],"t":1},{"d":6,"r":[0,4],"t":1},{"d":0,"r":[0,25],"t":0},{"d":8,"r":[4,20],"t":1},{"d":9,"r":[7,16],"t":1},{"d":8,"r":[4,20],"t":1},{"d":11,"r":[7,16],"t":1},{"d":12,"r":[0,9],"t":1},{"d":2,"r":[4,16],"t":1},{"d":14,"r":[0,6],"t":1},{"d":8,"r":[4,20],"t":1},{"d":16,"r":[0,5],"t":1}]}}
{"blocks":[{"a":{"classes":[1],"id":null,"kvs":[]},"c":[["",["hello"],[]],[]],"s":0,"t":"Div"}],"meta":{"nested":{"c":[{"c":"meta","s":6,"t":"Str"}],"s":3,"t":"MetaInlines"},"title":{"c":[{"c":"metadata1","s":11,"t":"Str"}],"s":8,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,20,24,25,35,36,40,53,57,58,62],"name":"tests/snapshots/json/002.qmd","total_length":63}],"metaTopLevelKeySources":{"nested":13,"title":15},"p":[{"d":0,"r":[26,63],"t":0},{"d":0,"r":[30,35],"t":0},{"d":0,"r":[41,54],"t":0},{"d":2,"r":[8,12],"t":1},{"d":0,"r":[41,54],"t":0},{"d":4,"r":[8,12],"t":1},{"d":5,"r":[0,4],"t":1},{"d":0,"r":[4,21],"t":0},{"d":7,"r":[7,16],"t":1},{"d":0,"r":[4,21],"t":0},{"d":9,"r":[7,16],"t":1},{"d":10,"r":[0,9],"t":1},{"d":0,"r":[41,54],"t":0},{"d":12,"r":[0,6],"t":1},{"d":0,"r":[4,21],"t":0},{"d":14,"r":[0,5],"t":1}]}}
2 changes: 1 addition & 1 deletion crates/pampa/snapshots/json/003.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
source: crates/pampa/tests/integration/test.rs
expression: output
---
{"blocks":[{"a":{"classes":[1],"id":null,"kvs":[]},"c":[["",["hello"],[]],[{"c":{"c":[{"key":"_scope","key_source":6,"value":{"c":[{"c":"lexical","s":11,"t":"Str"}],"s":8,"t":"MetaInlines"}},{"key":"nested","key_source":13,"value":{"c":[{"c":"meta","s":18,"t":"Str"}],"s":15,"t":"MetaInlines"}}],"s":4,"t":"MetaMap"},"s":2,"t":"BlockMetadata"}]],"s":0,"t":"Div"}],"meta":{"title":{"c":[{"c":"metadata1","s":24,"t":"Str"}],"s":21,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,20,24,25,35,36,40,56,69,73,74,78],"name":"tests/snapshots/json/003.qmd","total_length":79}],"metaTopLevelKeySources":{"title":26},"p":[{"d":0,"r":[26,79],"t":0},{"d":0,"r":[30,35],"t":0},{"d":0,"r":[37,74],"t":0},{"d":0,"r":[37,74],"t":0},{"d":3,"r":[4,32],"t":1},{"d":3,"r":[4,32],"t":1},{"d":5,"r":[0,6],"t":1},{"d":3,"r":[4,32],"t":1},{"d":7,"r":[8,15],"t":1},{"d":3,"r":[4,32],"t":1},{"d":9,"r":[8,15],"t":1},{"d":10,"r":[0,7],"t":1},{"d":3,"r":[4,32],"t":1},{"d":12,"r":[16,22],"t":1},{"d":3,"r":[4,32],"t":1},{"d":14,"r":[24,28],"t":1},{"d":3,"r":[4,32],"t":1},{"d":16,"r":[24,28],"t":1},{"d":17,"r":[0,4],"t":1},{"d":0,"r":[0,25],"t":0},{"d":19,"r":[4,20],"t":1},{"d":20,"r":[7,16],"t":1},{"d":19,"r":[4,20],"t":1},{"d":22,"r":[7,16],"t":1},{"d":23,"r":[0,9],"t":1},{"d":19,"r":[4,20],"t":1},{"d":25,"r":[0,5],"t":1}]}}
{"blocks":[{"a":{"classes":[1],"id":null,"kvs":[]},"c":[["",["hello"],[]],[{"c":{"c":[{"key":"_scope","key_source":5,"value":{"c":[{"c":"lexical","s":10,"t":"Str"}],"s":7,"t":"MetaInlines"}},{"key":"nested","key_source":12,"value":{"c":[{"c":"meta","s":17,"t":"Str"}],"s":14,"t":"MetaInlines"}}],"s":3,"t":"MetaMap"},"s":2,"t":"BlockMetadata"}]],"s":0,"t":"Div"}],"meta":{"title":{"c":[{"c":"metadata1","s":22,"t":"Str"}],"s":19,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,20,24,25,35,36,40,56,69,73,74,78],"name":"tests/snapshots/json/003.qmd","total_length":79}],"metaTopLevelKeySources":{"title":24},"p":[{"d":0,"r":[26,79],"t":0},{"d":0,"r":[30,35],"t":0},{"d":0,"r":[41,70],"t":0},{"d":0,"r":[41,70],"t":0},{"d":0,"r":[41,70],"t":0},{"d":4,"r":[0,6],"t":1},{"d":0,"r":[41,70],"t":0},{"d":6,"r":[8,15],"t":1},{"d":0,"r":[41,70],"t":0},{"d":8,"r":[8,15],"t":1},{"d":9,"r":[0,7],"t":1},{"d":0,"r":[41,70],"t":0},{"d":11,"r":[16,22],"t":1},{"d":0,"r":[41,70],"t":0},{"d":13,"r":[24,28],"t":1},{"d":0,"r":[41,70],"t":0},{"d":15,"r":[24,28],"t":1},{"d":16,"r":[0,4],"t":1},{"d":0,"r":[4,21],"t":0},{"d":18,"r":[7,16],"t":1},{"d":0,"r":[4,21],"t":0},{"d":20,"r":[7,16],"t":1},{"d":21,"r":[0,9],"t":1},{"d":0,"r":[4,21],"t":0},{"d":23,"r":[0,5],"t":1}]}}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
source: crates/pampa/tests/integration/test.rs
expression: output
---
{"blocks":[{"c":[{"c":"Content","s":1,"t":"Str"},{"s":2,"t":"Space"},{"c":"paragraph","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"after","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"metadata.","s":7,"t":"Str"}],"s":0,"t":"Para"},{"s":8,"t":"HorizontalRule"},{"c":[{"c":"Second","s":10,"t":"Str"},{"s":11,"t":"Space"},{"c":"paragraph","s":12,"t":"Str"},{"s":13,"t":"Space"},{"c":"after","s":14,"t":"Str"},{"s":15,"t":"Space"},{"c":"horizontal","s":16,"t":"Str"},{"s":17,"t":"Space"},{"c":"rule.","s":18,"t":"Str"}],"s":9,"t":"Para"}],"meta":{"author":{"c":[{"c":"Test","s":24,"t":"Str"},{"s":26,"t":"Space"},{"c":"Author","s":28,"t":"Str"}],"s":21,"t":"MetaInlines"},"title":{"c":[{"c":"Test","s":33,"t":"Str"},{"s":35,"t":"Space"},{"c":"Document","s":37,"t":"Str"}],"s":30,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,24,44,48,49,83,84,88,89,129],"name":"tests/snapshots/json/horizontal-rules-vs-metadata.qmd","total_length":130}],"metaTopLevelKeySources":{"author":39,"title":41},"p":[{"d":0,"r":[50,84],"t":0},{"d":0,"r":[50,57],"t":0},{"d":0,"r":[57,58],"t":0},{"d":0,"r":[58,67],"t":0},{"d":0,"r":[67,68],"t":0},{"d":0,"r":[68,73],"t":0},{"d":0,"r":[73,74],"t":0},{"d":0,"r":[74,83],"t":0},{"d":0,"r":[85,89],"t":0},{"d":0,"r":[90,130],"t":0},{"d":0,"r":[90,96],"t":0},{"d":0,"r":[96,97],"t":0},{"d":0,"r":[97,106],"t":0},{"d":0,"r":[106,107],"t":0},{"d":0,"r":[107,112],"t":0},{"d":0,"r":[112,113],"t":0},{"d":0,"r":[113,123],"t":0},{"d":0,"r":[123,124],"t":0},{"d":0,"r":[124,129],"t":0},{"d":0,"r":[0,49],"t":0},{"d":19,"r":[4,44],"t":1},{"d":20,"r":[29,40],"t":1},{"d":19,"r":[4,44],"t":1},{"d":22,"r":[29,40],"t":1},{"d":23,"r":[0,4],"t":1},{"d":22,"r":[29,40],"t":1},{"d":25,"r":[4,5],"t":1},{"d":22,"r":[29,40],"t":1},{"d":27,"r":[5,11],"t":1},{"d":19,"r":[4,44],"t":1},{"d":29,"r":[7,20],"t":1},{"d":19,"r":[4,44],"t":1},{"d":31,"r":[7,20],"t":1},{"d":32,"r":[0,4],"t":1},{"d":31,"r":[7,20],"t":1},{"d":34,"r":[4,5],"t":1},{"d":31,"r":[7,20],"t":1},{"d":36,"r":[5,13],"t":1},{"d":19,"r":[4,44],"t":1},{"d":38,"r":[21,27],"t":1},{"d":19,"r":[4,44],"t":1},{"d":40,"r":[0,5],"t":1}]}}
{"blocks":[{"c":[{"c":"Content","s":1,"t":"Str"},{"s":2,"t":"Space"},{"c":"paragraph","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"after","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"metadata.","s":7,"t":"Str"}],"s":0,"t":"Para"},{"s":8,"t":"HorizontalRule"},{"c":[{"c":"Second","s":10,"t":"Str"},{"s":11,"t":"Space"},{"c":"paragraph","s":12,"t":"Str"},{"s":13,"t":"Space"},{"c":"after","s":14,"t":"Str"},{"s":15,"t":"Space"},{"c":"horizontal","s":16,"t":"Str"},{"s":17,"t":"Space"},{"c":"rule.","s":18,"t":"Str"}],"s":9,"t":"Para"}],"meta":{"author":{"c":[{"c":"Test","s":23,"t":"Str"},{"s":25,"t":"Space"},{"c":"Author","s":27,"t":"Str"}],"s":20,"t":"MetaInlines"},"title":{"c":[{"c":"Test","s":32,"t":"Str"},{"s":34,"t":"Space"},{"c":"Document","s":36,"t":"Str"}],"s":29,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,24,44,48,49,83,84,88,89,129],"name":"tests/snapshots/json/horizontal-rules-vs-metadata.qmd","total_length":130}],"metaTopLevelKeySources":{"author":38,"title":40},"p":[{"d":0,"r":[50,84],"t":0},{"d":0,"r":[50,57],"t":0},{"d":0,"r":[57,58],"t":0},{"d":0,"r":[58,67],"t":0},{"d":0,"r":[67,68],"t":0},{"d":0,"r":[68,73],"t":0},{"d":0,"r":[73,74],"t":0},{"d":0,"r":[74,83],"t":0},{"d":0,"r":[85,89],"t":0},{"d":0,"r":[90,130],"t":0},{"d":0,"r":[90,96],"t":0},{"d":0,"r":[96,97],"t":0},{"d":0,"r":[97,106],"t":0},{"d":0,"r":[106,107],"t":0},{"d":0,"r":[107,112],"t":0},{"d":0,"r":[112,113],"t":0},{"d":0,"r":[113,123],"t":0},{"d":0,"r":[123,124],"t":0},{"d":0,"r":[124,129],"t":0},{"d":0,"r":[4,45],"t":0},{"d":19,"r":[29,40],"t":1},{"d":0,"r":[4,45],"t":0},{"d":21,"r":[29,40],"t":1},{"d":22,"r":[0,4],"t":1},{"d":21,"r":[29,40],"t":1},{"d":24,"r":[4,5],"t":1},{"d":21,"r":[29,40],"t":1},{"d":26,"r":[5,11],"t":1},{"d":0,"r":[4,45],"t":0},{"d":28,"r":[7,20],"t":1},{"d":0,"r":[4,45],"t":0},{"d":30,"r":[7,20],"t":1},{"d":31,"r":[0,4],"t":1},{"d":30,"r":[7,20],"t":1},{"d":33,"r":[4,5],"t":1},{"d":30,"r":[7,20],"t":1},{"d":35,"r":[5,13],"t":1},{"d":0,"r":[4,45],"t":0},{"d":37,"r":[21,27],"t":1},{"d":0,"r":[4,45],"t":0},{"d":39,"r":[0,5],"t":1}]}}
2 changes: 1 addition & 1 deletion crates/pampa/snapshots/json/yaml-tags.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
source: crates/pampa/tests/integration/test.rs
expression: output
---
{"blocks":[{"c":[{"c":"This","s":1,"t":"Str"},{"s":2,"t":"Space"},{"c":"document","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"demonstrates","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"YAML","s":7,"t":"Str"},{"s":8,"t":"Space"},{"c":"tagged","s":9,"t":"Str"},{"s":10,"t":"Space"},{"c":"values.","s":11,"t":"Str"}],"s":0,"t":"Para"}],"meta":{"compute":{"c":[{"a":{"classes":[],"id":null,"kvs":[]},"c":[["",["yaml-tagged-string"],[["tag","expr"]]],[{"c":"x + 1","s":16,"t":"Str"}]],"s":15,"t":"Span"}],"s":14,"t":"MetaInlines"},"date":{"c":[{"a":{"classes":[],"id":null,"kvs":[]},"c":[["",["yaml-tagged-string"],[["tag","date"]]],[{"c":"2024-01-15","s":20,"t":"Str"}]],"s":19,"t":"Span"}],"s":18,"t":"MetaInlines"},"path":{"c":[{"c":"/usr/local/bin","s":23,"t":"Str"}],"s":22,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,24,51,74,78,79,126],"name":"tests/snapshots/json/yaml-tags.qmd","total_length":127}],"metaTopLevelKeySources":{"compute":25,"date":27,"path":29},"p":[{"d":0,"r":[80,127],"t":0},{"d":0,"r":[80,84],"t":0},{"d":0,"r":[84,85],"t":0},{"d":0,"r":[85,93],"t":0},{"d":0,"r":[93,94],"t":0},{"d":0,"r":[94,106],"t":0},{"d":0,"r":[106,107],"t":0},{"d":0,"r":[107,111],"t":0},{"d":0,"r":[111,112],"t":0},{"d":0,"r":[112,118],"t":0},{"d":0,"r":[118,119],"t":0},{"d":0,"r":[119,126],"t":0},{"d":0,"r":[0,79],"t":0},{"d":12,"r":[4,74],"t":1},{"d":13,"r":[15,20],"t":1},{"d":13,"r":[15,20],"t":1},{"d":13,"r":[15,20],"t":1},{"d":12,"r":[4,74],"t":1},{"d":17,"r":[60,70],"t":1},{"d":17,"r":[60,70],"t":1},{"d":17,"r":[60,70],"t":1},{"d":12,"r":[4,74],"t":1},{"d":21,"r":[33,47],"t":1},{"d":21,"r":[33,47],"t":1},{"d":12,"r":[4,74],"t":1},{"d":24,"r":[0,7],"t":1},{"d":12,"r":[4,74],"t":1},{"d":26,"r":[48,52],"t":1},{"d":12,"r":[4,74],"t":1},{"d":28,"r":[21,25],"t":1}]}}
{"blocks":[{"c":[{"c":"This","s":1,"t":"Str"},{"s":2,"t":"Space"},{"c":"document","s":3,"t":"Str"},{"s":4,"t":"Space"},{"c":"demonstrates","s":5,"t":"Str"},{"s":6,"t":"Space"},{"c":"YAML","s":7,"t":"Str"},{"s":8,"t":"Space"},{"c":"tagged","s":9,"t":"Str"},{"s":10,"t":"Space"},{"c":"values.","s":11,"t":"Str"}],"s":0,"t":"Para"}],"meta":{"compute":{"c":[{"a":{"classes":[],"id":null,"kvs":[]},"c":[["",["yaml-tagged-string"],[["tag","expr"]]],[{"c":"x + 1","s":15,"t":"Str"}]],"s":14,"t":"Span"}],"s":13,"t":"MetaInlines"},"date":{"c":[{"a":{"classes":[],"id":null,"kvs":[]},"c":[["",["yaml-tagged-string"],[["tag","date"]]],[{"c":"2024-01-15","s":19,"t":"Str"}]],"s":18,"t":"Span"}],"s":17,"t":"MetaInlines"},"path":{"c":[{"c":"/usr/local/bin","s":22,"t":"Str"}],"s":21,"t":"MetaInlines"}},"pandoc-api-version":[1,23,1],"astContext":{"files":[{"line_breaks":[3,24,51,74,78,79,126],"name":"tests/snapshots/json/yaml-tags.qmd","total_length":127}],"metaTopLevelKeySources":{"compute":24,"date":26,"path":28},"p":[{"d":0,"r":[80,127],"t":0},{"d":0,"r":[80,84],"t":0},{"d":0,"r":[84,85],"t":0},{"d":0,"r":[85,93],"t":0},{"d":0,"r":[93,94],"t":0},{"d":0,"r":[94,106],"t":0},{"d":0,"r":[106,107],"t":0},{"d":0,"r":[107,111],"t":0},{"d":0,"r":[111,112],"t":0},{"d":0,"r":[112,118],"t":0},{"d":0,"r":[118,119],"t":0},{"d":0,"r":[119,126],"t":0},{"d":0,"r":[4,75],"t":0},{"d":12,"r":[15,20],"t":1},{"d":12,"r":[15,20],"t":1},{"d":12,"r":[15,20],"t":1},{"d":0,"r":[4,75],"t":0},{"d":16,"r":[60,70],"t":1},{"d":16,"r":[60,70],"t":1},{"d":16,"r":[60,70],"t":1},{"d":0,"r":[4,75],"t":0},{"d":20,"r":[33,47],"t":1},{"d":20,"r":[33,47],"t":1},{"d":0,"r":[4,75],"t":0},{"d":23,"r":[0,7],"t":1},{"d":0,"r":[4,75],"t":0},{"d":25,"r":[48,52],"t":1},{"d":0,"r":[4,75],"t":0},{"d":27,"r":[21,25],"t":1}]}}
66 changes: 12 additions & 54 deletions crates/pampa/src/pandoc/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,16 +564,14 @@ fn content_provenance_desync_warning(
.build()
}

fn extract_between_delimiters(input: &str) -> Option<&str> {
let parts: Vec<&str> = input.split("---").collect();
if parts.len() >= 3 {
Some(parts[1].trim())
} else {
None
}
}

/// Convert RawBlock to ConfigValue using unified conversion.
/// Convert a `quarto_minus_metadata` RawBlock to ConfigValue using unified conversion.
///
/// The block's `text` is the YAML between the `---` delimiters — exactly the
/// grammar's `yaml` body node, delimiter lines excluded — and its
/// `source_info` covers that same range, so YAML offsets map onto the source
/// with no re-scanning here. (The reader once located the body itself by
/// splitting the raw node text on `---`, which cut the YAML at the first
/// `---` inside a value: bd-mjo6ao32, GH #671.)
///
/// This function:
/// 1. Preserves source location information
Expand All @@ -582,8 +580,7 @@ fn extract_between_delimiters(input: &str) -> Option<&str> {
///
/// # Panics
///
/// Panics if the RawBlock format is not "quarto_minus_metadata" or if YAML parsing fails.
/// These should be replaced with proper error handling in production.
/// Panics if the RawBlock format is not "quarto_minus_metadata".
pub fn rawblock_to_config_value(
block: &RawBlock,
diagnostics: &mut crate::utils::diagnostic_collector::DiagnosticCollector,
Expand All @@ -595,23 +592,11 @@ pub fn rawblock_to_config_value(
);
}

// Extract YAML content between --- delimiters
let content = extract_between_delimiters(&block.text).unwrap();

// Calculate offsets within RawBlock.text
// Find the actual position of the trimmed content in the original text
// extract_between_delimiters trims the content, so we need to find where it actually starts
let yaml_start = block.text.find(content).unwrap();

// block.source_info is already quarto_source_map::SourceInfo
let parent = block.source_info.clone();

// Create Substring SourceInfo for the YAML content within the RawBlock
let yaml_parent =
quarto_source_map::SourceInfo::substring(parent, yaml_start, yaml_start + content.len());
// The block's source_info already spans exactly the YAML text.
let yaml_parent = block.source_info.clone();

// Parse YAML with source tracking
let yaml = match quarto_yaml::parse_with_parent(content, yaml_parent.clone()) {
let yaml = match quarto_yaml::parse_with_parent(&block.text, yaml_parent.clone()) {
Ok(y) => y,
Err(e) => {
// Report the YAML parse error as a diagnostic
Expand Down Expand Up @@ -651,33 +636,6 @@ mod tests {
quarto_source_map::SourceInfo::for_test()
}

#[test]
fn test_extract_between_delimiters_valid() {
let input = "---\ntitle: Test\n---";
let result = extract_between_delimiters(input);
assert_eq!(result, Some("title: Test"));
}

#[test]
fn test_extract_between_delimiters_with_extra_content() {
let input = "---\ntitle: Test\n---\nBody text";
let result = extract_between_delimiters(input);
assert_eq!(result, Some("title: Test"));
}

#[test]
fn test_extract_between_delimiters_missing_delimiters() {
// Only one delimiter
let input = "---\ntitle: Test";
let result = extract_between_delimiters(input);
assert_eq!(result, None);

// No delimiters
let input = "title: Test";
let result = extract_between_delimiters(input);
assert_eq!(result, None);
}

#[test]
fn test_yaml_to_config_value_string_document_metadata() {
// .with_content_provenance(si()): a hand-built node derives no
Expand Down
25 changes: 20 additions & 5 deletions crates/pampa/src/pandoc/treesitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,12 +338,15 @@ fn process_list_item(
// the item contains a heading (e.g. `* # Section 1`). Flatten the
// section's blocks into the item, mirroring `process_section`.
PandocNativeIntermediate::IntermediateSection(section) => blocks.extend(section),
PandocNativeIntermediate::IntermediateMetadataString(text, _range) => {
PandocNativeIntermediate::IntermediateMetadataString(text, range) => {
// for now we assume it's metadata and emit it as a rawblock
blocks.push(Block::RawBlock(RawBlock {
format: "quarto_minus_metadata".to_string(),
text,
source_info: node_source_info_with_context(list_item_node, context),
source_info: quarto_source_map::SourceInfo::from_range(
context.current_file_id(),
range,
),
}));
}
// Tokens with no AST-level meaning: tree-sitter ERROR nodes from
Expand Down Expand Up @@ -699,9 +702,21 @@ fn native_visitor<T: Write>(
"fenced_div_note_id" => create_base_text_from_node_text(node, input_bytes),
"document" => process_document(node, children, context),
"metadata" => {
// Extract YAML frontmatter text
let text = node.utf8_text(input_bytes).unwrap().to_string();
PandocNativeIntermediate::IntermediateMetadataString(text, node_location(node))
// The grammar exposes the YAML between the `---` delimiters as
// the `body` field (a `yaml` node), so the text and range come
// straight from the parse — never re-derived from the node text,
// which is how a `---` inside a value once truncated the
// metadata (bd-mjo6ao32, GH #671). A `metadata` node without a
// body only arises from error recovery; the parse error is
// already reported, so treat it as an empty block.
let (text, range) = match node.child_by_field_name("body") {
Some(body) => (
body.utf8_text(input_bytes).unwrap().to_string(),
node_location(&body),
),
None => (String::new(), node_location(node)),
};
PandocNativeIntermediate::IntermediateMetadataString(text, range)
}
"section" => process_section(node, children, context),
"pandoc_paragraph" => process_paragraph(node, children, input_bytes, context),
Expand Down
1 change: 1 addition & 0 deletions crates/pampa/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub mod test_emphasis_opening_mark;
pub mod test_error_corpus;
pub mod test_fenced_div_no_space;
pub mod test_figure_figcaption_synthesis;
pub mod test_frontmatter_delimiters;
pub mod test_grid_table_error;
pub mod test_hard_soft_break;
pub mod test_heading_auto_id;
Expand Down
Loading
Loading