From 7c67612b8a894187fa3b64725531a5459f9211bf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 10 Aug 2018 22:33:29 +0300 Subject: organizize --- crates/libsyntax2/src/algo/mod.rs | 123 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 crates/libsyntax2/src/algo/mod.rs (limited to 'crates/libsyntax2/src/algo/mod.rs') diff --git a/crates/libsyntax2/src/algo/mod.rs b/crates/libsyntax2/src/algo/mod.rs new file mode 100644 index 000000000..d2de70fd4 --- /dev/null +++ b/crates/libsyntax2/src/algo/mod.rs @@ -0,0 +1,123 @@ +pub mod walk; + +use {SyntaxNodeRef, TextUnit, TextRange}; + +pub fn find_leaf_at_offset(node: SyntaxNodeRef, offset: TextUnit) -> LeafAtOffset { + let range = node.range(); + assert!( + contains_offset_nonstrict(range, offset), + "Bad offset: range {:?} offset {:?}", range, offset + ); + if range.is_empty() { + return LeafAtOffset::None; + } + + if node.is_leaf() { + return LeafAtOffset::Single(node); + } + + let mut children = node.children() + .filter(|child| { + let child_range = child.range(); + !child_range.is_empty() && contains_offset_nonstrict(child_range, offset) + }); + + let left = children.next().unwrap(); + let right = children.next(); + assert!(children.next().is_none()); + return if let Some(right) = right { + match (find_leaf_at_offset(left, offset), find_leaf_at_offset(right, offset)) { + (LeafAtOffset::Single(left), LeafAtOffset::Single(right)) => + LeafAtOffset::Between(left, right), + _ => unreachable!() + } + } else { + find_leaf_at_offset(left, offset) + }; +} + +#[derive(Clone, Copy, Debug)] +pub enum LeafAtOffset<'a> { + None, + Single(SyntaxNodeRef<'a>), + Between(SyntaxNodeRef<'a>, SyntaxNodeRef<'a>) +} + +impl<'a> LeafAtOffset<'a> { + pub fn right_biased(self) -> Option> { + match self { + LeafAtOffset::None => None, + LeafAtOffset::Single(node) => Some(node), + LeafAtOffset::Between(_, right) => Some(right) + } + } + + pub fn left_biased(self) -> Option> { + match self { + LeafAtOffset::None => None, + LeafAtOffset::Single(node) => Some(node), + LeafAtOffset::Between(left, _) => Some(left) + } + } +} + +impl<'f> Iterator for LeafAtOffset<'f> { + type Item = SyntaxNodeRef<'f>; + + fn next(&mut self) -> Option> { + match *self { + LeafAtOffset::None => None, + LeafAtOffset::Single(node) => { *self = LeafAtOffset::None; Some(node) } + LeafAtOffset::Between(left, right) => { *self = LeafAtOffset::Single(right); Some(left) } + } + } +} + + +pub fn find_covering_node(root: SyntaxNodeRef, range: TextRange) -> SyntaxNodeRef { + assert!(is_subrange(root.range(), range)); + let (left, right) = match ( + find_leaf_at_offset(root, range.start()).right_biased(), + find_leaf_at_offset(root, range.end()).left_biased() + ) { + (Some(l), Some(r)) => (l, r), + _ => return root + }; + + common_ancestor(left, right) +} + +fn common_ancestor<'a>(n1: SyntaxNodeRef<'a>, n2: SyntaxNodeRef<'a>) -> SyntaxNodeRef<'a> { + for p in ancestors(n1) { + if ancestors(n2).any(|a| a == p) { + return p; + } + } + panic!("Can't find common ancestor of {:?} and {:?}", n1, n2) +} + +pub fn ancestors<'a>(node: SyntaxNodeRef<'a>) -> impl Iterator> { + Ancestors(Some(node)) +} + +#[derive(Debug)] +struct Ancestors<'a>(Option>); + +impl<'a> Iterator for Ancestors<'a> { + type Item = SyntaxNodeRef<'a>; + + fn next(&mut self) -> Option { + self.0.take().map(|n| { + self.0 = n.parent(); + n + }) + } +} + +fn contains_offset_nonstrict(range: TextRange, offset: TextUnit) -> bool { + range.start() <= offset && offset <= range.end() +} + +fn is_subrange(range: TextRange, subrange: TextRange) -> bool { + range.start() <= subrange.start() && subrange.end() <= range.end() +} -- cgit v1.2.3