From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- vendor/rowan/src/syntax_text.rs | 311 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 vendor/rowan/src/syntax_text.rs (limited to 'vendor/rowan/src/syntax_text.rs') diff --git a/vendor/rowan/src/syntax_text.rs b/vendor/rowan/src/syntax_text.rs new file mode 100644 index 000000000..3ab3f6cb0 --- /dev/null +++ b/vendor/rowan/src/syntax_text.rs @@ -0,0 +1,311 @@ +use std::fmt; + +use crate::{ + cursor::{SyntaxNode, SyntaxToken}, + TextRange, TextSize, +}; + +#[derive(Clone)] +pub struct SyntaxText { + node: SyntaxNode, + range: TextRange, +} + +impl SyntaxText { + pub(crate) fn new(node: SyntaxNode) -> SyntaxText { + let range = node.text_range(); + SyntaxText { node, range } + } + + pub fn len(&self) -> TextSize { + self.range.len() + } + + pub fn is_empty(&self) -> bool { + self.range.is_empty() + } + + pub fn contains_char(&self, c: char) -> bool { + self.try_for_each_chunk(|chunk| if chunk.contains(c) { Err(()) } else { Ok(()) }).is_err() + } + + pub fn find_char(&self, c: char) -> Option { + let mut acc: TextSize = 0.into(); + let res = self.try_for_each_chunk(|chunk| { + if let Some(pos) = chunk.find(c) { + let pos: TextSize = (pos as u32).into(); + return Err(acc + pos); + } + acc += TextSize::of(chunk); + Ok(()) + }); + found(res) + } + + pub fn char_at(&self, offset: TextSize) -> Option { + let offset = offset.into(); + let mut start: TextSize = 0.into(); + let res = self.try_for_each_chunk(|chunk| { + let end = start + TextSize::of(chunk); + if start <= offset && offset < end { + let off: usize = u32::from(offset - start) as usize; + return Err(chunk[off..].chars().next().unwrap()); + } + start = end; + Ok(()) + }); + found(res) + } + + pub fn slice(&self, range: R) -> SyntaxText { + let start = range.start().unwrap_or_default(); + let end = range.end().unwrap_or(self.len()); + assert!(start <= end); + let len = end - start; + let start = self.range.start() + start; + let end = start + len; + assert!( + start <= end, + "invalid slice, range: {:?}, slice: {:?}", + self.range, + (range.start(), range.end()), + ); + let range = TextRange::new(start, end); + assert!( + self.range.contains_range(range), + "invalid slice, range: {:?}, slice: {:?}", + self.range, + range, + ); + SyntaxText { node: self.node.clone(), range } + } + + pub fn try_fold_chunks(&self, init: T, mut f: F) -> Result + where + F: FnMut(T, &str) -> Result, + { + self.tokens_with_ranges() + .try_fold(init, move |acc, (token, range)| f(acc, &token.text()[range])) + } + + pub fn try_for_each_chunk Result<(), E>, E>( + &self, + mut f: F, + ) -> Result<(), E> { + self.try_fold_chunks((), move |(), chunk| f(chunk)) + } + + pub fn for_each_chunk(&self, mut f: F) { + enum Void {} + match self.try_for_each_chunk(|chunk| Ok::<(), Void>(f(chunk))) { + Ok(()) => (), + Err(void) => match void {}, + } + } + + fn tokens_with_ranges(&self) -> impl Iterator { + let text_range = self.range; + self.node.descendants_with_tokens().filter_map(|element| element.into_token()).filter_map( + move |token| { + let token_range = token.text_range(); + let range = text_range.intersect(token_range)?; + Some((token, range - token_range.start())) + }, + ) + } +} + +fn found(res: Result<(), T>) -> Option { + match res { + Ok(()) => None, + Err(it) => Some(it), + } +} + +impl fmt::Debug for SyntaxText { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.to_string(), f) + } +} + +impl fmt::Display for SyntaxText { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f)) + } +} + +impl From for String { + fn from(text: SyntaxText) -> String { + text.to_string() + } +} + +impl PartialEq for SyntaxText { + fn eq(&self, mut rhs: &str) -> bool { + self.try_for_each_chunk(|chunk| { + if !rhs.starts_with(chunk) { + return Err(()); + } + rhs = &rhs[chunk.len()..]; + Ok(()) + }) + .is_ok() + && rhs.is_empty() + } +} + +impl PartialEq for str { + fn eq(&self, rhs: &SyntaxText) -> bool { + rhs == self + } +} + +impl PartialEq<&'_ str> for SyntaxText { + fn eq(&self, rhs: &&str) -> bool { + self == *rhs + } +} + +impl PartialEq for &'_ str { + fn eq(&self, rhs: &SyntaxText) -> bool { + rhs == self + } +} + +impl PartialEq for SyntaxText { + fn eq(&self, other: &SyntaxText) -> bool { + if self.range.len() != other.range.len() { + return false; + } + let mut lhs = self.tokens_with_ranges(); + let mut rhs = other.tokens_with_ranges(); + zip_texts(&mut lhs, &mut rhs).is_none() + && lhs.all(|it| it.1.is_empty()) + && rhs.all(|it| it.1.is_empty()) + } +} + +fn zip_texts>(xs: &mut I, ys: &mut I) -> Option<()> { + let mut x = xs.next()?; + let mut y = ys.next()?; + loop { + while x.1.is_empty() { + x = xs.next()?; + } + while y.1.is_empty() { + y = ys.next()?; + } + let x_text = &x.0.text()[x.1]; + let y_text = &y.0.text()[y.1]; + if !(x_text.starts_with(y_text) || y_text.starts_with(x_text)) { + return Some(()); + } + let advance = std::cmp::min(x.1.len(), y.1.len()); + x.1 = TextRange::new(x.1.start() + advance, x.1.end()); + y.1 = TextRange::new(y.1.start() + advance, y.1.end()); + } +} + +impl Eq for SyntaxText {} + +mod private { + use std::ops; + + use crate::{TextRange, TextSize}; + + pub trait SyntaxTextRange { + fn start(&self) -> Option; + fn end(&self) -> Option; + } + + impl SyntaxTextRange for TextRange { + fn start(&self) -> Option { + Some(TextRange::start(*self)) + } + fn end(&self) -> Option { + Some(TextRange::end(*self)) + } + } + + impl SyntaxTextRange for ops::Range { + fn start(&self) -> Option { + Some(self.start) + } + fn end(&self) -> Option { + Some(self.end) + } + } + + impl SyntaxTextRange for ops::RangeFrom { + fn start(&self) -> Option { + Some(self.start) + } + fn end(&self) -> Option { + None + } + } + + impl SyntaxTextRange for ops::RangeTo { + fn start(&self) -> Option { + None + } + fn end(&self) -> Option { + Some(self.end) + } + } + + impl SyntaxTextRange for ops::RangeFull { + fn start(&self) -> Option { + None + } + fn end(&self) -> Option { + None + } + } +} + +#[cfg(test)] +mod tests { + use crate::{green::SyntaxKind, GreenNodeBuilder}; + + use super::*; + + fn build_tree(chunks: &[&str]) -> SyntaxNode { + let mut builder = GreenNodeBuilder::new(); + builder.start_node(SyntaxKind(62)); + for &chunk in chunks.iter() { + builder.token(SyntaxKind(92), chunk.into()) + } + builder.finish_node(); + SyntaxNode::new_root(builder.finish()) + } + + #[test] + fn test_text_equality() { + fn do_check(t1: &[&str], t2: &[&str]) { + let t1 = build_tree(t1).text(); + let t2 = build_tree(t2).text(); + let expected = t1.to_string() == t2.to_string(); + let actual = t1 == t2; + assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2); + let actual = t1 == &*t2.to_string(); + assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2); + } + fn check(t1: &[&str], t2: &[&str]) { + do_check(t1, t2); + do_check(t2, t1) + } + + check(&[""], &[""]); + check(&["a"], &[""]); + check(&["a"], &["a"]); + check(&["abc"], &["def"]); + check(&["hello", "world"], &["hello", "world"]); + check(&["hellowo", "rld"], &["hell", "oworld"]); + check(&["hel", "lowo", "rld"], &["helloworld"]); + check(&["{", "abc", "}"], &["{", "123", "}"]); + check(&["{", "abc", "}", "{"], &["{", "123", "}"]); + check(&["{", "abc", "}"], &["{", "123", "}", "{"]); + check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]); + } +} -- cgit v1.2.3