1
Fork 0
mirror of https://github.com/RGBCube/cstree synced 2025-07-31 11:07:47 +00:00

Compare commits

...

5 commits

Author SHA1 Message Date
a102fdf1d4
merge feature branches 2025-07-29 19:30:11 +03:00
35c2799059
treewide: specify lifetimes correctly
don't overrestrict lifetimes, prefer `use<...>` to prevent `impl Trait` from capturing too much
2025-07-29 19:28:44 +03:00
c35e88c3da
text: add SyntaxText::rfind_char 2025-07-29 19:08:02 +03:00
a8ebf29808
Fix overly restrictive derive(Clone)s (#74)
Previously, we couldn't clone `SyntaxNodeChildren<S, D>` if D wasn't `Clone`,
but `D` doesn't get cloned, so this is misleading and overly restrictive.

This patch fixes that & other structs that did the same thing.
2025-07-12 17:27:24 +02:00
27c40cbf01
Fix clippy lints (#75) 2025-07-12 10:31:17 +02:00
10 changed files with 107 additions and 29 deletions

View file

@ -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);
}

View file

@ -392,7 +392,7 @@ nan
let root = parse.root();
let resolver = &parse.resolver;
let res = root.sexps().map(|it| it.eval(resolver)).collect::<Vec<_>>();
eprintln!("{:?}", res);
eprintln!("{res:?}");
assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),])
}

View file

@ -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)])?) => {

View file

@ -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<Item = &'a SyntaxNode<S, D>> {
pub fn ancestors(&self) -> impl Iterator<Item = &'a SyntaxNode<S, D>> + use<'a, S, D> {
match self {
NodeOrToken::Node(it) => it.ancestors(),
NodeOrToken::Token(it) => it.parent().ancestors(),

View file

@ -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<S, D>,
}
impl<S: Syntax, D> 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<S, D>) -> Self {
@ -118,12 +127,21 @@ impl<S: Syntax, D> ExactSizeIterator for SyntaxNodeChildren<'_, S, D> {
impl<S: Syntax, D> 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<S, D>,
}
impl<S: Syntax, D> 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<S, D>) -> Self {
@ -166,3 +184,35 @@ impl<S: Syntax, D> ExactSizeIterator for SyntaxElementChildren<'_, S, D> {
}
}
impl<S: Syntax, D> 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<C: Clone>() {}
fn test_impls_clone() {
assert_clone::<SyntaxNodeChildren<DummyKind, NotClone>>();
assert_clone::<SyntaxElementChildren<DummyKind, NotClone>>();
}
}

View file

@ -851,9 +851,7 @@ impl<S: Syntax, D> SyntaxNode<S, D> {
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<Item = (GreenElementRef, (usize, TextSize))> {
) -> impl Iterator<Item = (GreenElementRef<'_>, (usize, TextSize))> {
self.children()
.skip(start_index)
.enumerate()
@ -956,7 +954,7 @@ impl GreenNode {
&self,
end_index: usize,
mut offset: TextSize,
) -> impl Iterator<Item = (GreenElementRef, (usize, TextSize))> {
) -> impl Iterator<Item = (GreenElementRef<'_>, (usize, TextSize))> {
self.children()
.take(end_index)
.rev()

View file

@ -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<Item = &'a ResolvedNode<S, D>> {
pub fn ancestors(&self) -> impl Iterator<Item = &'a ResolvedNode<S, D>> + use<'a, S, D> {
match self {
NodeOrToken::Node(it) => it.ancestors(),
NodeOrToken::Token(it) => it.parent().ancestors(),

View file

@ -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<S, D>,
range: TextRange,
resolver: &'i I,
}
impl<I: ?Sized, S: Syntax, D> Clone for SyntaxText<'_, '_, I, S, D> {
fn clone(&self) -> Self {
*self
}
}
impl<I: ?Sized, S: Syntax, D> Copy for SyntaxText<'_, '_, I, S, D> {}
impl<'n, 'i, I: Resolver<TokenKey> + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, S, D> {
pub(crate) fn new(node: &'n SyntaxNode<S, D>, resolver: &'i I) -> Self {
let range = node.text_range();
@ -84,6 +91,18 @@ impl<'n, 'i, I: Resolver<TokenKey> + ?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<TextSize> {
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<char> {
@ -143,7 +162,7 @@ impl<'n, 'i, I: Resolver<TokenKey> + ?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<T, F, E>(&self, init: T, mut f: F) -> Result<T, E>
where
F: FnMut(T, &str) -> Result<T, E>,
F: FnMut(T, &'i str) -> Result<T, E>,
{
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<TokenKey> + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I,
self.fold_chunks((), |(), chunk| f(chunk))
}
fn tokens_with_ranges(&self) -> impl Iterator<Item = (&SyntaxToken<S, D>, TextRange)> {
fn tokens_with_ranges(&self) -> impl Iterator<Item = (&'n SyntaxToken<S, D>, 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<TokenKey> + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I,
}
}
#[inline]
fn found<T>(res: Result<(), T>) -> Option<T> {
match res {
Ok(()) => None,
Err(it) => Some(it),
}
res.err()
}
impl<I: Resolver<TokenKey> + ?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<C: Copy>() {}
fn test_impls_copy() {
assert_copy::<SyntaxText<TokenInterner, SyntaxKind, NotClone>>();
}
}
}

View file

@ -58,13 +58,13 @@ impl<S: Syntax, D> SyntaxToken<S, D> {
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!()

View file

@ -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"