Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
24 changes: 14 additions & 10 deletions crates/dotrain/src/parser/raindocument/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -784,24 +784,28 @@ impl RainDocument {
name = slices.0[..slices.0.len() - 1].to_owned();
name_position = [parsed_binding.1[0], parsed_binding.1[0] + boundry_offset];

let slices = content_text.split_at(boundry_offset + 1);
let trimmed_content = tracked_trim(slices.1);
content_position = if trimmed_content.0.is_empty() {
let orig_slices = content_text.split_at(boundry_offset + 1);
let trimmed_orig = tracked_trim(orig_slices.1);
// For content_position start: use leading trim from original text so comments
// at the start of a binding's content are preserved (not skipped).
// For content_position end: use trailing trim from modified doc (raw_trimmed.2)
// so comments before the next #-binding are excluded from this binding.
content_position = if trimmed_orig.0.is_empty() {
[
parsed_binding.1[0] + boundry_offset + 1,
parsed_binding.1[1],
]
} else {
[
parsed_binding.1[0] + boundry_offset + 1 + trimmed_content.1,
parsed_binding.1[1] - trimmed_content.2,
parsed_binding.1[0] + boundry_offset + 1 + trimmed_orig.1,
parsed_binding.1[1] - raw_trimmed.2,
]
};
content = if trimmed_content.0.is_empty() {
slices.1.to_owned()
} else {
trimmed_content.0.to_owned()
};
content = self
.text
.get(content_position[0]..content_position[1])
.unwrap_or("")
.to_owned();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
} else {
name = parsed_binding.0.clone();
name_position = parsed_binding.1;
Expand Down
44 changes: 44 additions & 0 deletions crates/dotrain/src/parser/raindocument/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -947,4 +947,48 @@ _: opcode-1(0xabcd 456);
assert_eq!(inner.hash, inner_hash_hex);
assert_eq!(inner.unwrap_constant_binding(), "0x1111");
}

// Mutation-validated: revert the process_binding fix (restore content_text path) and
// this test fails because b1's content_position extends through the comment.
#[test]
fn test_comment_before_binding_excluded_from_previous_content_position() {
let store = Store::new();
let meta_store = Arc::new(RwLock::new(store));

// Comment before #b2 must not appear in b1's content or content_position.
let text = "---\n#b1\n! elided\n\n/* comment for b2 */\n#b2\n! elided2\n";
let rain_document =
RainDocument::create(text.to_owned(), Some(meta_store.clone()), None, None);

let b1 = rain_document
.bindings()
.iter()
.find(|b| b.name == "b1")
.expect("binding b1 not found");

// b1 content must be the elision text only, not including the comment.
assert_eq!(b1.content, "! elided");

// b1 content_position must end at the last char of "! elided", not at the comment.
let content_end = b1.content_position[1];
let text_at_end = text.get(b1.content_position[0]..content_end).unwrap();
assert_eq!(text_at_end, "! elided");

// The comment text must not appear anywhere inside b1's content_position range.
let b1_content_range = text
.get(b1.content_position[0]..b1.content_position[1])
.unwrap();
assert!(
!b1_content_range.contains("/*"),
"comment leaked into b1 content_position: {b1_content_range:?}"
);

// b2 must also parse correctly.
let b2 = rain_document
.bindings()
.iter()
.find(|b| b.name == "b2")
.expect("binding b2 not found");
assert_eq!(b2.content, "! elided2");
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Loading