Skip to content

Commit 2879cb4

Browse files
cscheidclaude
andcommitted
Refactor SourceInfo to use filename indices instead of strings
Changed SourceInfo to store `Option<usize>` filename indices instead of `Option<String>` filenames, with the actual filenames stored once in ASTContext. This eliminates redundant string cloning throughout the AST. ## Changes - **SourceInfo**: Changed `filename` field to `filename_index: Option<usize>` - **ASTContext**: Added `filenames: Vec<String>` to store deduplicated filenames - **SourceLocation trait**: Added lifetime-annotated `filename()` method to resolve indices ## JSON Format Location objects now use: - `"filenameIndex": 0` instead of `"filename": "path/to/file"` - Top-level `"astContext": {"filenames": [...]}` field added to store filename array ## Updated Components - JSON reader/writer to handle new format - QMD reader to return (Pandoc, ASTContext) tuple - All test files and snapshot comparisons - Test infrastructure to accumulate failures for better error reporting All tests passing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 4cd9da8 commit 2879cb4

File tree

16 files changed

+214
-193
lines changed

16 files changed

+214
-193
lines changed

crates/quarto-markdown-pandoc/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ fn main() {
102102
return;
103103
}
104104

105-
let pandoc = match args.from.as_str() {
105+
let (pandoc, context) = match args.from.as_str() {
106106
"markdown" | "qmd" => {
107107
let error_formatter = if args.json_errors {
108108
Some(
@@ -157,7 +157,7 @@ fn main() {
157157

158158
let mut buf = Vec::new();
159159
match args.to.as_str() {
160-
"json" => writers::json::write(&pandoc, &mut buf),
160+
"json" => writers::json::write(&pandoc, &context, &mut buf),
161161
"native" => writers::native::write(&pandoc, &mut buf),
162162
"markdown" | "qmd" => writers::qmd::write(&pandoc, &mut buf),
163163
_ => {

crates/quarto-markdown-pandoc/src/pandoc/location.rs

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,31 @@ pub struct Range {
2222
}
2323

2424
/// Encapsulates source location information for AST nodes
25+
/// The filename field now holds an index into the ASTContext.filenames vector
2526
#[derive(Debug, Clone, PartialEq)]
2627
pub struct SourceInfo {
27-
pub filename: Option<String>,
28+
pub filename_index: Option<usize>,
2829
pub range: Range,
2930
}
3031

3132
impl SourceInfo {
32-
pub fn new(filename: Option<String>, range: Range) -> Self {
33-
SourceInfo { filename, range }
33+
pub fn new(filename_index: Option<usize>, range: Range) -> Self {
34+
SourceInfo {
35+
filename_index,
36+
range,
37+
}
3438
}
3539

3640
pub fn with_range(range: Range) -> Self {
3741
SourceInfo {
38-
filename: None,
42+
filename_index: None,
3943
range,
4044
}
4145
}
4246

4347
pub fn combine(&self, other: &SourceInfo) -> SourceInfo {
4448
SourceInfo {
45-
filename: self.filename.clone().or(other.filename.clone()),
49+
filename_index: self.filename_index.or(other.filename_index),
4650
range: Range {
4751
start: if self.range.start < other.range.start {
4852
self.range.start.clone()
@@ -60,8 +64,14 @@ impl SourceInfo {
6064
}
6165

6266
pub trait SourceLocation {
63-
fn filename(&self) -> Option<String>;
67+
fn filename_index(&self) -> Option<usize>;
6468
fn range(&self) -> Range;
69+
70+
/// Resolve the filename from the ASTContext using the stored index
71+
fn filename<'a>(&self, context: &'a ASTContext) -> Option<&'a String> {
72+
self.filename_index()
73+
.and_then(|idx| context.filenames.get(idx))
74+
}
6575
}
6676

6777
pub fn node_location(node: &tree_sitter::Node) -> Range {
@@ -86,7 +96,13 @@ pub fn node_source_info(node: &tree_sitter::Node) -> SourceInfo {
8696
}
8797

8898
pub fn node_source_info_with_context(node: &tree_sitter::Node, context: &ASTContext) -> SourceInfo {
89-
SourceInfo::new(context.primary_filename().cloned(), node_location(node))
99+
// If the context has at least one filename, use index 0
100+
let filename_index = if context.filenames.is_empty() {
101+
None
102+
} else {
103+
Some(0)
104+
};
105+
SourceInfo::new(filename_index, node_location(node))
90106
}
91107

92108
pub fn empty_range() -> Range {
@@ -113,8 +129,8 @@ macro_rules! impl_source_location {
113129
($($type:ty),*) => {
114130
$(
115131
impl SourceLocation for $type {
116-
fn filename(&self) -> Option<String> {
117-
self.source_info.filename.clone()
132+
fn filename_index(&self) -> Option<usize> {
133+
self.source_info.filename_index
118134
}
119135

120136
fn range(&self) -> Range {

crates/quarto-markdown-pandoc/src/pandoc/meta.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub fn parse_metadata_strings(meta: MetaValue, outer_metadata: &mut Meta) -> Met
170170
>,
171171
);
172172
match result {
173-
Ok(mut pandoc) => {
173+
Ok((mut pandoc, _context)) => {
174174
for (k, v) in pandoc.meta.into_iter() {
175175
outer_metadata.insert(k, v);
176176
}

crates/quarto-markdown-pandoc/src/pandoc/treesitter.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,12 @@ fn process_native_inline<T: Write>(
301301
PandocNativeIntermediate::IntermediateBaseText(text, range) => {
302302
if let Some(_) = whitespace_re.find(&text) {
303303
Inline::Space(Space {
304-
source_info: SourceInfo::new(context.primary_filename().cloned(), range),
304+
source_info: SourceInfo::new(if context.filenames.is_empty() { None } else { Some(0) }, range),
305305
})
306306
} else {
307307
Inline::Str(Str {
308308
text: apply_smart_quotes(text),
309-
source_info: SourceInfo::new(context.primary_filename().cloned(), range),
309+
source_info: SourceInfo::new(if context.filenames.is_empty() { None } else { Some(0) }, range),
310310
})
311311
}
312312
}
@@ -365,12 +365,12 @@ fn process_native_inlines<T: Write>(
365365
PandocNativeIntermediate::IntermediateBaseText(text, range) => {
366366
if let Some(_) = whitespace_re.find(&text) {
367367
inlines.push(Inline::Space(Space {
368-
source_info: SourceInfo::new(context.primary_filename().cloned(), range),
368+
source_info: SourceInfo::new(if context.filenames.is_empty() { None } else { Some(0) }, range),
369369
}))
370370
} else {
371371
inlines.push(Inline::Str(Str {
372372
text,
373-
source_info: SourceInfo::new(context.primary_filename().cloned(), range),
373+
source_info: SourceInfo::new(if context.filenames.is_empty() { None } else { Some(0) }, range),
374374
}))
375375
}
376376
}

0 commit comments

Comments
 (0)