mirror of
https://github.com/RGBCube/cstree
synced 2025-07-27 09:07:44 +00:00
add write_
interface for syntax nodes and tokens
and refactor `Debug` and `Display` to use it
This commit is contained in:
parent
cc5ea59d4b
commit
3ef1e7e82b
4 changed files with 188 additions and 48 deletions
|
@ -1,4 +1,4 @@
|
|||
use std::sync::atomic::AtomicU32;
|
||||
use std::{fmt, sync::atomic::AtomicU32};
|
||||
|
||||
use lasso::Resolver;
|
||||
use text_size::{TextRange, TextSize};
|
||||
|
@ -22,13 +22,57 @@ impl<L: Language, D> From<SyntaxToken<L, D>> for SyntaxElement<L, D> {
|
|||
}
|
||||
|
||||
impl<L: Language, D> SyntaxElement<L, D> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn display(&self, resolver: &impl Resolver) -> String {
|
||||
/// Returns this element's [`Display`](fmt::Display) representation as a string.
|
||||
///
|
||||
/// To avoid allocating for every element, see [`write_display`](type.SyntaxElement.html#method.write_display).
|
||||
pub fn display<R>(&self, resolver: &R) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.display(resolver),
|
||||
NodeOrToken::Token(it) => it.display(resolver),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes this element's [`Display`](fmt::Display) representation into the given `target`.
|
||||
pub fn write_display<R>(&self, resolver: &R, target: &mut impl fmt::Write) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.write_display(resolver, target),
|
||||
NodeOrToken::Token(it) => it.write_display(resolver, target),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this element's [`Debug`](fmt::Debug) representation as a string.
|
||||
/// If `recursive` is `true`, prints the entire subtree rooted in this element.
|
||||
/// Otherwise, only this element's kind and range are written.
|
||||
///
|
||||
/// To avoid allocating for every element, see [`write_debug`](type.SyntaxElement.html#method.write_debug).
|
||||
pub fn debug<R>(&self, resolver: &R, recursive: bool) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.debug(resolver, recursive),
|
||||
NodeOrToken::Token(it) => it.debug(resolver),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes this element's [`Debug`](fmt::Debug) representation into the given `target`.
|
||||
/// If `recursive` is `true`, prints the entire subtree rooted in this element.
|
||||
/// Otherwise, only this element's kind and range are written.
|
||||
pub fn write_debug<R>(&self, resolver: &R, target: &mut impl fmt::Write, recursive: bool) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.write_debug(resolver, target, recursive),
|
||||
NodeOrToken::Token(it) => it.write_debug(resolver, target),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to an element of the tree, can be either a reference to a node or one to a token.
|
||||
|
@ -56,13 +100,57 @@ impl<'a, L: Language, D> From<&'a SyntaxElement<L, D>> for SyntaxElementRef<'a,
|
|||
}
|
||||
|
||||
impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn display(&self, resolver: &impl Resolver) -> String {
|
||||
/// Returns this element's [`Display`](fmt::Display) representation as a string.
|
||||
///
|
||||
/// To avoid allocating for every element, see [`write_display`](type.SyntaxElementRef.html#method.write_display).
|
||||
pub fn display<R>(&self, resolver: &R) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.display(resolver),
|
||||
NodeOrToken::Token(it) => it.display(resolver),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes this element's [`Display`](fmt::Display) representation into the given `target`.
|
||||
pub fn write_display<R>(&self, resolver: &R, target: &mut impl fmt::Write) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.write_display(resolver, target),
|
||||
NodeOrToken::Token(it) => it.write_display(resolver, target),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns this element's [`Debug`](fmt::Debug) representation as a string.
|
||||
/// If `recursive` is `true`, prints the entire subtree rooted in this element.
|
||||
/// Otherwise, only this element's kind and range are written.
|
||||
///
|
||||
/// To avoid allocating for every element, see [`write_debug`](type.SyntaxElementRef.html#method.write_debug).
|
||||
pub fn debug<R>(&self, resolver: &R, recursive: bool) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.debug(resolver, recursive),
|
||||
NodeOrToken::Token(it) => it.debug(resolver),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes this element's [`Debug`](fmt::Debug) representation into the given `target`.
|
||||
/// If `recursive` is `true`, prints the entire subtree rooted in this element.
|
||||
/// Otherwise, only this element's kind and range are written.
|
||||
pub fn write_debug<R>(&self, resolver: &R, target: &mut impl fmt::Write, recursive: bool) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
match self {
|
||||
NodeOrToken::Node(it) => it.write_debug(resolver, target, recursive),
|
||||
NodeOrToken::Token(it) => it.write_debug(resolver, target),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Language, D> SyntaxElement<L, D> {
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
use parking_lot::RwLock;
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
fmt::Write,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter, ptr,
|
||||
sync::{
|
||||
|
@ -33,55 +33,75 @@ unsafe impl<L: Language, D: 'static> Send for SyntaxNode<L, D> {}
|
|||
unsafe impl<L: Language, D: 'static> Sync for SyntaxNode<L, D> {}
|
||||
|
||||
impl<L: Language, D> SyntaxNode<L, D> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn debug<R>(&self, resolver: &R, recursive: bool) -> String
|
||||
/// Writes this node's [`Debug`](fmt::Debug) representation into the given `target`.
|
||||
/// If `recursive` is `true`, prints the entire subtree rooted in this node.
|
||||
/// Otherwise, only this node's kind and range are written.
|
||||
pub fn write_debug<R>(&self, resolver: &R, target: &mut impl fmt::Write, recursive: bool) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
// NOTE: `fmt::Write` methods on `String` never fail
|
||||
let mut res = String::new();
|
||||
if recursive {
|
||||
let mut level = 0;
|
||||
for event in self.preorder_with_tokens() {
|
||||
match event {
|
||||
WalkEvent::Enter(element) => {
|
||||
for _ in 0..level {
|
||||
write!(res, " ").unwrap();
|
||||
write!(target, " ")?;
|
||||
}
|
||||
writeln!(
|
||||
res,
|
||||
"{}",
|
||||
match element {
|
||||
NodeOrToken::Node(node) => node.debug(resolver, false),
|
||||
NodeOrToken::Token(token) => token.debug(resolver),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
element.write_debug(resolver, target, false)?;
|
||||
writeln!(target)?;
|
||||
level += 1;
|
||||
}
|
||||
WalkEvent::Leave(_) => level -= 1,
|
||||
}
|
||||
}
|
||||
assert_eq!(level, 0);
|
||||
Ok(())
|
||||
} else {
|
||||
write!(res, "{:?}@{:?}", self.kind(), self.text_range()).unwrap();
|
||||
write!(target, "{:?}@{:?}", self.kind(), self.text_range())
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub fn display<R>(&self, resolver: &R) -> String
|
||||
/// Returns this node's [`Debug`](fmt::Debug) representation as a string.
|
||||
/// If `recursive` is `true`, prints the entire subtree rooted in this node.
|
||||
/// Otherwise, only this node's kind and range are written.
|
||||
///
|
||||
/// To avoid allocating for every node, see [`write_debug`](SyntaxNode::write_debug).
|
||||
#[inline]
|
||||
pub fn debug<R>(&self, resolver: &R, recursive: bool) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
// NOTE: `fmt::Write` methods on `String` never fail
|
||||
let mut res = String::new();
|
||||
self.write_debug(resolver, &mut res, recursive).unwrap();
|
||||
res
|
||||
}
|
||||
|
||||
/// Writes this node's [`Display`](fmt::Display) representation into the given `target`.
|
||||
pub fn write_display<R>(&self, resolver: &R, target: &mut impl fmt::Write) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
self.preorder_with_tokens()
|
||||
.filter_map(|event| match event {
|
||||
WalkEvent::Enter(NodeOrToken::Token(token)) => Some(token),
|
||||
_ => None,
|
||||
})
|
||||
.try_for_each(|it| write!(res, "{}", it.display(resolver)))
|
||||
.unwrap();
|
||||
.try_for_each(|it| it.write_display(resolver, target))
|
||||
}
|
||||
|
||||
/// Returns this node's [`Display`](fmt::Display) representation as a string.
|
||||
///
|
||||
/// To avoid allocating for every node, see [`write_display`](SyntaxNode::write_display).
|
||||
#[inline]
|
||||
pub fn display<R>(&self, resolver: &R) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
// NOTE: `fmt::Write` methods on `String` never fail
|
||||
let mut res = String::new();
|
||||
self.write_display(resolver, &mut res).unwrap();
|
||||
res
|
||||
}
|
||||
|
||||
|
|
|
@ -184,13 +184,13 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
|||
|
||||
impl<L: Language, D> fmt::Debug for ResolvedNode<L, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.debug(&**self.resolver(), f.alternate()))
|
||||
self.write_debug(&**self.resolver(), f, f.alternate())
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Language, D> fmt::Display for ResolvedNode<L, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.display(&**self.resolver()))
|
||||
self.write_display(&**self.resolver(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,19 +198,19 @@ impl<L: Language, D> ResolvedToken<L, D> {
|
|||
/// Uses the resolver associated with this tree to return the source text of this token.
|
||||
#[inline]
|
||||
pub fn text(&self) -> &str {
|
||||
self.green().text(&**self.parent().resolver())
|
||||
self.green().text(&**self.resolver())
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Language, D> fmt::Debug for ResolvedToken<L, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.debug(&**self.parent().resolver()))
|
||||
self.write_debug(&**self.resolver(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: Language, D> fmt::Display for ResolvedToken<L, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.display(&**self.parent().resolver()))
|
||||
self.write_display(&**self.resolver(), f)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,7 +262,7 @@ macro_rules! forward_node {
|
|||
}
|
||||
|
||||
impl<L: Language, D> ResolvedNode<L, D> {
|
||||
/// If there is a resolver associated with this tree, returns it.
|
||||
/// Returns the [`Resolver`] associated with this tree.
|
||||
pub fn resolver(&self) -> &StdArc<dyn Resolver> {
|
||||
self.syntax.resolver().unwrap()
|
||||
}
|
||||
|
@ -494,6 +494,11 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
|||
}
|
||||
|
||||
impl<L: Language, D> ResolvedToken<L, D> {
|
||||
/// Returns the [`Resolver`] associated with this tree.
|
||||
pub fn resolver(&self) -> &StdArc<dyn Resolver> {
|
||||
self.syntax.resolver().unwrap()
|
||||
}
|
||||
|
||||
/// Always returns `Some(self)`.
|
||||
///
|
||||
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxToken`] vs [`ResolvedToken`].
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::{
|
||||
fmt::Write,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter,
|
||||
sync::Arc as StdArc,
|
||||
};
|
||||
|
||||
use lasso::Resolver;
|
||||
|
@ -45,29 +46,51 @@ impl<L: Language, D> PartialEq for SyntaxToken<L, D> {
|
|||
impl<L: Language, D> Eq for SyntaxToken<L, D> {}
|
||||
|
||||
impl<L: Language, D> SyntaxToken<L, D> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn debug<R>(&self, resolver: &R) -> String
|
||||
/// Writes this token's [`Debug`](fmt::Debug) representation into the given `target`.
|
||||
pub fn write_debug<R>(&self, resolver: &R, target: &mut impl fmt::Write) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
let mut res = String::new();
|
||||
write!(res, "{:?}@{:?}", self.kind(), self.text_range()).unwrap();
|
||||
if self.resolve_text(resolver).len() < 25 {
|
||||
write!(res, " {:?}", self.resolve_text(resolver)).unwrap();
|
||||
return res;
|
||||
}
|
||||
write!(target, "{:?}@{:?}", self.kind(), self.text_range())?;
|
||||
let text = self.resolve_text(resolver);
|
||||
if text.len() < 25 {
|
||||
return write!(target, " {:?}", text);
|
||||
}
|
||||
|
||||
for idx in 21..25 {
|
||||
if text.is_char_boundary(idx) {
|
||||
let text = format!("{} ...", &text[..idx]);
|
||||
write!(res, " {:?}", text).unwrap();
|
||||
return res;
|
||||
return write!(target, r#" "{} ...""#, &text[..idx]);
|
||||
}
|
||||
}
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
/// Returns this token's [`Debug`](fmt::Debug) representation as a string.
|
||||
///
|
||||
/// To avoid allocating for every token, see [`write_debug`](SyntaxToken::write_debug).
|
||||
pub fn debug<R>(&self, resolver: &R) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
// NOTE: `fmt::Write` methods on `String` never fail
|
||||
let mut res = String::new();
|
||||
self.write_debug(resolver, &mut res).unwrap();
|
||||
res
|
||||
}
|
||||
|
||||
/// Writes this token's [`Display`](fmt::Display) representation into the given `target`.
|
||||
#[inline]
|
||||
pub fn write_display<R>(&self, resolver: &R, target: &mut impl fmt::Write) -> fmt::Result
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
{
|
||||
write!(target, "{}", self.resolve_text(resolver))
|
||||
}
|
||||
|
||||
/// Returns this token's [`Display`](fmt::Display) representation as a string.
|
||||
///
|
||||
/// To avoid allocating for every token, see [`write_display`](SyntaxToken::write_display).
|
||||
#[inline]
|
||||
pub fn display<R>(&self, resolver: &R) -> String
|
||||
where
|
||||
R: Resolver + ?Sized,
|
||||
|
@ -75,13 +98,17 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
|||
self.resolve_text(resolver).to_string()
|
||||
}
|
||||
|
||||
/// If there is a resolver associated with this tree, returns it.
|
||||
#[inline]
|
||||
pub fn resolver(&self) -> Option<&StdArc<dyn Resolver>> {
|
||||
self.parent.resolver()
|
||||
}
|
||||
|
||||
/// Turns this token into a [`ResolvedToken`], but only if there is a resolver associated with this tree.
|
||||
#[inline]
|
||||
pub fn try_resolved(&self) -> Option<&ResolvedToken<L, D>> {
|
||||
// safety: we only coerce if `resolver` exists
|
||||
self.parent()
|
||||
.resolver()
|
||||
.map(|_| unsafe { ResolvedToken::coerce_ref(self) })
|
||||
self.resolver().map(|_| unsafe { ResolvedToken::coerce_ref(self) })
|
||||
}
|
||||
|
||||
/// Turns this token into a [`ResolvedToken`].
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue