diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index f55082de67..79fdf75b7f 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -149,14 +149,14 @@ fn expand_macro_recur( expanded.text_range().len(), &expansion_span_map, ); - Some(expand(sema, expanded, result_span_map, offset_in_original_node)) + Some(expand(sema, expanded, result_span_map, u32::from(offset_in_original_node) as i32)) } fn expand( sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode, result_span_map: &mut SpanMap, - offset_in_original_node: TextSize, + mut offset_in_original_node: i32, ) -> SyntaxNode { let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); @@ -166,8 +166,14 @@ fn expand( sema, &child, result_span_map, - offset_in_original_node + child.syntax().text_range().start(), + TextSize::new( + (offset_in_original_node + (u32::from(child.syntax().text_range().start()) as i32)) + as u32, + ), ) { + offset_in_original_node = offset_in_original_node + + (u32::from(new_node.text_range().len()) as i32) + - (u32::from(child.syntax().text_range().len()) as i32); // check if the whole original syntax is replaced if expanded == *child.syntax() { return new_node; diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs index 2680e5036c..f80de05ec6 100644 --- a/crates/span/src/map.rs +++ b/crates/span/src/map.rs @@ -13,6 +13,7 @@ use crate::{ /// Maps absolute text ranges for the corresponding file to the relevant span data. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct SpanMap { + /// The offset stored here is the *end* of the node. spans: Vec<(TextSize, SpanData)>, /// Index of the matched macro arm on successful expansion for declarative macros. // FIXME: Does it make sense to have this here? @@ -109,11 +110,32 @@ where /// /// The length of the replacement node needs to be `other_size`. pub fn merge(&mut self, other_range: TextRange, other_size: TextSize, other: &SpanMap) { + // I find the following diagram helpful to illustrate the bounds and why we use `<` or `<=`: + // -------------------------------------------------------------------- + // 1 3 5 6 7 10 11 <-- offsets we store + // 0-1 1-3 3-5 5-6 6-7 7-10 10-11 <-- ranges these offsets refer to + // 3 .. 7 <-- other_range + // 3-5 5-6 6-7 <-- ranges we replace (len = 7-3 = 4) + // ^^^^^^^^^^^ ^^^^^^^^^^ + // remove shift + // 2 3 5 9 <-- offsets we insert + // 0-2 2-3 3-5 5-9 <-- ranges we insert (other_size = 9-0 = 9) + // ------------------------------------ + // 1 3 + // 0-1 1-3 <-- these remain intact + // 5 6 8 12 + // 3-5 5-6 6-8 8-12 <-- we shift these by other_range.start() and insert them + // 15 16 + // 12-15 15-16 <-- we shift these by other_size-other_range.len() = 9-4 = 5 + // ------------------------------------ + // 1 3 5 6 8 12 15 16 <-- final offsets we store + // 0-1 1-3 3-5 5-6 6-8 8-12 12-15 15-16 <-- final ranges + self.spans.retain_mut(|(offset, _)| { - if other_range.contains(*offset) { + if other_range.start() < *offset && *offset <= other_range.end() { false } else { - if *offset >= other_range.end() { + if *offset > other_range.end() { *offset += other_size; *offset -= other_range.len(); }