summaryrefslogtreecommitdiffstats
path: root/vendor/rowan/src/syntax_text.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/rowan/src/syntax_text.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/rowan/src/syntax_text.rs')
-rw-r--r--vendor/rowan/src/syntax_text.rs311
1 files changed, 311 insertions, 0 deletions
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<TextSize> {
+ 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<char> {
+ 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<R: private::SyntaxTextRange>(&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<T, F, E>(&self, init: T, mut f: F) -> Result<T, E>
+ where
+ F: FnMut(T, &str) -> Result<T, E>,
+ {
+ self.tokens_with_ranges()
+ .try_fold(init, move |acc, (token, range)| f(acc, &token.text()[range]))
+ }
+
+ pub fn try_for_each_chunk<F: FnMut(&str) -> Result<(), E>, E>(
+ &self,
+ mut f: F,
+ ) -> Result<(), E> {
+ self.try_fold_chunks((), move |(), chunk| f(chunk))
+ }
+
+ pub fn for_each_chunk<F: FnMut(&str)>(&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<Item = (SyntaxToken, TextRange)> {
+ 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<T>(res: Result<(), T>) -> Option<T> {
+ 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<SyntaxText> for String {
+ fn from(text: SyntaxText) -> String {
+ text.to_string()
+ }
+}
+
+impl PartialEq<str> 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<SyntaxText> 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<SyntaxText> 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<I: Iterator<Item = (SyntaxToken, TextRange)>>(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<TextSize>;
+ fn end(&self) -> Option<TextSize>;
+ }
+
+ impl SyntaxTextRange for TextRange {
+ fn start(&self) -> Option<TextSize> {
+ Some(TextRange::start(*self))
+ }
+ fn end(&self) -> Option<TextSize> {
+ Some(TextRange::end(*self))
+ }
+ }
+
+ impl SyntaxTextRange for ops::Range<TextSize> {
+ fn start(&self) -> Option<TextSize> {
+ Some(self.start)
+ }
+ fn end(&self) -> Option<TextSize> {
+ Some(self.end)
+ }
+ }
+
+ impl SyntaxTextRange for ops::RangeFrom<TextSize> {
+ fn start(&self) -> Option<TextSize> {
+ Some(self.start)
+ }
+ fn end(&self) -> Option<TextSize> {
+ None
+ }
+ }
+
+ impl SyntaxTextRange for ops::RangeTo<TextSize> {
+ fn start(&self) -> Option<TextSize> {
+ None
+ }
+ fn end(&self) -> Option<TextSize> {
+ Some(self.end)
+ }
+ }
+
+ impl SyntaxTextRange for ops::RangeFull {
+ fn start(&self) -> Option<TextSize> {
+ None
+ }
+ fn end(&self) -> Option<TextSize> {
+ 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"]);
+ }
+}