diff --git a/cstree/examples/math.rs b/cstree/examples/math.rs index ae44a4e..a98f6a2 100644 --- a/cstree/examples/math.rs +++ b/cstree/examples/math.rs @@ -114,7 +114,7 @@ fn print(indent: usize, element: SyntaxElementRef<'_>, resolver: &impl Resolver) print!("{:indent$}", "", indent = indent); match element { NodeOrToken::Node(node) => { - println!("- {:?}", kind); + println!("- {kind:?}"); for child in node.children_with_tokens() { print(indent + 2, child, resolver); } diff --git a/cstree/examples/s_expressions.rs b/cstree/examples/s_expressions.rs index dcbe7a3..f37b3fb 100644 --- a/cstree/examples/s_expressions.rs +++ b/cstree/examples/s_expressions.rs @@ -392,7 +392,7 @@ nan let root = parse.root(); let resolver = &parse.resolver; let res = root.sexps().map(|it| it.eval(resolver)).collect::>(); - eprintln!("{:?}", res); + eprintln!("{res:?}"); assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),]) } diff --git a/cstree/src/interning/lasso_compat/token_interner.rs b/cstree/src/interning/lasso_compat/token_interner.rs index 35b8e75..248baa4 100644 --- a/cstree/src/interning/lasso_compat/token_interner.rs +++ b/cstree/src/interning/lasso_compat/token_interner.rs @@ -15,7 +15,7 @@ const DEFAULT_STRING_CAPACITY: usize = 512; /// Default memory in bytes that the interner will initially allocate space for. /// Value recommended by the author of `lasso`. -const DEFAULT_BYTE_CAPACITY: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(4096) }; +const DEFAULT_BYTE_CAPACITY: NonZeroUsize = NonZeroUsize::new(4096).unwrap(); macro_rules! impl_traits { (for $interner:ty $(, if #[cfg(feature = $feature:literal)])?) => { diff --git a/cstree/src/syntax/element.rs b/cstree/src/syntax/element.rs index bef407a..27fbdc4 100644 --- a/cstree/src/syntax/element.rs +++ b/cstree/src/syntax/element.rs @@ -292,7 +292,7 @@ impl<'a, S: Syntax, D> SyntaxElementRef<'a, S, D> { /// Returns an iterator along the chain of parents of this node. #[inline] - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use<'a, S, D> { match self { NodeOrToken::Node(it) => it.ancestors(), NodeOrToken::Token(it) => it.parent().ancestors(), diff --git a/cstree/src/syntax/iter.rs b/cstree/src/syntax/iter.rs index 74926d8..fdc0ae1 100644 --- a/cstree/src/syntax/iter.rs +++ b/cstree/src/syntax/iter.rs @@ -66,12 +66,21 @@ impl ExactSizeIterator for Iter<'_> { impl FusedIterator for Iter<'_> {} /// An iterator over the child nodes of a [`SyntaxNode`]. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct SyntaxNodeChildren<'n, S: Syntax, D: 'static = ()> { inner: Iter<'n>, parent: &'n SyntaxNode, } +impl Clone for SyntaxNodeChildren<'_, S, D> { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + parent: self.parent, + } + } +} + impl<'n, S: Syntax, D> SyntaxNodeChildren<'n, S, D> { #[inline] pub(super) fn new(parent: &'n SyntaxNode) -> Self { @@ -118,12 +127,21 @@ impl ExactSizeIterator for SyntaxNodeChildren<'_, S, D> { impl FusedIterator for SyntaxNodeChildren<'_, S, D> {} /// An iterator over the children of a [`SyntaxNode`]. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct SyntaxElementChildren<'n, S: Syntax, D: 'static = ()> { inner: Iter<'n>, parent: &'n SyntaxNode, } +impl Clone for SyntaxElementChildren<'_, S, D> { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + parent: self.parent, + } + } +} + impl<'n, S: Syntax, D> SyntaxElementChildren<'n, S, D> { #[inline] pub(super) fn new(parent: &'n SyntaxNode) -> Self { @@ -166,3 +184,35 @@ impl ExactSizeIterator for SyntaxElementChildren<'_, S, D> { } } impl FusedIterator for SyntaxElementChildren<'_, S, D> {} + +#[cfg(test)] +#[allow(dead_code)] +mod tests { + use super::*; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct DummyKind; + + impl Syntax for DummyKind { + fn from_raw(_: crate::RawSyntaxKind) -> Self { + unreachable!() + } + + fn into_raw(self) -> crate::RawSyntaxKind { + unreachable!() + } + + fn static_text(self) -> Option<&'static str> { + unreachable!() + } + } + + struct NotClone; + + fn assert_clone() {} + + fn test_impls_clone() { + assert_clone::>(); + assert_clone::>(); + } +} diff --git a/cstree/src/syntax/node.rs b/cstree/src/syntax/node.rs index 429b1da..356e0f6 100644 --- a/cstree/src/syntax/node.rs +++ b/cstree/src/syntax/node.rs @@ -851,9 +851,7 @@ impl SyntaxNode { let range = self.text_range(); assert!( range.start() <= offset && offset <= range.end(), - "Bad offset: range {:?} offset {:?}", - range, - offset + "Bad offset: range {range:?} offset {offset:?}" ); if range.is_empty() { return TokenAtOffset::None; @@ -940,7 +938,7 @@ impl GreenNode { &self, start_index: usize, mut offset: TextSize, - ) -> impl Iterator { + ) -> impl Iterator, (usize, TextSize))> { self.children() .skip(start_index) .enumerate() @@ -956,7 +954,7 @@ impl GreenNode { &self, end_index: usize, mut offset: TextSize, - ) -> impl Iterator { + ) -> impl Iterator, (usize, TextSize))> { self.children() .take(end_index) .rev() diff --git a/cstree/src/syntax/resolved.rs b/cstree/src/syntax/resolved.rs index 500d611..4c97301 100644 --- a/cstree/src/syntax/resolved.rs +++ b/cstree/src/syntax/resolved.rs @@ -713,7 +713,7 @@ impl<'a, S: Syntax, D> ResolvedElementRef<'a, S, D> { /// Returns an iterator along the chain of parents of this node. #[inline] - pub fn ancestors(&self) -> impl Iterator> { + pub fn ancestors(&self) -> impl Iterator> + use<'a, S, D> { match self { NodeOrToken::Node(it) => it.ancestors(), NodeOrToken::Token(it) => it.parent().ancestors(), diff --git a/cstree/src/syntax/text.rs b/cstree/src/syntax/text.rs index b613556..a0724f4 100644 --- a/cstree/src/syntax/text.rs +++ b/cstree/src/syntax/text.rs @@ -40,13 +40,20 @@ use crate::{ /// let sub = text.slice(2.into()..5.into()); /// assert_eq!(sub, "748"); /// ``` -#[derive(Clone)] pub struct SyntaxText<'n, 'i, I: ?Sized, S: Syntax, D: 'static = ()> { node: &'n SyntaxNode, range: TextRange, resolver: &'i I, } +impl Clone for SyntaxText<'_, '_, I, S, D> { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for SyntaxText<'_, '_, I, S, D> {} + impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, S, D> { pub(crate) fn new(node: &'n SyntaxNode, resolver: &'i I) -> Self { let range = node.text_range(); @@ -84,6 +91,18 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, found(res) } + /// If `self.contains_char(c)`, returns `Some(pos)`, where `pos` is the byte position of the + /// last appearance of `c`. Otherwise, returns `None`. + pub fn rfind_char(&self, c: char) -> Option { + let mut acc: TextSize = 0.into(); + let mut res = None; + self.for_each_chunk(|chunk| { + res = chunk.rfind(c).map(|pos| acc + TextSize::from(pos as u32)).or(res); + acc += TextSize::of(chunk); + }); + res + } + /// If `offset < self.len()`, returns `Some(c)`, where `c` is the first `char` at or after /// `offset` (in bytes). Otherwise, returns `None`. pub fn char_at(&self, offset: TextSize) -> Option { @@ -143,7 +162,7 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, /// See also [`fold_chunks`](SyntaxText::fold_chunks) for folds that always succeed. pub fn try_fold_chunks(&self, init: T, mut f: F) -> Result where - F: FnMut(T, &str) -> Result, + F: FnMut(T, &'i str) -> Result, { self.tokens_with_ranges().try_fold(init, move |acc, (token, range)| { f(acc, &token.resolve_text(self.resolver)[range]) @@ -188,7 +207,7 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, self.fold_chunks((), |(), chunk| f(chunk)) } - fn tokens_with_ranges(&self) -> impl Iterator, TextRange)> { + fn tokens_with_ranges(&self) -> impl Iterator, TextRange)> + use<'i, 'n, I, S, D> { let text_range = self.range; self.node .descendants_with_tokens() @@ -201,11 +220,9 @@ impl<'n, 'i, I: Resolver + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, } } +#[inline] fn found(res: Result<(), T>) -> Option { - match res { - Ok(()) => None, - Err(it) => Some(it), - } + res.err() } impl + ?Sized, S: Syntax, D> fmt::Debug for SyntaxText<'_, '_, I, S, D> { @@ -378,7 +395,7 @@ mod private { #[cfg(test)] mod tests { - use crate::{build::GreenNodeBuilder, RawSyntaxKind}; + use crate::{build::GreenNodeBuilder, interning::TokenInterner, RawSyntaxKind}; use super::*; @@ -429,9 +446,9 @@ mod tests { let t2 = t2.resolve_text(&resolver); let expected = t1.to_string() == t2.to_string(); let actual = t1 == t2; - assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (SyntaxText)", t1, t2); + assert_eq!(expected, actual, "`{t1}` (SyntaxText) `{t2}` (SyntaxText)"); let actual = t1 == t2.to_string().as_str(); - assert_eq!(expected, actual, "`{}` (SyntaxText) `{}` (&str)", t1, t2); + assert_eq!(expected, actual, "`{t1}` (SyntaxText) `{t2}` (&str)"); } fn check(t1: &[&str], t2: &[&str]) { do_check(t1, t2); @@ -450,4 +467,17 @@ mod tests { check(&["{", "abc", "}"], &["{", "123", "}", "{"]); check(&["{", "abc", "}ab"], &["{", "abc", "}", "ab"]); } + + #[allow(dead_code)] + mod impl_asserts { + use super::*; + + struct NotClone; + + fn assert_copy() {} + + fn test_impls_copy() { + assert_copy::>(); + } + } } diff --git a/cstree/src/syntax/token.rs b/cstree/src/syntax/token.rs index bcd1ff9..65dd902 100644 --- a/cstree/src/syntax/token.rs +++ b/cstree/src/syntax/token.rs @@ -58,13 +58,13 @@ impl SyntaxToken { write!(target, "{:?}@{:?}", self.kind(), self.text_range())?; let text = self.resolve_text(resolver); if text.len() < 25 { - return write!(target, " {:?}", text); + return write!(target, " {text:?}"); } for idx in 21..25 { if text.is_char_boundary(idx) { let text = format!("{} ...", &text[..idx]); - return write!(target, " {:?}", text); + return write!(target, " {text:?}"); } } unreachable!() diff --git a/cstree/tests/it/basic.rs b/cstree/tests/it/basic.rs index c02fc3b..207bdab 100644 --- a/cstree/tests/it/basic.rs +++ b/cstree/tests/it/basic.rs @@ -149,18 +149,18 @@ fn inline_resolver() { let leaf1_0 = leaf1_0.into_token().unwrap(); assert_eq!(leaf1_0.text(), "1.0"); assert_eq!(leaf1_0.text_range(), TextRange::at(6.into(), 3.into())); - assert_eq!(format!("{}", leaf1_0), leaf1_0.text()); - assert_eq!(format!("{:?}", leaf1_0), "SyntaxKind(5)@6..9 \"1.0\""); + assert_eq!(format!("{leaf1_0}"), leaf1_0.text()); + assert_eq!(format!("{leaf1_0:?}"), "SyntaxKind(5)@6..9 \"1.0\""); } { let node2 = tree.children().nth(2).unwrap(); assert_eq!(node2.text(), "2.02.12.2"); let resolver = node2.resolver(); assert_eq!(node2.resolve_text(resolver.as_ref()), node2.text()); - assert_eq!(format!("{}", node2).as_str(), node2.text()); - assert_eq!(format!("{:?}", node2), "SyntaxKind(6)@9..18"); + assert_eq!(format!("{node2}").as_str(), node2.text()); + assert_eq!(format!("{node2:?}"), "SyntaxKind(6)@9..18"); assert_eq!( - format!("{:#?}", node2), + format!("{node2:#?}"), r#"SyntaxKind(6)@9..18 SyntaxKind(7)@9..12 "2.0" SyntaxKind(8)@12..15 "2.1"