//! Maps syntax elements through disjoint syntax nodes. //! //! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] use itertools::Itertools; use rustc_hash::FxHashMap; use crate::{SyntaxElement, SyntaxNode}; #[derive(Debug, Default)] pub struct SyntaxMapping { // important information to keep track of: // node -> node // token -> token (implicit in mappings) // input parent -> output parent (for deep lookups) // mappings -> parents entry_parents: Vec, node_mappings: FxHashMap, } impl SyntaxMapping { pub fn new() -> Self { Self::default() } /// Like [`SyntaxMapping::upmap_child`] but for syntax elements. pub fn upmap_child_element( &self, child: &SyntaxElement, input_ancestor: &SyntaxNode, output_ancestor: &SyntaxNode, ) -> Result { match child { SyntaxElement::Node(node) => { self.upmap_child(node, input_ancestor, output_ancestor).map(SyntaxElement::Node) } SyntaxElement::Token(token) => { let upmap_parent = self.upmap_child(&token.parent().unwrap(), input_ancestor, output_ancestor)?; let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); debug_assert!( element.as_token().is_some_and(|it| it.kind() == token.kind()), "token upmapping mapped to the wrong node ({token:?} -> {element:?})" ); Ok(element) } } } /// Maps a child node of the input ancestor to the corresponding node in /// the output ancestor. pub fn upmap_child( &self, child: &SyntaxNode, input_ancestor: &SyntaxNode, output_ancestor: &SyntaxNode, ) -> Result { debug_assert!( child == input_ancestor || child.ancestors().any(|ancestor| &ancestor == input_ancestor) ); // Build a list mapping up to the first mappable ancestor let to_first_upmap = if child != input_ancestor { std::iter::successors(Some((child.index(), child.clone())), |(_, current)| { let parent = current.parent().unwrap(); if &parent == input_ancestor { return None; } Some((parent.index(), parent)) }) .map(|(i, _)| i) .collect::>() } else { vec![] }; // Progressively up-map the input ancestor until we get to the output ancestor let to_output_ancestor = if input_ancestor != output_ancestor { self.upmap_to_ancestor(input_ancestor, output_ancestor)? } else { vec![] }; let to_map_down = to_output_ancestor.into_iter().rev().chain(to_first_upmap.into_iter().rev()); let mut target = output_ancestor.clone(); for index in to_map_down { target = target .children_with_tokens() .nth(index) .and_then(|it| it.into_node()) .expect("equivalent ancestor node should be present in target tree"); } debug_assert_eq!(child.kind(), target.kind()); Ok(target) } fn upmap_to_ancestor( &self, input_ancestor: &SyntaxNode, output_ancestor: &SyntaxNode, ) -> Result, MissingMapping> { let mut current = self.upmap_node_single(input_ancestor).unwrap_or_else(|| input_ancestor.clone()); let mut upmap_chain = vec![current.index()]; loop { let Some(parent) = current.parent() else { break }; if &parent == output_ancestor { return Ok(upmap_chain); } current = match self.upmap_node_single(&parent) { Some(next) => next, None => parent, }; upmap_chain.push(current.index()); } Err(MissingMapping(current)) } pub fn upmap_element( &self, input: &SyntaxElement, output_root: &SyntaxNode, ) -> Option> { match input { SyntaxElement::Node(node) => { Some(self.upmap_node(node, output_root)?.map(SyntaxElement::Node)) } SyntaxElement::Token(token) => { let upmap_parent = match self.upmap_node(&token.parent().unwrap(), output_root)? { Ok(it) => it, Err(err) => return Some(Err(err)), }; let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); debug_assert!( element.as_token().is_some_and(|it| it.kind() == token.kind()), "token upmapping mapped to the wrong node ({token:?} -> {element:?})" ); Some(Ok(element)) } } } pub fn upmap_node( &self, input: &SyntaxNode, output_root: &SyntaxNode, ) -> Option> { // Try to follow the mapping tree, if it exists let input_mapping = self.upmap_node_single(input); let input_ancestor = input.ancestors().find_map(|ancestor| self.upmap_node_single(&ancestor)); match (input_mapping, input_ancestor) { (Some(input_mapping), _) => { // A mapping exists at the input, follow along the tree Some(self.upmap_child(&input_mapping, &input_mapping, output_root)) } (None, Some(input_ancestor)) => { // A mapping exists at an ancestor, follow along the tree Some(self.upmap_child(input, &input_ancestor, output_root)) } (None, None) => { // No mapping exists at all, is the same position in the final tree None } } } pub fn merge(&mut self, mut other: SyntaxMapping) { // Remap other's entry parents to be after the current list of entry parents let remap_base: u32 = self.entry_parents.len().try_into().unwrap(); self.entry_parents.append(&mut other.entry_parents); self.node_mappings.extend(other.node_mappings.into_iter().map(|(node, entry)| { (node, MappingEntry { parent: entry.parent + remap_base, ..entry }) })); } /// Follows the input one step along the syntax mapping tree fn upmap_node_single(&self, input: &SyntaxNode) -> Option { let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?; let output = self.entry_parents[*parent as usize] .children_with_tokens() .nth(*child_slot as usize) .and_then(SyntaxElement::into_node) .unwrap(); debug_assert_eq!(input.kind(), output.kind()); Some(output) } pub fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) { let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping; let parent_entry: u32 = self.entry_parents.len().try_into().unwrap(); self.entry_parents.push(parent_node); let node_entries = node_mappings .into_iter() .map(|(node, slot)| (node, MappingEntry { parent: parent_entry, child_slot: slot })); self.node_mappings.extend(node_entries); } } #[derive(Debug)] pub struct SyntaxMappingBuilder { parent_node: SyntaxNode, node_mappings: Vec<(SyntaxNode, u32)>, } impl SyntaxMappingBuilder { pub fn new(parent_node: SyntaxNode) -> Self { Self { parent_node, node_mappings: vec![] } } pub fn map_node(&mut self, input: SyntaxNode, output: SyntaxNode) { debug_assert_eq!(output.parent().as_ref(), Some(&self.parent_node)); self.node_mappings.push((input, output.index() as u32)); } pub fn map_children( &mut self, input: impl Iterator, output: impl Iterator, ) { for pairs in input.zip_longest(output) { let (input, output) = match pairs { itertools::EitherOrBoth::Both(l, r) => (l, r), itertools::EitherOrBoth::Left(_) => { unreachable!("mapping more input nodes than there are output nodes") } itertools::EitherOrBoth::Right(_) => break, }; self.map_node(input, output); } } pub fn finish(self, mappings: &mut SyntaxMapping) { mappings.add_mapping(self); } } #[derive(Debug)] pub struct MissingMapping(pub SyntaxNode); #[derive(Debug, Clone, Copy)] struct MappingEntry { parent: u32, child_slot: u32, }