mirror of
https://github.com/RGBCube/cstree
synced 2025-07-27 09:07:44 +00:00
Add derive macro for Syntax
(used to be Language
) (#51)
This commit is contained in:
parent
2aa543036f
commit
c5279bae7d
70 changed files with 1459 additions and 899 deletions
|
@ -3,10 +3,13 @@
|
||||||
## `v0.12.0`
|
## `v0.12.0`
|
||||||
|
|
||||||
* Documentation has been improved in most areas, together with a switch to a more principled module structure that allows explicitly documenting submodules.
|
* Documentation has been improved in most areas, together with a switch to a more principled module structure that allows explicitly documenting submodules.
|
||||||
|
* The `Language` trait has been deprecated in favour of a new `Syntax` trait. `Syntax` provides the same methods that `Language` did before, but is implemented directly on the syntax kind enum instead of an additional type representing the language.
|
||||||
|
* The supertrait requirements on `PartialOrd`, `Ord`, and `Hash` have been dropped.
|
||||||
|
* TODO: this allows to optionally provide derive. To enable, add feature flag
|
||||||
* The `interning` module has been rewritten. It now provides fuctions for obtaining a default interner (`new_interner` and `new_threaded_interner`) and provides a small, dependency-free interner implementation.
|
* The `interning` module has been rewritten. It now provides fuctions for obtaining a default interner (`new_interner` and `new_threaded_interner`) and provides a small, dependency-free interner implementation.
|
||||||
* Compatibility with other interners can be enable via feature flags.
|
* Compatibility with other interners can be enable via feature flags.
|
||||||
* **Note** that compatibilty with `lasso` is not enabled by default. Use the `lasso_compat` feature to match the previous default.
|
* **Note** that compatibilty with `lasso` is not enabled by default. Use the `lasso_compat` feature to match the previous default.
|
||||||
* Introduced `Language::static_text` to optimize tokens that always appear with the same text (estimated 10-15% faster tree building when used, depending on the ratio of static to dynamic tokens).
|
* Introduced `Syntax::static_text` to optimize tokens that always appear with the same text (estimated 10-15% faster tree building when used, depending on the ratio of static to dynamic tokens).
|
||||||
* Since `cstree`s are lossless, `GreenNodeBuilder::token` must still be passed the source text even for static tokens.
|
* Since `cstree`s are lossless, `GreenNodeBuilder::token` must still be passed the source text even for static tokens.
|
||||||
* Internal performance improvements for up to 10% faster tree building by avoiding unnecessary duplication of elements.
|
* Internal performance improvements for up to 10% faster tree building by avoiding unnecessary duplication of elements.
|
||||||
* Use `NonNull` for the internal representation of `SyntaxNode`, meaning it now benefits from niche optimizations (`Option<SyntaxNode>` is now the same size as `SyntaxNode` itself: the size of a pointer).
|
* Use `NonNull` for the internal representation of `SyntaxNode`, meaning it now benefits from niche optimizations (`Option<SyntaxNode>` is now the same size as `SyntaxNode` itself: the size of a pointer).
|
||||||
|
@ -14,7 +17,7 @@
|
||||||
* `RawSyntaxKind` has been changed to use a 32-bit index internally, which means existing `Language` implementations and syntax kind `enum`s need to be adjusted to `#[repr(u32)]` and the corresponding conversions.
|
* `RawSyntaxKind` has been changed to use a 32-bit index internally, which means existing `Language` implementations and syntax kind `enum`s need to be adjusted to `#[repr(u32)]` and the corresponding conversions.
|
||||||
* The crate's export module structure has been reorganized to give different groups of definitions their own submodules. A `cstree::prelude` module is available, containing the most commonly needed types that were previously accessible via `use cstree::*`. Otherwise, the module structure is now as follows:
|
* The crate's export module structure has been reorganized to give different groups of definitions their own submodules. A `cstree::prelude` module is available, containing the most commonly needed types that were previously accessible via `use cstree::*`. Otherwise, the module structure is now as follows:
|
||||||
* `cstree`
|
* `cstree`
|
||||||
* `Language`
|
* `Syntax`
|
||||||
* `RawSyntaxKind`
|
* `RawSyntaxKind`
|
||||||
* `build`
|
* `build`
|
||||||
* `GreenNodeBuilder`
|
* `GreenNodeBuilder`
|
||||||
|
|
75
Cargo.toml
75
Cargo.toml
|
@ -1,6 +1,12 @@
|
||||||
[package]
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"cstree",
|
||||||
|
"cstree-derive",
|
||||||
|
"test_suite",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
name = "cstree"
|
|
||||||
version = "0.12.0-rc.0" # when updating, also update `#![doc(html_root_url)]`
|
version = "0.12.0-rc.0" # when updating, also update `#![doc(html_root_url)]`
|
||||||
authors = [
|
authors = [
|
||||||
"Domenic Quirl <DomenicQuirl@pm.me>",
|
"Domenic Quirl <DomenicQuirl@pm.me>",
|
||||||
|
@ -10,70 +16,7 @@ description = "Library for generic lossless syntax trees"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/domenicquirl/cstree"
|
repository = "https://github.com/domenicquirl/cstree"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
rust-version = "1.68"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
text-size = "1.1.0"
|
|
||||||
fxhash = "0.2.1"
|
|
||||||
parking_lot = "0.12.1"
|
|
||||||
|
|
||||||
# Arc
|
|
||||||
triomphe = "0.1.7"
|
|
||||||
sptr = "0.3.2"
|
|
||||||
|
|
||||||
# Default Interner
|
|
||||||
indexmap = "1.9"
|
|
||||||
|
|
||||||
[dependencies.lasso]
|
|
||||||
version = "0.6"
|
|
||||||
features = ["inline-more"]
|
|
||||||
optional = true
|
|
||||||
|
|
||||||
[dependencies.salsa]
|
|
||||||
git = "https://github.com/salsa-rs/salsa/"
|
|
||||||
version = "0.1"
|
|
||||||
optional = true
|
|
||||||
package = "salsa-2022"
|
|
||||||
|
|
||||||
[dependencies.serde]
|
|
||||||
version = "1.0"
|
|
||||||
optional = true
|
|
||||||
default-features = false
|
|
||||||
features = ["derive", "std"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
m_lexer = "0.0.4"
|
|
||||||
serde_json = "1.0"
|
|
||||||
serde_test = "1.0"
|
|
||||||
crossbeam-utils = "0.8"
|
|
||||||
criterion = "0.3"
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "main"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
# Implementations of `serde::{De,}Serialize` for CSTrees.
|
|
||||||
serialize = ["serde", "lasso?/serialize"]
|
|
||||||
# Interoperability with the `lasso` interning crate.
|
|
||||||
# When enabled, `cstree`'s default interners will use `lasso` internally, too.
|
|
||||||
lasso_compat = ["lasso"]
|
|
||||||
# Additionally provide threadsafe interner types.
|
|
||||||
# Where applicable (and if the corresponding features are selected), provide compatibility
|
|
||||||
# implementations for multi-thread interners from other crates.
|
|
||||||
multi_threaded_interning = ["lasso_compat", "lasso/multi-threaded"]
|
|
||||||
# Interoperability with the `salsa` framework for incremental computation.
|
|
||||||
# Use this feature for "Salsa 2022".
|
|
||||||
# WARNING: This feature is considered unstable!
|
|
||||||
salsa_2022_compat = ["salsa"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "salsa"
|
|
||||||
required-features = ["salsa_2022_compat"]
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
|
||||||
all-features = true
|
|
||||||
rustdoc-args = ["--cfg", "doc_cfg"]
|
|
||||||
|
|
50
README.md
50
README.md
|
@ -38,7 +38,7 @@ concrete syntax trees as its output. We'll talk more about parsing below -- firs
|
||||||
to happen to go from input text to a `cstree` syntax tree:
|
to happen to go from input text to a `cstree` syntax tree:
|
||||||
|
|
||||||
1. Define an enumeration of the types of tokens (like keywords) and nodes (like "an expression")
|
1. Define an enumeration of the types of tokens (like keywords) and nodes (like "an expression")
|
||||||
that you want to have in your syntax and implement `Language`
|
that you want to have in your syntax and implement `Syntax`
|
||||||
|
|
||||||
2. Create a `GreenNodeBuilder` and call `start_node`, `token` and `finish_node` from your parser
|
2. Create a `GreenNodeBuilder` and call `start_node`, `token` and `finish_node` from your parser
|
||||||
|
|
||||||
|
@ -52,12 +52,12 @@ compound expression. They will, however, be allowed to write nested expressions
|
||||||
### Defining the language
|
### Defining the language
|
||||||
First, we need to list the different part of our language's grammar.
|
First, we need to list the different part of our language's grammar.
|
||||||
We can do that using an `enum` with a unit variant for any terminal and non-terminal.
|
We can do that using an `enum` with a unit variant for any terminal and non-terminal.
|
||||||
The `enum` needs to be convertible to a `u16`, so we use the `repr` attribute to ensure it uses the correct
|
The `enum` needs to be convertible to a `u32`, so we use the `repr` attribute to ensure it uses the correct
|
||||||
representation.
|
representation.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(u16)]
|
#[repr(u32)]
|
||||||
enum SyntaxKind {
|
enum SyntaxKind {
|
||||||
/* Tokens */
|
/* Tokens */
|
||||||
Int, // 42
|
Int, // 42
|
||||||
|
@ -71,6 +71,15 @@ enum SyntaxKind {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
For convenience when we're working with generic `cstree` types like `SyntaxNode`, we'll also give a
|
||||||
|
name to our syntax as a whole and add a type alias for it.
|
||||||
|
That way, we can match against `SyntaxKind`s using the original name, but use the more informative
|
||||||
|
`Node<Calculator>` to instantiate `cstree`'s types.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
type Calculator = SyntaxKind;
|
||||||
|
```
|
||||||
|
|
||||||
Most of these are tokens to lex the input string into, like numbers (`Int`) and operators (`Plus`, `Minus`).
|
Most of these are tokens to lex the input string into, like numbers (`Int`) and operators (`Plus`, `Minus`).
|
||||||
We only really need one type of node; expressions.
|
We only really need one type of node; expressions.
|
||||||
Our syntax tree's root node will have the special kind `Root`, all other nodes will be
|
Our syntax tree's root node will have the special kind `Root`, all other nodes will be
|
||||||
|
@ -78,22 +87,16 @@ expressions containing a sequence of arithmetic operations potentially involving
|
||||||
expression nodes.
|
expression nodes.
|
||||||
|
|
||||||
To use our `SyntaxKind`s with `cstree`, we need to tell it how to convert it back to just a number (the
|
To use our `SyntaxKind`s with `cstree`, we need to tell it how to convert it back to just a number (the
|
||||||
`#[repr(u16)]` that we added) by implementing the `Language` trait. We can also tell `cstree` about tokens that
|
`#[repr(u32)]` that we added) by implementing the `Syntax` trait. We can also tell `cstree` about tokens that
|
||||||
always have the same text through the `static_text` method on the trait. This is useful for the operators and
|
always have the same text through the `static_text` method on the trait. This is useful for the operators and
|
||||||
parentheses, but not possible for numbers, since an integer token may be produced from the input `3`, but also from
|
parentheses, but not possible for numbers, since an integer token may be produced from the input `3`, but also from
|
||||||
other numbers like `7` or `12`. We implement `Language` on an empty type, just so we can give it a name.
|
other numbers like `7` or `12`. We implement `Syntax` on an empty type, just so we can give it a name.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
impl Syntax for Calculator {
|
||||||
pub struct Calculator;
|
fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
|
// This just needs to be the inverse of `into_raw`, but could also
|
||||||
impl Language for Calculator {
|
// be an `impl TryFrom<u32> for SyntaxKind` or any other conversion.
|
||||||
// The tokens and nodes we just defined
|
|
||||||
type Kind = SyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
|
||||||
// This just needs to be the inverse of `kind_to_raw`, but could also
|
|
||||||
// be an `impl TryFrom<u16> for SyntaxKind` or any other conversion.
|
|
||||||
match raw.0 {
|
match raw.0 {
|
||||||
0 => SyntaxKind::Int,
|
0 => SyntaxKind::Int,
|
||||||
1 => SyntaxKind::Plus,
|
1 => SyntaxKind::Plus,
|
||||||
|
@ -106,12 +109,12 @@ impl Language for Calculator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
fn ino_raw(self) -> RawSyntaxKind {
|
||||||
RawSyntaxKind(kind as u16)
|
RawSyntaxKind(self as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
fn static_text(self) -> Option<&'static str> {
|
||||||
match kind {
|
match self {
|
||||||
SyntaxKind::Plus => Some("+"),
|
SyntaxKind::Plus => Some("+"),
|
||||||
SyntaxKind::Minus => Some("-"),
|
SyntaxKind::Minus => Some("-"),
|
||||||
SyntaxKind::LParen => Some("("),
|
SyntaxKind::LParen => Some("("),
|
||||||
|
@ -122,7 +125,14 @@ impl Language for Calculator {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Deriving `Syntax`
|
||||||
|
To save yourself the hassle of defining this conversion (and, perhaps more importantly, continually updating it
|
||||||
|
while your language's syntax is in flux), `cstree` includes a derive macro for `Syntax` when built with the `derive`
|
||||||
|
feature. With the macro, the `Syntax` trait implementation above can be replaced by simply adding
|
||||||
|
`#[derive(Syntax)]` to `SyntaxKind`.
|
||||||
|
|
||||||
### Parsing into a green tree
|
### Parsing into a green tree
|
||||||
|
|
||||||
With that out of the way, we can start writing the parser for our expressions.
|
With that out of the way, we can start writing the parser for our expressions.
|
||||||
For the purposes of this introduction to `cstree`, I'll assume that there is a lexer that yields the following
|
For the purposes of this introduction to `cstree`, I'll assume that there is a lexer that yields the following
|
||||||
tokens:
|
tokens:
|
||||||
|
|
18
cstree-derive/Cargo.toml
Normal file
18
cstree-derive/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[package]
|
||||||
|
name = "cstree_derive"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "cstree_derive"
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0.56"
|
||||||
|
quote = "1.0.26"
|
||||||
|
syn = { version = "2.0.14" }
|
1
cstree-derive/LICENSE-APACHE
Symbolic link
1
cstree-derive/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../LICENSE-APACHE
|
1
cstree-derive/LICENSE-MIT
Symbolic link
1
cstree-derive/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../LICENSE-MIT
|
1
cstree-derive/README.md
Symbolic link
1
cstree-derive/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../README.md
|
56
cstree-derive/src/errors.rs
Normal file
56
cstree-derive/src/errors.rs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
use std::{cell::RefCell, fmt, thread};
|
||||||
|
|
||||||
|
use quote::ToTokens;
|
||||||
|
|
||||||
|
/// Context to collect multiple errors and output them all after parsing in order to not abort
|
||||||
|
/// immediately on the first error.
|
||||||
|
///
|
||||||
|
/// Ensures that the errors are handled using [`check`](ErrorContext::check) by otherwise panicking
|
||||||
|
/// on `Drop`.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub(crate) struct ErrorContext {
|
||||||
|
errors: RefCell<Option<Vec<syn::Error>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ErrorContext {
|
||||||
|
/// Create a new context.
|
||||||
|
///
|
||||||
|
/// This context contains no errors, but will still trigger a panic if it is not `check`ed.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ErrorContext {
|
||||||
|
errors: RefCell::new(Some(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an error to the context that points to `source`.
|
||||||
|
pub fn error_at<S: ToTokens, T: fmt::Display>(&self, source: S, msg: T) {
|
||||||
|
self.errors
|
||||||
|
.borrow_mut()
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
// Transform `ToTokens` here so we don't monomorphize `new_spanned` so much.
|
||||||
|
.push(syn::Error::new_spanned(source.into_token_stream(), msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a `syn` parse error directly.
|
||||||
|
pub fn syn_error(&self, err: syn::Error) {
|
||||||
|
self.errors.borrow_mut().as_mut().unwrap().push(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume the context, producing a formatted error string if there are errors.
|
||||||
|
pub fn check(self) -> Result<(), Vec<syn::Error>> {
|
||||||
|
let errors = self.errors.borrow_mut().take().unwrap();
|
||||||
|
match errors.len() {
|
||||||
|
0 => Ok(()),
|
||||||
|
_ => Err(errors),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ErrorContext {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if !thread::panicking() && self.errors.borrow().is_some() {
|
||||||
|
panic!("forgot to check for errors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
cstree-derive/src/lib.rs
Normal file
74
cstree-derive/src/lib.rs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
use errors::ErrorContext;
|
||||||
|
use parsing::SyntaxKindEnum;
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{quote, quote_spanned};
|
||||||
|
use syn::{parse_macro_input, spanned::Spanned, DeriveInput};
|
||||||
|
|
||||||
|
mod errors;
|
||||||
|
mod parsing;
|
||||||
|
mod symbols;
|
||||||
|
|
||||||
|
use symbols::*;
|
||||||
|
|
||||||
|
#[proc_macro_derive(Syntax, attributes(static_text))]
|
||||||
|
pub fn language(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let ast = parse_macro_input!(input as DeriveInput);
|
||||||
|
expand_syntax(ast).unwrap_or_else(to_compile_errors).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expand_syntax(ast: DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
|
||||||
|
let error_handler = ErrorContext::new();
|
||||||
|
let Ok(syntax_kind_enum) = SyntaxKindEnum::parse_from_ast(&error_handler, &ast) else {
|
||||||
|
return Err(error_handler.check().unwrap_err());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that the `enum` is `#[repr(u32)]`
|
||||||
|
match &syntax_kind_enum.repr {
|
||||||
|
Some(repr) if repr == U32 => (),
|
||||||
|
Some(_) | None => error_handler.error_at(
|
||||||
|
syntax_kind_enum.source,
|
||||||
|
"syntax kind definitions must be `#[repr(u32)]` to derive `Syntax`",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
error_handler.check()?;
|
||||||
|
|
||||||
|
let name = &syntax_kind_enum.name;
|
||||||
|
let variant_count = syntax_kind_enum.variants.len() as u32;
|
||||||
|
let static_texts = syntax_kind_enum.variants.iter().map(|variant| {
|
||||||
|
let variant_name = &variant.name;
|
||||||
|
let static_text = match variant.static_text.as_deref() {
|
||||||
|
Some(text) => quote!(::core::option::Option::Some(#text)),
|
||||||
|
None => quote!(::core::option::Option::None),
|
||||||
|
};
|
||||||
|
quote_spanned!(variant.source.span()=>
|
||||||
|
#name :: #variant_name => #static_text,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let trait_impl = quote_spanned! { syntax_kind_enum.source.span()=>
|
||||||
|
#[automatically_derived]
|
||||||
|
impl ::cstree::Syntax for #name {
|
||||||
|
fn from_raw(raw: ::cstree::RawSyntaxKind) -> Self {
|
||||||
|
assert!(raw.0 < #variant_count, "Invalid raw syntax kind: {}", raw.0);
|
||||||
|
// Safety: discriminant is valid by the assert above
|
||||||
|
unsafe { ::std::mem::transmute::<u32, #name>(raw.0) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw(self) -> ::cstree::RawSyntaxKind {
|
||||||
|
::cstree::RawSyntaxKind(self as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_text(self) -> ::core::option::Option<&'static str> {
|
||||||
|
match self {
|
||||||
|
#( #static_texts )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(trait_impl)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
|
||||||
|
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
|
||||||
|
quote!(#(#compile_errors)*)
|
||||||
|
}
|
131
cstree-derive/src/parsing.rs
Normal file
131
cstree-derive/src/parsing.rs
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
mod attributes;
|
||||||
|
|
||||||
|
use syn::{punctuated::Punctuated, Token};
|
||||||
|
|
||||||
|
use crate::{errors::ErrorContext, symbols::*};
|
||||||
|
|
||||||
|
use self::attributes::Attr;
|
||||||
|
|
||||||
|
/// Convenience for recording errors inside `ErrorContext` instead of the `Err` variant of the `Result`.
|
||||||
|
pub(crate) type Result<T, E = ()> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
pub(crate) struct SyntaxKindEnum<'i> {
|
||||||
|
pub(crate) name: syn::Ident,
|
||||||
|
pub(crate) repr: Option<syn::Ident>,
|
||||||
|
pub(crate) variants: Vec<SyntaxKindVariant<'i>>,
|
||||||
|
pub(crate) source: &'i syn::DeriveInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> SyntaxKindEnum<'i> {
|
||||||
|
pub(crate) fn parse_from_ast(error_handler: &ErrorContext, item: &'i syn::DeriveInput) -> Result<Self> {
|
||||||
|
let syn::Data::Enum(data) = &item.data else {
|
||||||
|
error_handler.error_at(item, "`Syntax` can only be derived on enums");
|
||||||
|
return Err(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = item.ident.clone();
|
||||||
|
|
||||||
|
let mut repr = Attr::none(error_handler, REPR);
|
||||||
|
for repr_attr in item.attrs.iter().filter(|&attr| attr.path().is_ident(&REPR)) {
|
||||||
|
if let syn::Meta::List(nested) = &repr_attr.meta {
|
||||||
|
if let Ok(nested) = nested.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated) {
|
||||||
|
for meta in nested {
|
||||||
|
if let syn::Meta::Path(path) = meta {
|
||||||
|
if let Some(ident) = path.get_ident() {
|
||||||
|
repr.set(repr_attr, ident.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let variants = data
|
||||||
|
.variants
|
||||||
|
.iter()
|
||||||
|
.map(|variant| SyntaxKindVariant::parse_from_ast(error_handler, variant))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
name,
|
||||||
|
repr: repr.get(),
|
||||||
|
variants,
|
||||||
|
source: item,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct SyntaxKindVariant<'i> {
|
||||||
|
pub(crate) name: syn::Ident,
|
||||||
|
pub(crate) static_text: Option<String>,
|
||||||
|
pub(crate) source: &'i syn::Variant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i> SyntaxKindVariant<'i> {
|
||||||
|
pub(crate) fn parse_from_ast(error_handler: &ErrorContext, variant: &'i syn::Variant) -> Self {
|
||||||
|
let name = variant.ident.clone();
|
||||||
|
|
||||||
|
// Check that `variant` is a unit variant
|
||||||
|
match &variant.fields {
|
||||||
|
syn::Fields::Unit => (),
|
||||||
|
syn::Fields::Named(_) | syn::Fields::Unnamed(_) => {
|
||||||
|
error_handler.error_at(variant, "syntax kinds with fields are not supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that discriminants are unaltered
|
||||||
|
if variant.discriminant.is_some() {
|
||||||
|
error_handler.error_at(
|
||||||
|
variant,
|
||||||
|
"syntax kinds are not allowed to have custom discriminant values",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut static_text = Attr::none(error_handler, STATIC_TEXT);
|
||||||
|
for text in variant
|
||||||
|
.attrs
|
||||||
|
.iter()
|
||||||
|
.flat_map(|attr| get_static_text(error_handler, attr))
|
||||||
|
{
|
||||||
|
static_text.set(&text, text.value());
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
static_text: static_text.get(),
|
||||||
|
source: variant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_static_text(error_handler: &ErrorContext, attr: &syn::Attribute) -> Option<syn::LitStr> {
|
||||||
|
use syn::Meta::*;
|
||||||
|
|
||||||
|
if attr.path() != STATIC_TEXT {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match &attr.meta {
|
||||||
|
List(list) => match list.parse_args() {
|
||||||
|
Ok(lit) => Some(lit),
|
||||||
|
Err(e) => {
|
||||||
|
error_handler.error_at(
|
||||||
|
list,
|
||||||
|
"argument to `static_text` must be a string literal: `#[static_text(\"...\")]`",
|
||||||
|
);
|
||||||
|
error_handler.syn_error(e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Path(_) => {
|
||||||
|
error_handler.error_at(attr, "missing text for `static_text`: try `#[static_text(\"...\")]`");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
NameValue(_) => {
|
||||||
|
error_handler.error_at(
|
||||||
|
attr,
|
||||||
|
"`static_text` takes the text as a function argument: `#[static_text(\"...\")]`",
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
59
cstree-derive/src/parsing/attributes.rs
Normal file
59
cstree-derive/src/parsing/attributes.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::ToTokens;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Attr<'i, T> {
|
||||||
|
error_handler: &'i ErrorContext,
|
||||||
|
name: Symbol,
|
||||||
|
tokens: TokenStream,
|
||||||
|
value: Option<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'i, T> Attr<'i, T> {
|
||||||
|
pub(super) fn none(error_handler: &'i ErrorContext, name: Symbol) -> Self {
|
||||||
|
Attr {
|
||||||
|
error_handler,
|
||||||
|
name,
|
||||||
|
tokens: TokenStream::new(),
|
||||||
|
value: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set<S: ToTokens>(&mut self, source: S, value: T) {
|
||||||
|
let tokens = source.into_token_stream();
|
||||||
|
|
||||||
|
if self.value.is_some() {
|
||||||
|
self.error_handler
|
||||||
|
.error_at(tokens, format!("duplicate attribute: `{}`", self.name));
|
||||||
|
} else {
|
||||||
|
self.tokens = tokens;
|
||||||
|
self.value = Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_opt<S: ToTokens>(&mut self, source: S, value: Option<T>) {
|
||||||
|
if let Some(value) = value {
|
||||||
|
self.set(source, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn set_if_none(&mut self, value: T) {
|
||||||
|
if self.value.is_none() {
|
||||||
|
self.value = Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get(self) -> Option<T> {
|
||||||
|
self.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_with_tokens(self) -> Option<(TokenStream, T)> {
|
||||||
|
match self.value {
|
||||||
|
Some(v) => Some((self.tokens, v)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
50
cstree-derive/src/symbols.rs
Normal file
50
cstree-derive/src/symbols.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use std::fmt::{self};
|
||||||
|
use syn::{Ident, Path};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Symbol(&'static str);
|
||||||
|
|
||||||
|
pub const STATIC_TEXT: Symbol = Symbol("static_text");
|
||||||
|
pub const REPR: Symbol = Symbol("repr");
|
||||||
|
pub const U32: Symbol = Symbol("u32");
|
||||||
|
|
||||||
|
impl Symbol {
|
||||||
|
pub const fn new(text: &'static str) -> Self {
|
||||||
|
Self(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Symbol> for Ident {
|
||||||
|
fn eq(&self, word: &Symbol) -> bool {
|
||||||
|
self == word.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq<Symbol> for &'a Ident {
|
||||||
|
fn eq(&self, word: &Symbol) -> bool {
|
||||||
|
*self == word.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq<Symbol> for Path {
|
||||||
|
fn eq(&self, word: &Symbol) -> bool {
|
||||||
|
self.is_ident(word.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq<Symbol> for &'a Path {
|
||||||
|
fn eq(&self, word: &Symbol) -> bool {
|
||||||
|
self.is_ident(word.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Symbol {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Debug for Symbol {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.debug_tuple("Symbol").field(&self.0).finish()
|
||||||
|
}
|
||||||
|
}
|
79
cstree/Cargo.toml
Normal file
79
cstree/Cargo.toml
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
[package]
|
||||||
|
name = "cstree"
|
||||||
|
edition.workspace = true
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
text-size = "1.1.0"
|
||||||
|
fxhash = "0.2.1"
|
||||||
|
parking_lot = "0.12.1"
|
||||||
|
|
||||||
|
# Arc
|
||||||
|
triomphe = "0.1.7"
|
||||||
|
sptr = "0.3.2"
|
||||||
|
|
||||||
|
# Default Interner
|
||||||
|
indexmap = "1.9"
|
||||||
|
|
||||||
|
[dependencies.cstree_derive]
|
||||||
|
path = "../cstree-derive"
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.lasso]
|
||||||
|
version = "0.6"
|
||||||
|
features = ["inline-more"]
|
||||||
|
optional = true
|
||||||
|
|
||||||
|
[dependencies.salsa]
|
||||||
|
git = "https://github.com/salsa-rs/salsa/"
|
||||||
|
version = "0.1"
|
||||||
|
optional = true
|
||||||
|
package = "salsa-2022"
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0"
|
||||||
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
features = ["derive", "std"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
m_lexer = "0.0.4"
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde_test = "1.0"
|
||||||
|
crossbeam-utils = "0.8"
|
||||||
|
criterion = "0.3"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "main"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
# Derive macro for `Syntax`
|
||||||
|
derive = ["dep:cstree_derive"]
|
||||||
|
# Implementations of `serde::{De,}Serialize` for CSTrees.
|
||||||
|
serialize = ["serde", "lasso?/serialize"]
|
||||||
|
# Interoperability with the `lasso` interning crate.
|
||||||
|
# When enabled, `cstree`'s default interners will use `lasso` internally, too.
|
||||||
|
lasso_compat = ["lasso"]
|
||||||
|
# Additionally provide threadsafe interner types.
|
||||||
|
# Where applicable (and if the corresponding features are selected), provide compatibility
|
||||||
|
# implementations for multi-thread interners from other crates.
|
||||||
|
multi_threaded_interning = ["lasso_compat", "lasso/multi-threaded"]
|
||||||
|
# Interoperability with the `salsa` framework for incremental computation.
|
||||||
|
# Use this feature for "Salsa 2022".
|
||||||
|
# WARNING: This feature is considered unstable!
|
||||||
|
salsa_2022_compat = ["salsa"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "salsa"
|
||||||
|
required-features = ["salsa_2022_compat"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "doc_cfg"]
|
1
cstree/LICENSE-APACHE
Symbolic link
1
cstree/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../LICENSE-APACHE
|
1
cstree/LICENSE-MIT
Symbolic link
1
cstree/LICENSE-MIT
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../LICENSE-MIT
|
1
cstree/README.md
Symbolic link
1
cstree/README.md
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../README.md
|
|
@ -3,9 +3,8 @@ use cstree::{
|
||||||
build::*,
|
build::*,
|
||||||
green::GreenNode,
|
green::GreenNode,
|
||||||
interning::{new_interner, Interner},
|
interning::{new_interner, Interner},
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
use std::{fmt, hash::Hash};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Element<'s> {
|
pub enum Element<'s> {
|
||||||
|
@ -14,37 +13,14 @@ pub enum Element<'s> {
|
||||||
Plus,
|
Plus,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum TestKind {
|
pub enum TestKind {
|
||||||
Element { n: u32 },
|
Element { n: u32 },
|
||||||
Plus,
|
Plus,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Bool: Hash + Ord + fmt::Debug + Copy {
|
impl Syntax for TestKind {
|
||||||
const VALUE: bool;
|
fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
||||||
pub struct TestLang<T: Bool> {
|
|
||||||
_marker: std::marker::PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct NoStaticText;
|
|
||||||
impl Bool for NoStaticText {
|
|
||||||
const VALUE: bool = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub struct UseStaticText;
|
|
||||||
impl Bool for UseStaticText {
|
|
||||||
const VALUE: bool = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Bool> Language for TestLang<T> {
|
|
||||||
type Kind = TestKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
|
||||||
if raw.0 == u32::MAX - 1 {
|
if raw.0 == u32::MAX - 1 {
|
||||||
TestKind::Plus
|
TestKind::Plus
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,40 +28,37 @@ impl<T: Bool> Language for TestLang<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
fn into_raw(self) -> RawSyntaxKind {
|
||||||
match kind {
|
match self {
|
||||||
TestKind::Element { n } => RawSyntaxKind(n),
|
TestKind::Element { n } => RawSyntaxKind(n),
|
||||||
TestKind::Plus => RawSyntaxKind(u32::MAX - 1),
|
TestKind::Plus => RawSyntaxKind(u32::MAX - 1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
fn static_text(self) -> Option<&'static str> {
|
||||||
if !<T as Bool>::VALUE {
|
match self {
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
match kind {
|
|
||||||
TestKind::Plus => Some("+"),
|
TestKind::Plus => Some("+"),
|
||||||
TestKind::Element { .. } => None,
|
TestKind::Element { .. } => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_tree_with_cache<T: Bool, I>(root: &Element<'_>, cache: &mut NodeCache<'_, I>) -> GreenNode
|
pub fn build_tree_with_cache<I>(root: &Element<'_>, cache: &mut NodeCache<'_, I>, use_static_text: bool) -> GreenNode
|
||||||
where
|
where
|
||||||
I: Interner,
|
I: Interner,
|
||||||
{
|
{
|
||||||
let mut builder: GreenNodeBuilder<TestLang<T>, I> = GreenNodeBuilder::with_cache(cache);
|
let mut builder: GreenNodeBuilder<TestKind, I> = GreenNodeBuilder::with_cache(cache);
|
||||||
build_recursive(root, &mut builder, 0);
|
build_recursive(root, &mut builder, 0, use_static_text);
|
||||||
let (node, cache) = builder.finish();
|
let (node, cache) = builder.finish();
|
||||||
assert!(cache.is_none());
|
assert!(cache.is_none());
|
||||||
node
|
node
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_recursive<T: Bool, I>(
|
pub fn build_recursive<I>(
|
||||||
root: &Element<'_>,
|
root: &Element<'_>,
|
||||||
builder: &mut GreenNodeBuilder<'_, '_, TestLang<T>, I>,
|
builder: &mut GreenNodeBuilder<'_, '_, TestKind, I>,
|
||||||
mut from: u32,
|
mut from: u32,
|
||||||
|
use_static_text: bool,
|
||||||
) -> u32
|
) -> u32
|
||||||
where
|
where
|
||||||
I: Interner,
|
I: Interner,
|
||||||
|
@ -94,13 +67,16 @@ where
|
||||||
Element::Node(children) => {
|
Element::Node(children) => {
|
||||||
builder.start_node(TestKind::Element { n: from });
|
builder.start_node(TestKind::Element { n: from });
|
||||||
for child in children {
|
for child in children {
|
||||||
from = build_recursive(child, builder, from + 1);
|
from = build_recursive(child, builder, from + 1, use_static_text);
|
||||||
}
|
}
|
||||||
builder.finish_node();
|
builder.finish_node();
|
||||||
}
|
}
|
||||||
Element::Token(text) => {
|
Element::Token(text) => {
|
||||||
builder.token(TestKind::Element { n: from }, text);
|
builder.token(TestKind::Element { n: from }, text);
|
||||||
}
|
}
|
||||||
|
Element::Plus if use_static_text => {
|
||||||
|
builder.static_token(TestKind::Plus);
|
||||||
|
}
|
||||||
Element::Plus => {
|
Element::Plus => {
|
||||||
builder.token(TestKind::Plus, "+");
|
builder.token(TestKind::Plus, "+");
|
||||||
}
|
}
|
||||||
|
@ -132,14 +108,14 @@ pub fn create(c: &mut Criterion) {
|
||||||
|
|
||||||
group.bench_function("with static text", |b| {
|
group.bench_function("with static text", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let tree = build_tree_with_cache::<UseStaticText, _>(&tree, &mut cache);
|
let tree = build_tree_with_cache(&tree, &mut cache, true);
|
||||||
black_box(tree);
|
black_box(tree);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
group.bench_function("without static text", |b| {
|
group.bench_function("without static text", |b| {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let tree = build_tree_with_cache::<NoStaticText, _>(&tree, &mut cache);
|
let tree = build_tree_with_cache(&tree, &mut cache, false);
|
||||||
black_box(tree);
|
black_box(tree);
|
||||||
})
|
})
|
||||||
});
|
});
|
|
@ -13,17 +13,21 @@
|
||||||
//! - "+" Token(Add)
|
//! - "+" Token(Add)
|
||||||
//! - "4" Token(Number)
|
//! - "4" Token(Number)
|
||||||
|
|
||||||
use cstree::{build::GreenNodeBuilder, interning::Resolver, util::NodeOrToken};
|
use cstree::{build::GreenNodeBuilder, interning::Resolver, util::NodeOrToken, Syntax};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
enum SyntaxKind {
|
enum SyntaxKind {
|
||||||
Whitespace = 0,
|
Whitespace,
|
||||||
|
|
||||||
|
#[static_text("+")]
|
||||||
Add,
|
Add,
|
||||||
|
#[static_text("-")]
|
||||||
Sub,
|
Sub,
|
||||||
|
#[static_text("*")]
|
||||||
Mul,
|
Mul,
|
||||||
|
#[static_text("/")]
|
||||||
Div,
|
Div,
|
||||||
|
|
||||||
Number,
|
Number,
|
||||||
|
@ -31,6 +35,7 @@ enum SyntaxKind {
|
||||||
Operation,
|
Operation,
|
||||||
Root,
|
Root,
|
||||||
}
|
}
|
||||||
|
type MySyntax = SyntaxKind;
|
||||||
use SyntaxKind::*;
|
use SyntaxKind::*;
|
||||||
|
|
||||||
impl From<SyntaxKind> for cstree::RawSyntaxKind {
|
impl From<SyntaxKind> for cstree::RawSyntaxKind {
|
||||||
|
@ -39,40 +44,15 @@ impl From<SyntaxKind> for cstree::RawSyntaxKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
type SyntaxNode = cstree::syntax::SyntaxNode<MySyntax>;
|
||||||
enum Lang {}
|
|
||||||
impl cstree::Language for Lang {
|
|
||||||
type Kind = SyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: cstree::RawSyntaxKind) -> Self::Kind {
|
|
||||||
assert!(raw.0 <= Root as u32);
|
|
||||||
unsafe { std::mem::transmute::<u32, SyntaxKind>(raw.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> cstree::RawSyntaxKind {
|
|
||||||
kind.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
|
||||||
match kind {
|
|
||||||
Add => Some("+"),
|
|
||||||
Sub => Some("-"),
|
|
||||||
Mul => Some("*"),
|
|
||||||
Div => Some("/"),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type SyntaxNode = cstree::syntax::SyntaxNode<Lang>;
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
type SyntaxToken = cstree::syntax::SyntaxToken<Lang>;
|
type SyntaxToken = cstree::syntax::SyntaxToken<MySyntax>;
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
type SyntaxElement = cstree::util::NodeOrToken<SyntaxNode, SyntaxToken>;
|
type SyntaxElement = cstree::util::NodeOrToken<SyntaxNode, SyntaxToken>;
|
||||||
type SyntaxElementRef<'a> = cstree::util::NodeOrToken<&'a SyntaxNode, &'a SyntaxToken>;
|
type SyntaxElementRef<'a> = cstree::util::NodeOrToken<&'a SyntaxNode, &'a SyntaxToken>;
|
||||||
|
|
||||||
struct Parser<'input, I: Iterator<Item = (SyntaxKind, &'input str)>> {
|
struct Parser<'input, I: Iterator<Item = (SyntaxKind, &'input str)>> {
|
||||||
builder: GreenNodeBuilder<'static, 'static, Lang>,
|
builder: GreenNodeBuilder<'static, 'static, MySyntax>,
|
||||||
iter: Peekable<I>,
|
iter: Peekable<I>,
|
||||||
}
|
}
|
||||||
impl<'input, I: Iterator<Item = (SyntaxKind, &'input str)>> Parser<'input, I> {
|
impl<'input, I: Iterator<Item = (SyntaxKind, &'input str)>> Parser<'input, I> {
|
|
@ -6,7 +6,7 @@ use cstree::{
|
||||||
syntax::{ResolvedElementRef, ResolvedNode},
|
syntax::{ResolvedElementRef, ResolvedNode},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum SyntaxKind {
|
pub enum SyntaxKind {
|
||||||
/* Tokens */
|
/* Tokens */
|
||||||
|
@ -19,16 +19,12 @@ pub enum SyntaxKind {
|
||||||
Expr,
|
Expr,
|
||||||
Root,
|
Root,
|
||||||
}
|
}
|
||||||
|
type Calculator = SyntaxKind;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
impl Syntax for Calculator {
|
||||||
pub struct Calculator;
|
fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
impl Language for Calculator {
|
// This just needs to be the inverse of `into_raw`, but could also
|
||||||
// The tokens and nodes we just defined
|
// be an `impl TryFrom<u32> for SyntaxKind` or any other conversion.
|
||||||
type Kind = SyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
|
||||||
// This just needs to be the inverse of `kind_to_raw`, but could also
|
|
||||||
// be an `impl TryFrom<u16> for SyntaxKind` or any other conversion.
|
|
||||||
match raw.0 {
|
match raw.0 {
|
||||||
0 => SyntaxKind::Int,
|
0 => SyntaxKind::Int,
|
||||||
1 => SyntaxKind::Plus,
|
1 => SyntaxKind::Plus,
|
||||||
|
@ -41,12 +37,12 @@ impl Language for Calculator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
fn into_raw(self) -> RawSyntaxKind {
|
||||||
RawSyntaxKind(kind as u32)
|
RawSyntaxKind(self as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
fn static_text(self) -> Option<&'static str> {
|
||||||
match kind {
|
match self {
|
||||||
SyntaxKind::Plus => Some("+"),
|
SyntaxKind::Plus => Some("+"),
|
||||||
SyntaxKind::Minus => Some("-"),
|
SyntaxKind::Minus => Some("-"),
|
||||||
SyntaxKind::LParen => Some("("),
|
SyntaxKind::LParen => Some("("),
|
|
@ -7,11 +7,22 @@
|
||||||
//! You may want to follow the conceptual overview of the design alongside this tutorial:
|
//! You may want to follow the conceptual overview of the design alongside this tutorial:
|
||||||
//! https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md
|
//! https://github.com/rust-analyzer/rust-analyzer/blob/master/docs/dev/syntax.md
|
||||||
|
|
||||||
/// Let's start with defining all kinds of tokens and composite nodes.
|
use std::collections::VecDeque;
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
|
/// Let's start with defining all kinds of syntactical elements that we want to have in our
|
||||||
|
/// language's grammar. These correspond to tokens and composite nodes in the syntax tree that we
|
||||||
|
/// want to parse the S-expressions into.
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
/// Implementing the `Syntax` trait teaches `cstree` to convert our `SyntaxKind`s to its internal
|
||||||
|
/// types, allowing for a nicer `SyntaxNode` API where "kinds" are values from our `enum` instead of
|
||||||
|
/// plain `u32` values.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum SyntaxKind {
|
pub enum SyntaxKind {
|
||||||
LParen = 0, // '('
|
#[static_text("(")]
|
||||||
|
LParen, // '('
|
||||||
|
#[static_text(")")]
|
||||||
RParen, // ')'
|
RParen, // ')'
|
||||||
Word, // '+', '15'
|
Word, // '+', '15'
|
||||||
Whitespace, // whitespaces is explicit
|
Whitespace, // whitespaces is explicit
|
||||||
|
@ -22,53 +33,20 @@ pub enum SyntaxKind {
|
||||||
Atom, // `+`, `15`, wraps a WORD token
|
Atom, // `+`, `15`, wraps a WORD token
|
||||||
Root, // top-level node: a list of s-expressions
|
Root, // top-level node: a list of s-expressions
|
||||||
}
|
}
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
|
/// When matching against the kind of the node, `SyntaxKind::Kind` is a fine name to use.
|
||||||
|
/// For specifying generic arguments like `Node<MySyntax>`, we'll use this alias to refer to the
|
||||||
|
/// syntax as a whole.
|
||||||
|
type SExprSyntax = SyntaxKind;
|
||||||
use SyntaxKind::*;
|
use SyntaxKind::*;
|
||||||
|
|
||||||
/// Some boilerplate is needed, as cstree represents kinds as `struct SyntaxKind(u16)` internally,
|
/// `GreenNode` is an immutable tree, which caches identical nodes and tokens, but doesn't contain
|
||||||
/// in order to not need the user's `enum SyntaxKind` as a type parameter.
|
|
||||||
///
|
|
||||||
/// First, to easily pass the enum variants into cstree via `.into()`:
|
|
||||||
impl From<SyntaxKind> for cstree::RawSyntaxKind {
|
|
||||||
fn from(kind: SyntaxKind) -> Self {
|
|
||||||
Self(kind as u32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Second, implementing the `Language` trait teaches cstree to convert between these two SyntaxKind
|
|
||||||
/// types, allowing for a nicer SyntaxNode API where "kinds" are values from our `enum SyntaxKind`,
|
|
||||||
/// instead of plain u16 values.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub enum Lang {}
|
|
||||||
impl cstree::Language for Lang {
|
|
||||||
type Kind = SyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: cstree::RawSyntaxKind) -> Self::Kind {
|
|
||||||
assert!(raw.0 <= Root as u32);
|
|
||||||
unsafe { std::mem::transmute::<u32, SyntaxKind>(raw.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> cstree::RawSyntaxKind {
|
|
||||||
kind.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
|
||||||
match kind {
|
|
||||||
LParen => Some("("),
|
|
||||||
RParen => Some(")"),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// GreenNode is an immutable tree, which caches identical nodes and tokens, but doesn't contain
|
|
||||||
/// offsets and parent pointers.
|
/// offsets and parent pointers.
|
||||||
/// cstree also deduplicates the actual source string in addition to the tree nodes, so we will need
|
/// `cstree` also deduplicates the actual source string in addition to the tree nodes, so we will need
|
||||||
/// the Resolver to get the real text back from the interned representation.
|
/// the `Resolver` to get the real text back from the interned representation.
|
||||||
use cstree::{green::GreenNode, interning::Resolver, Language};
|
use cstree::{green::GreenNode, interning::Resolver};
|
||||||
|
|
||||||
/// You can construct GreenNodes by hand, but a builder is helpful for top-down parsers: it maintains
|
/// You can construct `GreenNode`s by hand, but a builder is helpful for top-down parsers: it maintains
|
||||||
/// a stack of currently in-progress nodes.
|
/// a stack of currently in-progress nodes.
|
||||||
use cstree::build::GreenNodeBuilder;
|
use cstree::build::GreenNodeBuilder;
|
||||||
|
|
||||||
|
@ -89,13 +67,13 @@ fn parse(text: &str) -> Parse<impl Resolver> {
|
||||||
/// input tokens, including whitespace.
|
/// input tokens, including whitespace.
|
||||||
tokens: VecDeque<(SyntaxKind, &'input str)>,
|
tokens: VecDeque<(SyntaxKind, &'input str)>,
|
||||||
/// the in-progress green tree.
|
/// the in-progress green tree.
|
||||||
builder: GreenNodeBuilder<'static, 'static, Lang>,
|
builder: GreenNodeBuilder<'static, 'static, SExprSyntax>,
|
||||||
/// the list of syntax errors we've accumulated so far.
|
/// the list of syntax errors we've accumulated so far.
|
||||||
errors: Vec<String>,
|
errors: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The outcome of parsing a single S-expression
|
/// The outcome of parsing a single S-expression
|
||||||
enum SexpRes {
|
enum SExprResult {
|
||||||
/// An S-expression (i.e. an atom, or a list) was successfully parsed
|
/// An S-expression (i.e. an atom, or a list) was successfully parsed
|
||||||
Ok,
|
Ok,
|
||||||
/// Nothing was parsed, as no significant tokens remained
|
/// Nothing was parsed, as no significant tokens remained
|
||||||
|
@ -111,14 +89,14 @@ fn parse(text: &str) -> Parse<impl Resolver> {
|
||||||
// Parse zero or more S-expressions
|
// Parse zero or more S-expressions
|
||||||
loop {
|
loop {
|
||||||
match self.sexp() {
|
match self.sexp() {
|
||||||
SexpRes::Eof => break,
|
SExprResult::Eof => break,
|
||||||
SexpRes::RParen => {
|
SExprResult::RParen => {
|
||||||
self.builder.start_node(Error);
|
self.builder.start_node(Error);
|
||||||
self.errors.push("unmatched `)`".to_string());
|
self.errors.push("unmatched `)`".to_string());
|
||||||
self.bump(); // be sure to advance even in case of an error, so as to not get stuck
|
self.bump(); // be sure to advance even in case of an error, so as to not get stuck
|
||||||
self.builder.finish_node();
|
self.builder.finish_node();
|
||||||
}
|
}
|
||||||
SexpRes::Ok => {}
|
SExprResult::Ok => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Don't forget to eat *trailing* whitespace
|
// Don't forget to eat *trailing* whitespace
|
||||||
|
@ -144,28 +122,28 @@ fn parse(text: &str) -> Parse<impl Resolver> {
|
||||||
self.bump(); // '('
|
self.bump(); // '('
|
||||||
loop {
|
loop {
|
||||||
match self.sexp() {
|
match self.sexp() {
|
||||||
SexpRes::Eof => {
|
SExprResult::Eof => {
|
||||||
self.errors.push("expected `)`".to_string());
|
self.errors.push("expected `)`".to_string());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SexpRes::RParen => {
|
SExprResult::RParen => {
|
||||||
self.bump();
|
self.bump();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
SexpRes::Ok => {}
|
SExprResult::Ok => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// close the list node
|
// close the list node
|
||||||
self.builder.finish_node();
|
self.builder.finish_node();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sexp(&mut self) -> SexpRes {
|
fn sexp(&mut self) -> SExprResult {
|
||||||
// Eat leading whitespace
|
// Eat leading whitespace
|
||||||
self.skip_ws();
|
self.skip_ws();
|
||||||
// Either a list, an atom, a closing paren, or an eof.
|
// Either a list, an atom, a closing paren, or an eof.
|
||||||
let t = match self.current() {
|
let t = match self.current() {
|
||||||
None => return SexpRes::Eof,
|
None => return SExprResult::Eof,
|
||||||
Some(RParen) => return SexpRes::RParen,
|
Some(RParen) => return SExprResult::RParen,
|
||||||
Some(t) => t,
|
Some(t) => t,
|
||||||
};
|
};
|
||||||
match t {
|
match t {
|
||||||
|
@ -178,7 +156,7 @@ fn parse(text: &str) -> Parse<impl Resolver> {
|
||||||
Error => self.bump(),
|
Error => self.bump(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
SexpRes::Ok
|
SExprResult::Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance one token, adding it to the current branch of the tree builder.
|
/// Advance one token, adding it to the current branch of the tree builder.
|
||||||
|
@ -208,13 +186,13 @@ fn parse(text: &str) -> Parse<impl Resolver> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// To work with the parse results we need a view into the green tree - the syntax tree.
|
/// To work with the parse results we need a view into the green tree - the syntax tree.
|
||||||
/// It is also immutable, like a GreenNode, but it contains parent pointers, offsets, and has
|
/// It is also immutable, like a `GreenNode`, but it contains parent pointers, offsets, and has
|
||||||
/// identity semantics.
|
/// identity semantics.
|
||||||
type SyntaxNode = cstree::syntax::SyntaxNode<Lang>;
|
type SyntaxNode = cstree::syntax::SyntaxNode<SExprSyntax>;
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
type SyntaxToken = cstree::syntax::SyntaxToken<Lang>;
|
type SyntaxToken = cstree::syntax::SyntaxToken<SExprSyntax>;
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
type SyntaxElement = cstree::syntax::SyntaxElement<Lang>;
|
type SyntaxElement = cstree::syntax::SyntaxElement<SExprSyntax>;
|
||||||
|
|
||||||
impl<I> Parse<I> {
|
impl<I> Parse<I> {
|
||||||
fn syntax(&self) -> SyntaxNode {
|
fn syntax(&self) -> SyntaxNode {
|
||||||
|
@ -292,21 +270,21 @@ mod ast {
|
||||||
ast_node!(List, List);
|
ast_node!(List, List);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sexp is slightly different because it can be both an atom and a list, so let's do it by hand.
|
// `SExpr` is slightly different because it can be both an atom and a list, so let's do it by hand.
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
#[derive(PartialEq, Eq, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct Sexp(SyntaxNode);
|
struct SExpr(SyntaxNode);
|
||||||
|
|
||||||
enum SexpKind {
|
enum SexpKind {
|
||||||
Atom(ast::Atom),
|
Atom(ast::Atom),
|
||||||
List(ast::List),
|
List(ast::List),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sexp {
|
impl SExpr {
|
||||||
fn cast(node: SyntaxNode) -> Option<Self> {
|
fn cast(node: SyntaxNode) -> Option<Self> {
|
||||||
use ast::*;
|
use ast::*;
|
||||||
if Atom::cast(node.clone()).is_some() || List::cast(node.clone()).is_some() {
|
if Atom::cast(node.clone()).is_some() || List::cast(node.clone()).is_some() {
|
||||||
Some(Sexp(node))
|
Some(SExpr(node))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -323,8 +301,8 @@ impl Sexp {
|
||||||
|
|
||||||
// Let's enhance AST nodes with ancillary functions and eval.
|
// Let's enhance AST nodes with ancillary functions and eval.
|
||||||
impl ast::Root {
|
impl ast::Root {
|
||||||
fn sexps(&self) -> impl Iterator<Item = Sexp> + '_ {
|
fn sexps(&self) -> impl Iterator<Item = SExpr> + '_ {
|
||||||
self.0.children().cloned().filter_map(Sexp::cast)
|
self.0.children().cloned().filter_map(SExpr::cast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +333,7 @@ impl ast::Atom {
|
||||||
use cstree::util::NodeOrToken;
|
use cstree::util::NodeOrToken;
|
||||||
|
|
||||||
match self.0.green().children().next() {
|
match self.0.green().children().next() {
|
||||||
Some(NodeOrToken::Token(token)) => Lang::static_text(Lang::kind_from_raw(token.kind()))
|
Some(NodeOrToken::Token(token)) => SExprSyntax::static_text(SExprSyntax::from_raw(token.kind()))
|
||||||
.or_else(|| token.text(resolver))
|
.or_else(|| token.text(resolver))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -364,8 +342,8 @@ impl ast::Atom {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::List {
|
impl ast::List {
|
||||||
fn sexps(&self) -> impl Iterator<Item = Sexp> + '_ {
|
fn sexps(&self) -> impl Iterator<Item = SExpr> + '_ {
|
||||||
self.0.children().cloned().filter_map(Sexp::cast)
|
self.0.children().cloned().filter_map(SExpr::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(&self, resolver: &impl Resolver) -> Option<i64> {
|
fn eval(&self, resolver: &impl Resolver) -> Option<i64> {
|
||||||
|
@ -386,7 +364,7 @@ impl ast::List {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sexp {
|
impl SExpr {
|
||||||
fn eval(&self, resolver: &impl Resolver) -> Option<i64> {
|
fn eval(&self, resolver: &impl Resolver) -> Option<i64> {
|
||||||
match self.kind() {
|
match self.kind() {
|
||||||
SexpKind::Atom(atom) => atom.eval(resolver),
|
SexpKind::Atom(atom) => atom.eval(resolver),
|
||||||
|
@ -418,10 +396,10 @@ nan
|
||||||
assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),])
|
assert_eq!(res, vec![Some(92), Some(92), None, None, Some(92),])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split the input string into a flat list of tokens (such as L_PAREN, WORD, and WHITESPACE)
|
/// Split the input string into a flat list of tokens (such as `LParen`, `Word`, and `Whitespace`)
|
||||||
fn lex(text: &str) -> VecDeque<(SyntaxKind, &str)> {
|
fn lex(text: &str) -> VecDeque<(SyntaxKind, &str)> {
|
||||||
fn tok(t: SyntaxKind) -> m_lexer::TokenKind {
|
fn tok(t: SyntaxKind) -> m_lexer::TokenKind {
|
||||||
m_lexer::TokenKind(cstree::RawSyntaxKind::from(t).0 as u16)
|
m_lexer::TokenKind(t.into_raw().0 as u16)
|
||||||
}
|
}
|
||||||
fn kind(t: m_lexer::TokenKind) -> SyntaxKind {
|
fn kind(t: m_lexer::TokenKind) -> SyntaxKind {
|
||||||
match t.0 {
|
match t.0 {
|
|
@ -34,7 +34,7 @@ fn main() {
|
||||||
|
|
||||||
let interner = db.as_interner();
|
let interner = db.as_interner();
|
||||||
let mut shared_interner = &interner;
|
let mut shared_interner = &interner;
|
||||||
let mut builder: GreenNodeBuilder<TestLang, _> = GreenNodeBuilder::with_interner(&mut shared_interner);
|
let mut builder: GreenNodeBuilder<MySyntax, _> = GreenNodeBuilder::with_interner(&mut shared_interner);
|
||||||
let (tree, _no_interner_because_it_was_borrowed) = {
|
let (tree, _no_interner_because_it_was_borrowed) = {
|
||||||
builder.start_node(TestSyntaxKind::Plus);
|
builder.start_node(TestSyntaxKind::Plus);
|
||||||
builder.token(TestSyntaxKind::Float, "2.05");
|
builder.token(TestSyntaxKind::Float, "2.05");
|
||||||
|
@ -45,6 +45,6 @@ fn main() {
|
||||||
builder.finish_node();
|
builder.finish_node();
|
||||||
builder.finish()
|
builder.finish()
|
||||||
};
|
};
|
||||||
let tree: SyntaxNode<TestLang> = SyntaxNode::new_root(tree);
|
let tree: SyntaxNode<MySyntax> = SyntaxNode::new_root(tree);
|
||||||
assert_eq!(tree.resolve_text(shared_interner), "2.05 + 7.32");
|
assert_eq!(tree.resolve_text(shared_interner), "2.05 + 7.32");
|
||||||
}
|
}
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
interning::{new_interner, Interner, TokenInterner, TokenKey},
|
interning::{new_interner, Interner, TokenInterner, TokenKey},
|
||||||
util::NodeOrToken,
|
util::NodeOrToken,
|
||||||
utility_types::MaybeOwned,
|
utility_types::MaybeOwned,
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{node::GreenNodeHead, token::GreenTokenData};
|
use super::{node::GreenNodeHead, token::GreenTokenData};
|
||||||
|
@ -35,12 +35,12 @@ impl NodeCache<'static> {
|
||||||
/// tokens. To re-use an existing interner, see [`with_interner`](NodeCache::with_interner).
|
/// tokens. To re-use an existing interner, see [`with_interner`](NodeCache::with_interner).
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::{*, Language as _};
|
/// # use cstree::testing::*;
|
||||||
/// use cstree::build::NodeCache;
|
/// use cstree::build::NodeCache;
|
||||||
///
|
///
|
||||||
/// // Build a tree
|
/// // Build a tree
|
||||||
/// let mut cache = NodeCache::new();
|
/// let mut cache = NodeCache::new();
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::with_cache(&mut cache);
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::with_cache(&mut cache);
|
||||||
/// # builder.start_node(Root);
|
/// # builder.start_node(Root);
|
||||||
/// # builder.token(Int, "42");
|
/// # builder.token(Int, "42");
|
||||||
/// # builder.finish_node();
|
/// # builder.finish_node();
|
||||||
|
@ -48,9 +48,9 @@ impl NodeCache<'static> {
|
||||||
/// let (tree, _) = builder.finish();
|
/// let (tree, _) = builder.finish();
|
||||||
///
|
///
|
||||||
/// // Check it out!
|
/// // Check it out!
|
||||||
/// assert_eq!(tree.kind(), MyLanguage::kind_to_raw(Root));
|
/// assert_eq!(tree.kind(), MySyntax::into_raw(Root));
|
||||||
/// let int = tree.children().next().unwrap();
|
/// let int = tree.children().next().unwrap();
|
||||||
/// assert_eq!(int.kind(), MyLanguage::kind_to_raw(Int));
|
/// assert_eq!(int.kind(), MySyntax::into_raw(Int));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -75,14 +75,14 @@ where
|
||||||
/// (strings) across tokens.
|
/// (strings) across tokens.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::{*, Language as _};
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::interning::*;
|
/// # use cstree::interning::*;
|
||||||
/// use cstree::build::NodeCache;
|
/// use cstree::build::NodeCache;
|
||||||
///
|
///
|
||||||
/// // Create the builder from a custom interner
|
/// // Create the builder from a custom interner
|
||||||
/// let mut interner = new_interner();
|
/// let mut interner = new_interner();
|
||||||
/// let mut cache = NodeCache::with_interner(&mut interner);
|
/// let mut cache = NodeCache::with_interner(&mut interner);
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage, TokenInterner> =
|
/// let mut builder: GreenNodeBuilder<MySyntax, TokenInterner> =
|
||||||
/// GreenNodeBuilder::with_cache(&mut cache);
|
/// GreenNodeBuilder::with_cache(&mut cache);
|
||||||
///
|
///
|
||||||
/// // Construct the tree
|
/// // Construct the tree
|
||||||
|
@ -93,9 +93,9 @@ where
|
||||||
/// let (tree, _) = builder.finish();
|
/// let (tree, _) = builder.finish();
|
||||||
///
|
///
|
||||||
/// // Use the tree
|
/// // Use the tree
|
||||||
/// assert_eq!(tree.kind(), MyLanguage::kind_to_raw(Root));
|
/// assert_eq!(tree.kind(), MySyntax::into_raw(Root));
|
||||||
/// let int = tree.children().next().unwrap();
|
/// let int = tree.children().next().unwrap();
|
||||||
/// assert_eq!(int.kind(), MyLanguage::kind_to_raw(Int));
|
/// assert_eq!(int.kind(), MySyntax::into_raw(Int));
|
||||||
/// assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
/// assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -111,14 +111,14 @@ where
|
||||||
/// (strings) across tokens.
|
/// (strings) across tokens.
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::{*, Language as _};
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::interning::*;
|
/// # use cstree::interning::*;
|
||||||
/// use cstree::build::NodeCache;
|
/// use cstree::build::NodeCache;
|
||||||
///
|
///
|
||||||
/// // Create the builder from a custom interner
|
/// // Create the builder from a custom interner
|
||||||
/// let mut interner = new_interner();
|
/// let mut interner = new_interner();
|
||||||
/// let cache = NodeCache::from_interner(interner);
|
/// let cache = NodeCache::from_interner(interner);
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage, TokenInterner> =
|
/// let mut builder: GreenNodeBuilder<MySyntax, TokenInterner> =
|
||||||
/// GreenNodeBuilder::from_cache(cache);
|
/// GreenNodeBuilder::from_cache(cache);
|
||||||
///
|
///
|
||||||
/// // Construct the tree
|
/// // Construct the tree
|
||||||
|
@ -130,9 +130,9 @@ where
|
||||||
///
|
///
|
||||||
/// // Use the tree
|
/// // Use the tree
|
||||||
/// let interner = cache.unwrap().into_interner().unwrap();
|
/// let interner = cache.unwrap().into_interner().unwrap();
|
||||||
/// assert_eq!(tree.kind(), MyLanguage::kind_to_raw(Root));
|
/// assert_eq!(tree.kind(), MySyntax::into_raw(Root));
|
||||||
/// let int = tree.children().next().unwrap();
|
/// let int = tree.children().next().unwrap();
|
||||||
/// assert_eq!(int.kind(), MyLanguage::kind_to_raw(Int));
|
/// assert_eq!(int.kind(), MySyntax::into_raw(Int));
|
||||||
/// assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
/// assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -177,9 +177,9 @@ where
|
||||||
self.interner.into_owned()
|
self.interner.into_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node<L: Language>(&mut self, kind: L::Kind, all_children: &mut Vec<GreenElement>, offset: usize) -> GreenNode {
|
fn node<S: Syntax>(&mut self, kind: S, all_children: &mut Vec<GreenElement>, offset: usize) -> GreenNode {
|
||||||
// NOTE: this fn must remove all children starting at `first_child` from `all_children` before returning
|
// NOTE: this fn must remove all children starting at `first_child` from `all_children` before returning
|
||||||
let kind = L::kind_to_raw(kind);
|
let kind = S::into_raw(kind);
|
||||||
let mut hasher = FxHasher32::default();
|
let mut hasher = FxHasher32::default();
|
||||||
let mut text_len: TextSize = 0.into();
|
let mut text_len: TextSize = 0.into();
|
||||||
for child in &all_children[offset..] {
|
for child in &all_children[offset..] {
|
||||||
|
@ -229,9 +229,9 @@ where
|
||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token<L: Language>(&mut self, kind: L::Kind, text: Option<TokenKey>, len: u32) -> GreenToken {
|
fn token<S: Syntax>(&mut self, kind: S, text: Option<TokenKey>, len: u32) -> GreenToken {
|
||||||
let text_len = TextSize::from(len);
|
let text_len = TextSize::from(len);
|
||||||
let kind = L::kind_to_raw(kind);
|
let kind = S::into_raw(kind);
|
||||||
let data = GreenTokenData { kind, text, text_len };
|
let data = GreenTokenData { kind, text, text_len };
|
||||||
self.tokens
|
self.tokens
|
||||||
.entry(data)
|
.entry(data)
|
||||||
|
@ -253,29 +253,29 @@ pub struct Checkpoint(usize);
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::{*, Language as _};
|
/// # use cstree::testing::*;
|
||||||
/// // Build a tree
|
/// // Build a tree
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// builder.start_node(Root);
|
/// builder.start_node(Root);
|
||||||
/// builder.token(Int, "42");
|
/// builder.token(Int, "42");
|
||||||
/// builder.finish_node();
|
/// builder.finish_node();
|
||||||
/// let (tree, cache) = builder.finish();
|
/// let (tree, cache) = builder.finish();
|
||||||
///
|
///
|
||||||
/// // Check it out!
|
/// // Check it out!
|
||||||
/// assert_eq!(tree.kind(), MyLanguage::kind_to_raw(Root));
|
/// assert_eq!(tree.kind(), MySyntax::into_raw(Root));
|
||||||
/// let int = tree.children().next().unwrap();
|
/// let int = tree.children().next().unwrap();
|
||||||
/// assert_eq!(int.kind(), MyLanguage::kind_to_raw(Int));
|
/// assert_eq!(int.kind(), MySyntax::into_raw(Int));
|
||||||
/// let resolver = cache.unwrap().into_interner().unwrap();
|
/// let resolver = cache.unwrap().into_interner().unwrap();
|
||||||
/// assert_eq!(int.as_token().unwrap().text(&resolver), Some("42"));
|
/// assert_eq!(int.as_token().unwrap().text(&resolver), Some("42"));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GreenNodeBuilder<'cache, 'interner, L: Language, I = TokenInterner> {
|
pub struct GreenNodeBuilder<'cache, 'interner, S: Syntax, I = TokenInterner> {
|
||||||
cache: MaybeOwned<'cache, NodeCache<'interner, I>>,
|
cache: MaybeOwned<'cache, NodeCache<'interner, I>>,
|
||||||
parents: Vec<(L::Kind, usize)>,
|
parents: Vec<(S, usize)>,
|
||||||
children: Vec<GreenElement>,
|
children: Vec<GreenElement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language> GreenNodeBuilder<'static, 'static, L> {
|
impl<S: Syntax> GreenNodeBuilder<'static, 'static, S> {
|
||||||
/// Creates new builder with an empty [`NodeCache`].
|
/// Creates new builder with an empty [`NodeCache`].
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -286,15 +286,15 @@ impl<L: Language> GreenNodeBuilder<'static, 'static, L> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language> Default for GreenNodeBuilder<'static, 'static, L> {
|
impl<S: Syntax> Default for GreenNodeBuilder<'static, 'static, S> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'cache, 'interner, L, I> GreenNodeBuilder<'cache, 'interner, L, I>
|
impl<'cache, 'interner, S, I> GreenNodeBuilder<'cache, 'interner, S, I>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
I: Interner<TokenKey>,
|
I: Interner<TokenKey>,
|
||||||
{
|
{
|
||||||
/// Reusing a [`NodeCache`] between multiple builders saves memory, as it allows to structurally
|
/// Reusing a [`NodeCache`] between multiple builders saves memory, as it allows to structurally
|
||||||
|
@ -312,11 +312,11 @@ where
|
||||||
/// The `cache` given will be returned on [`finish`](GreenNodeBuilder::finish).
|
/// The `cache` given will be returned on [`finish`](GreenNodeBuilder::finish).
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::{*, Language as _};
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::build::*;
|
/// # use cstree::build::*;
|
||||||
/// // Construct a builder from our own cache
|
/// // Construct a builder from our own cache
|
||||||
/// let cache = NodeCache::new();
|
/// let cache = NodeCache::new();
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::from_cache(cache);
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::from_cache(cache);
|
||||||
///
|
///
|
||||||
/// // Build a tree
|
/// // Build a tree
|
||||||
/// # builder.start_node(Root);
|
/// # builder.start_node(Root);
|
||||||
|
@ -327,9 +327,9 @@ where
|
||||||
///
|
///
|
||||||
/// // Use the tree
|
/// // Use the tree
|
||||||
/// let interner = cache.unwrap().into_interner().unwrap();
|
/// let interner = cache.unwrap().into_interner().unwrap();
|
||||||
/// assert_eq!(tree.kind(), MyLanguage::kind_to_raw(Root));
|
/// assert_eq!(tree.kind(), MySyntax::into_raw(Root));
|
||||||
/// let int = tree.children().next().unwrap();
|
/// let int = tree.children().next().unwrap();
|
||||||
/// assert_eq!(int.kind(), MyLanguage::kind_to_raw(Int));
|
/// assert_eq!(int.kind(), MySyntax::into_raw(Int));
|
||||||
/// assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
/// assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn from_cache(cache: NodeCache<'interner, I>) -> Self {
|
pub fn from_cache(cache: NodeCache<'interner, I>) -> Self {
|
||||||
|
@ -377,7 +377,7 @@ where
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::build::*;
|
/// # use cstree::build::*;
|
||||||
/// # use cstree::interning::*;
|
/// # use cstree::interning::*;
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// let interner = builder.interner_mut();
|
/// let interner = builder.interner_mut();
|
||||||
/// let key = interner.get_or_intern("foo");
|
/// let key = interner.get_or_intern("foo");
|
||||||
/// assert_eq!(interner.resolve(key), "foo");
|
/// assert_eq!(interner.resolve(key), "foo");
|
||||||
|
@ -392,19 +392,19 @@ where
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// In debug mode, if `kind` has static text, this function will verify that `text` matches that text.
|
/// In debug mode, if `kind` has static text, this function will verify that `text` matches that text.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn token(&mut self, kind: L::Kind, text: &str) {
|
pub fn token(&mut self, kind: S, text: &str) {
|
||||||
let token = match L::static_text(kind) {
|
let token = match S::static_text(kind) {
|
||||||
Some(static_text) => {
|
Some(static_text) => {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
static_text, text,
|
static_text, text,
|
||||||
r#"Received `{kind:?}` token which should have text "{static_text}", but "{text}" was given."#
|
r#"Received `{kind:?}` token which should have text "{static_text}", but "{text}" was given."#
|
||||||
);
|
);
|
||||||
self.cache.token::<L>(kind, None, static_text.len() as u32)
|
self.cache.token::<S>(kind, None, static_text.len() as u32)
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let len = text.len() as u32;
|
let len = text.len() as u32;
|
||||||
let text = self.cache.intern(text);
|
let text = self.cache.intern(text);
|
||||||
self.cache.token::<L>(kind, Some(text), len)
|
self.cache.token::<S>(kind, Some(text), len)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.children.push(token.into());
|
self.children.push(token.into());
|
||||||
|
@ -420,15 +420,15 @@ where
|
||||||
/// ## Panics
|
/// ## Panics
|
||||||
/// If `kind` does not have static text, i.e., `L::static_text(kind)` returns `None`.
|
/// If `kind` does not have static text, i.e., `L::static_text(kind)` returns `None`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn static_token(&mut self, kind: L::Kind) {
|
pub fn static_token(&mut self, kind: S) {
|
||||||
let static_text = L::static_text(kind).unwrap_or_else(|| panic!("Missing static text for '{kind:?}'"));
|
let static_text = S::static_text(kind).unwrap_or_else(|| panic!("Missing static text for '{kind:?}'"));
|
||||||
let token = self.cache.token::<L>(kind, None, static_text.len() as u32);
|
let token = self.cache.token::<S>(kind, None, static_text.len() as u32);
|
||||||
self.children.push(token.into());
|
self.children.push(token.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start new node of the given `kind` and make it current.
|
/// Start new node of the given `kind` and make it current.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn start_node(&mut self, kind: L::Kind) {
|
pub fn start_node(&mut self, kind: S) {
|
||||||
let len = self.children.len();
|
let len = self.children.len();
|
||||||
self.parents.push((kind, len));
|
self.parents.push((kind, len));
|
||||||
}
|
}
|
||||||
|
@ -438,7 +438,7 @@ where
|
||||||
pub fn finish_node(&mut self) {
|
pub fn finish_node(&mut self) {
|
||||||
let (kind, first_child) = self.parents.pop().unwrap();
|
let (kind, first_child) = self.parents.pop().unwrap();
|
||||||
// NOTE: we rely on the node cache to remove all children starting at `first_child` from `self.children`
|
// NOTE: we rely on the node cache to remove all children starting at `first_child` from `self.children`
|
||||||
let node = self.cache.node::<L>(kind, &mut self.children, first_child);
|
let node = self.cache.node::<S>(kind, &mut self.children, first_child);
|
||||||
self.children.push(node.into());
|
self.children.push(node.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,13 +450,13 @@ where
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::{build::GreenNodeBuilder, Language};
|
/// # use cstree::build::GreenNodeBuilder;
|
||||||
/// # struct Parser;
|
/// # struct Parser;
|
||||||
/// # impl Parser {
|
/// # impl Parser {
|
||||||
/// # fn peek(&self) -> Option<TestSyntaxKind> { None }
|
/// # fn peek(&self) -> Option<TestSyntaxKind> { None }
|
||||||
/// # fn parse_expr(&mut self) {}
|
/// # fn parse_expr(&mut self) {}
|
||||||
/// # }
|
/// # }
|
||||||
/// # let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// # let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// # let mut parser = Parser;
|
/// # let mut parser = Parser;
|
||||||
/// let checkpoint = builder.checkpoint();
|
/// let checkpoint = builder.checkpoint();
|
||||||
/// parser.parse_expr();
|
/// parser.parse_expr();
|
||||||
|
@ -475,7 +475,7 @@ where
|
||||||
/// Wrap the previous branch marked by [`checkpoint`](GreenNodeBuilder::checkpoint) in a new
|
/// Wrap the previous branch marked by [`checkpoint`](GreenNodeBuilder::checkpoint) in a new
|
||||||
/// branch and make it current.
|
/// branch and make it current.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: L::Kind) {
|
pub fn start_node_at(&mut self, checkpoint: Checkpoint, kind: S) {
|
||||||
let Checkpoint(checkpoint) = checkpoint;
|
let Checkpoint(checkpoint) = checkpoint;
|
||||||
assert!(
|
assert!(
|
||||||
checkpoint <= self.children.len(),
|
checkpoint <= self.children.len(),
|
|
@ -5,7 +5,7 @@
|
||||||
//! [`GreenNodeBuilder::token`], which takes the kind of token and a refernce to the text of the token in the source.
|
//! [`GreenNodeBuilder::token`], which takes the kind of token and a refernce to the text of the token in the source.
|
||||||
//!
|
//!
|
||||||
//! Of course, there are tokens whose text will always be the same, such as punctuation (like a semicolon), keywords
|
//! Of course, there are tokens whose text will always be the same, such as punctuation (like a semicolon), keywords
|
||||||
//! (like `fn`), or operators (like `<=`). Use [`Language::static_text`] when implementing `Language` to make `cstree`
|
//! (like `fn`), or operators (like `<=`). Use [`Syntax::static_text`] when implementing `Syntax` to make `cstree`
|
||||||
//! aware of such tokens.
|
//! aware of such tokens.
|
||||||
//!
|
//!
|
||||||
//! There is, however, another category of tokens whose text will appear repeatedly, but for which we cannot know the
|
//! There is, however, another category of tokens whose text will appear repeatedly, but for which we cannot know the
|
||||||
|
@ -59,44 +59,45 @@
|
||||||
//! capabilities with `cstree` as well. Support for this is experimental, and you have to opt in via the
|
//! capabilities with `cstree` as well. Support for this is experimental, and you have to opt in via the
|
||||||
//! `salsa_2022_compat` feature. For instructions on how to do this, and whether you actually want to, please refer to
|
//! `salsa_2022_compat` feature. For instructions on how to do this, and whether you actually want to, please refer to
|
||||||
//! [the `salsa_compat` module documentation].
|
//! [the `salsa_compat` module documentation].
|
||||||
//!
|
#![cfg_attr(
|
||||||
//! ## Multi-threaded interners
|
feature = "multi_threaded_interning",
|
||||||
//! If you want to use your interner on more than one thread, the interner needs to support interning new text through
|
doc = r###"
|
||||||
//! shared access. With the `multi_threaded_interning` feature, you can get such an interner by calling
|
## Multi-threaded interners
|
||||||
//! [`new_threaded_interner`]. The feature also enables support for `ThreadedRodeo`, the multi-threaded interner from
|
|
||||||
//! `lasso`.
|
If you want to use your interner on more than one thread, the interner needs to support interning new text through
|
||||||
//!
|
shared access. With the `multi_threaded_interning` feature, you can get such an interner by calling
|
||||||
//! **You can pass a reference to that interner to anything that expects an [`Interner`]!**
|
[`new_threaded_interner`]. The feature also enables support for `ThreadedRodeo`, the multi-threaded interner from
|
||||||
//! While the interning methods on [`Interner`] require a `&mut self` to also work for single-threaded interners, both
|
`lasso`.
|
||||||
//! [`Resolver`] and [`Interner`] will be implemented for `&interner` if `interner` is multi-threaded:
|
|
||||||
//!
|
**You can pass a reference to that interner to anything that expects an [`Interner`]!**
|
||||||
//! ```
|
While the interning methods on [`Interner`] require a `&mut self` to also work for single-threaded interners, both
|
||||||
//! # use cstree::testing::{*, Language as _};
|
[`Resolver`] and [`Interner`] will be implemented for `&interner` if `interner` is multi-threaded:
|
||||||
//! # use cstree::interning::*;
|
|
||||||
//!
|
```
|
||||||
//! let interner = new_threaded_interner();
|
# use cstree::testing::*;
|
||||||
//! let mut builder: GreenNodeBuilder<MyLanguage, &MultiThreadedTokenInterner> =
|
# use cstree::interning::*;
|
||||||
//! GreenNodeBuilder::from_interner(&interner);
|
let interner = new_threaded_interner();
|
||||||
//!
|
let mut builder: GreenNodeBuilder<MySyntax, &MultiThreadedTokenInterner> =
|
||||||
//! # builder.start_node(Root);
|
GreenNodeBuilder::from_interner(&interner);
|
||||||
//! # builder.token(Int, "42");
|
# builder.start_node(Root);
|
||||||
//! # builder.finish_node();
|
# builder.token(Int, "42");
|
||||||
//! parse(&mut builder, "42");
|
# builder.finish_node();
|
||||||
//! let (tree, cache) = builder.finish();
|
parse(&mut builder, "42");
|
||||||
//!
|
let (tree, cache) = builder.finish();
|
||||||
//! // Note that we get a cache and interner back, because we passed an "owned" reference to `from_interner`
|
// Note that we get a cache and interner back, because we passed an "owned" reference to `from_interner`
|
||||||
//! let used_interner = cache.unwrap().into_interner().unwrap();
|
let used_interner = cache.unwrap().into_interner().unwrap();
|
||||||
//! assert_eq!(used_interner as *const _, &interner as *const _);
|
assert_eq!(used_interner as *const _, &interner as *const _);
|
||||||
//!
|
let int = tree.children().next().unwrap();
|
||||||
//! let int = tree.children().next().unwrap();
|
assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
||||||
//! assert_eq!(int.as_token().unwrap().text(&interner), Some("42"));
|
```
|
||||||
//! ```
|
|
||||||
//!
|
Here, we use `from_interner`, but pass it only a shared reference to "own". Take care to denote the type signature
|
||||||
//! Here, we use `from_interner`, but pass it only a shared reference to "own". Take care to denote the type signature
|
of the `GreenNodeBuilder` appropriately.
|
||||||
//! of the `GreenNodeBuilder` appropriately.
|
"###
|
||||||
|
)]
|
||||||
//!
|
//!
|
||||||
//! [crate documentation]: crate
|
//! [crate documentation]: crate
|
||||||
//! [`Language::static_text`]: crate::Language::static_text
|
//! [`Syntax::static_text`]: crate::Syntax::static_text
|
||||||
//! [`GreenNodeBuilder::token`]: crate::build::GreenNodeBuilder::token
|
//! [`GreenNodeBuilder::token`]: crate::build::GreenNodeBuilder::token
|
||||||
//! [`GreenNodeBuilder::new`]: crate::build::GreenNodeBuilder::new
|
//! [`GreenNodeBuilder::new`]: crate::build::GreenNodeBuilder::new
|
||||||
//! [`finish`]: crate::build::GreenNodeBuilder::finish
|
//! [`finish`]: crate::build::GreenNodeBuilder::finish
|
|
@ -55,7 +55,7 @@
|
||||||
//! let db = Database::default();
|
//! let db = Database::default();
|
||||||
//! let interner = db.as_interner(); // <-- conversion happens here
|
//! let interner = db.as_interner(); // <-- conversion happens here
|
||||||
//! let mut shared_interner = &interner;
|
//! let mut shared_interner = &interner;
|
||||||
//! let mut builder: GreenNodeBuilder<TestLang, _> = GreenNodeBuilder::with_interner(&mut shared_interner);
|
//! let mut builder: GreenNodeBuilder<MySyntax, _> = GreenNodeBuilder::with_interner(&mut shared_interner);
|
||||||
//! let (tree, _no_interner_because_it_was_borrowed) = {
|
//! let (tree, _no_interner_because_it_was_borrowed) = {
|
||||||
//! builder.start_node(TestSyntaxKind::Plus);
|
//! builder.start_node(TestSyntaxKind::Plus);
|
||||||
//! builder.token(TestSyntaxKind::Float, "2.05");
|
//! builder.token(TestSyntaxKind::Float, "2.05");
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
//! builder.finish_node();
|
//! builder.finish_node();
|
||||||
//! builder.finish()
|
//! builder.finish()
|
||||||
//! };
|
//! };
|
||||||
//! let tree: SyntaxNode<TestLang> = SyntaxNode::new_root(tree);
|
//! let tree: SyntaxNode<MySyntax> = SyntaxNode::new_root(tree);
|
||||||
//! assert_eq!(tree.resolve_text(shared_interner), "2.05 + 7.32");
|
//! assert_eq!(tree.resolve_text(shared_interner), "2.05 + 7.32");
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
|
@ -40,7 +40,7 @@
|
||||||
//! to happen to go from input text to a `cstree` syntax tree:
|
//! to happen to go from input text to a `cstree` syntax tree:
|
||||||
//!
|
//!
|
||||||
//! 1. Define an enumeration of the types of tokens (like keywords) and nodes (like "an expression") that you want to
|
//! 1. Define an enumeration of the types of tokens (like keywords) and nodes (like "an expression") that you want to
|
||||||
//! have in your syntax and implement [`Language`]
|
//! have in your syntax and implement [`Syntax`]
|
||||||
//!
|
//!
|
||||||
//! 2. Create a [`GreenNodeBuilder`](build::GreenNodeBuilder) and call
|
//! 2. Create a [`GreenNodeBuilder`](build::GreenNodeBuilder) and call
|
||||||
//! [`start_node`](build::GreenNodeBuilder::start_node), [`token`](build::GreenNodeBuilder::token) and
|
//! [`start_node`](build::GreenNodeBuilder::start_node), [`token`](build::GreenNodeBuilder::token) and
|
||||||
|
@ -61,8 +61,8 @@
|
||||||
//! The `enum` needs to be convertible to a `u32`, so we use the `repr` attribute to ensure it uses the correct
|
//! The `enum` needs to be convertible to a `u32`, so we use the `repr` attribute to ensure it uses the correct
|
||||||
//! representation.
|
//! representation.
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust,no_run
|
||||||
//! #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
//! #[repr(u32)]
|
//! #[repr(u32)]
|
||||||
//! enum SyntaxKind {
|
//! enum SyntaxKind {
|
||||||
//! /* Tokens */
|
//! /* Tokens */
|
||||||
|
@ -77,6 +77,15 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! For convenience when we're working with generic `cstree` types like `SyntaxNode`, we'll also give a name to our
|
||||||
|
//! syntax as a whole and add a type alias for it. That way, we can match against `SyntaxKind`s using the original name,
|
||||||
|
//! but use the more informative `Node<Calculator>` to instantiate `cstree`'s types.
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! # enum SyntaxKind {}
|
||||||
|
//! type Calculator = SyntaxKind;
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! Most of these are tokens to lex the input string into, like numbers (`Int`) and operators (`Plus`, `Minus`).
|
//! Most of these are tokens to lex the input string into, like numbers (`Int`) and operators (`Plus`, `Minus`).
|
||||||
//! We only really need one type of node; expressions.
|
//! We only really need one type of node; expressions.
|
||||||
//! Our syntax tree's root node will have the special kind `Root`, all other nodes will be
|
//! Our syntax tree's root node will have the special kind `Root`, all other nodes will be
|
||||||
|
@ -84,21 +93,22 @@
|
||||||
//! expression nodes.
|
//! expression nodes.
|
||||||
//!
|
//!
|
||||||
//! To use our `SyntaxKind`s with `cstree`, we need to tell it how to convert it back to just a number (the
|
//! To use our `SyntaxKind`s with `cstree`, we need to tell it how to convert it back to just a number (the
|
||||||
//! `#[repr(u16)]` that we added) by implementing the [`Language`] trait. We can also tell `cstree` about tokens that
|
//! `#[repr(u32)]` that we added) by implementing the [`Syntax`] trait. We can also tell `cstree` about tokens that
|
||||||
//! always have the same text through the `static_text` method on the trait. This is useful for the operators and
|
//! always have the same text through the `static_text` method on the trait. This is useful for the operators and
|
||||||
//! parentheses, but not possible for numbers, since an integer token may be produced from the input `3`, but also from
|
//! parentheses, but not possible for numbers, since an integer token may be produced from the input `3`, but also from
|
||||||
//! other numbers like `7` or `12`. We implement `Language` on an empty type, just so we can give it a name.
|
//! other numbers like `7` or `12`.
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust,no_run
|
||||||
//! #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
//! # #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
//! pub struct Calculator;
|
//! # #[repr(u32)]
|
||||||
//! impl Language for Calculator {
|
//! # enum SyntaxKind { Int, Plus, Minus, LParen, RParen, Expr, Root }
|
||||||
//! // The tokens and nodes we just defined
|
//! # type Calculator = SyntaxKind;
|
||||||
//! type Kind = SyntaxKind;
|
//! # use cstree::{Syntax, RawSyntaxKind};
|
||||||
//!
|
//!
|
||||||
//! fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
//! impl Syntax for Calculator {
|
||||||
//! // This just needs to be the inverse of `kind_to_raw`, but could also
|
//! fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
//! // be an `impl TryFrom<u16> for SyntaxKind` or any other conversion.
|
//! // This just needs to be the inverse of `into_raw`, but could also
|
||||||
|
//! // be an `impl TryFrom<u32> for SyntaxKind` or any other conversion.
|
||||||
//! match raw.0 {
|
//! match raw.0 {
|
||||||
//! 0 => SyntaxKind::Int,
|
//! 0 => SyntaxKind::Int,
|
||||||
//! 1 => SyntaxKind::Plus,
|
//! 1 => SyntaxKind::Plus,
|
||||||
|
@ -111,12 +121,12 @@
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
//! fn into_raw(self) -> RawSyntaxKind {
|
||||||
//! RawSyntaxKind(kind as u32)
|
//! RawSyntaxKind(self as u32)
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
//! fn static_text(self) -> Option<&'static str> {
|
||||||
//! match kind {
|
//! match self {
|
||||||
//! SyntaxKind::Plus => Some("+"),
|
//! SyntaxKind::Plus => Some("+"),
|
||||||
//! SyntaxKind::Minus => Some("-"),
|
//! SyntaxKind::Minus => Some("-"),
|
||||||
//! SyntaxKind::LParen => Some("("),
|
//! SyntaxKind::LParen => Some("("),
|
||||||
|
@ -127,12 +137,21 @@
|
||||||
//! }
|
//! }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
//! #### Deriving `Syntax`
|
||||||
|
//!
|
||||||
|
//! To save yourself the hassle of defining this conversion (and, perhaps more importantly,
|
||||||
|
//! continually updating it while your language's syntax is in flux), `cstree` includes a derive
|
||||||
|
//! macro for [`Syntax`](macro@crate::Syntax) when built with the `derive` feature. With the macro,
|
||||||
|
//! the `Syntax` trait implementation above can be replaced by simply adding `#[derive(Syntax)]` to
|
||||||
|
//! `SyntaxKind`.
|
||||||
|
//!
|
||||||
//! ### Parsing into a green tree
|
//! ### Parsing into a green tree
|
||||||
|
//!
|
||||||
//! With that out of the way, we can start writing the parser for our expressions.
|
//! With that out of the way, we can start writing the parser for our expressions.
|
||||||
//! For the purposes of this introduction to `cstree`, I'll assume that there is a lexer that yields the following
|
//! For the purposes of this introduction to `cstree`, I'll assume that there is a lexer that yields the following
|
||||||
//! tokens:
|
//! tokens:
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust,no_run
|
||||||
//! #[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
//! #[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
//! pub enum Token<'input> {
|
//! pub enum Token<'input> {
|
||||||
//! // Note that number strings are not yet parsed into actual numbers,
|
//! // Note that number strings are not yet parsed into actual numbers,
|
||||||
|
@ -150,7 +169,19 @@
|
||||||
//! A simple lexer that yields such tokens is part of the full `readme` example, but we'll be busy enough with the
|
//! A simple lexer that yields such tokens is part of the full `readme` example, but we'll be busy enough with the
|
||||||
//! combination of `cstree` and the actual parser, which we define like this:
|
//! combination of `cstree` and the actual parser, which we define like this:
|
||||||
//!
|
//!
|
||||||
//! ```rust,ignore
|
//! ```rust,no_run
|
||||||
|
//! # use std::iter::Peekable;
|
||||||
|
//! # use cstree::build::GreenNodeBuilder;
|
||||||
|
//! # struct Lexer<'a> { input: &'a str }
|
||||||
|
//! # impl<'a> Lexer<'a> { fn new(input: &'a str) -> Self { Self { input } } }
|
||||||
|
//! # struct Token<'a> { input: &'a str }
|
||||||
|
//! # impl<'a> Iterator for Lexer<'a> {
|
||||||
|
//! # type Item = Token<'a>;
|
||||||
|
//! # fn next(&mut self) -> Option<Self::Item> { None }
|
||||||
|
//! # }
|
||||||
|
//! # #[derive(Debug, Clone, Copy, PartialEq, Eq, cstree::Syntax)]
|
||||||
|
//! # #[repr(u32)] enum Calculator { A }
|
||||||
|
//!
|
||||||
//! pub struct Parser<'input> {
|
//! pub struct Parser<'input> {
|
||||||
//! // `Peekable` is a standard library iterator adapter that allows
|
//! // `Peekable` is a standard library iterator adapter that allows
|
||||||
//! // looking ahead at the next item without removing it from the iterator yet
|
//! // looking ahead at the next item without removing it from the iterator yet
|
||||||
|
@ -391,7 +422,7 @@ pub mod prelude {
|
||||||
build::GreenNodeBuilder,
|
build::GreenNodeBuilder,
|
||||||
green::{GreenNode, GreenToken},
|
green::{GreenNode, GreenToken},
|
||||||
syntax::{SyntaxElement, SyntaxNode, SyntaxToken},
|
syntax::{SyntaxElement, SyntaxNode, SyntaxToken},
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,62 +446,39 @@ pub mod sync {
|
||||||
pub use triomphe::Arc;
|
pub use triomphe::Arc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `Language` trait is the bridge between the internal `cstree` representation and your
|
/// A type that represents what items in your language can be.
|
||||||
|
/// Typically, this is an `enum` with variants such as `Identifier`, `Literal`, ...
|
||||||
|
///
|
||||||
|
/// The `Syntax` trait is the bridge between the internal `cstree` representation and your
|
||||||
/// language's types.
|
/// language's types.
|
||||||
/// This is essential for providing a [`SyntaxNode`] API that can be used with your types, as in the
|
/// This is essential for providing a [`SyntaxNode`] API that can be used with your types, as in the
|
||||||
/// `s_expressions` example:
|
/// `s_expressions` example:
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
/// #[derive(Debug, Clone, Copy, PartialEq, Eq, cstree::Syntax)]
|
||||||
/// # #[allow(non_camel_case_types)]
|
/// # #[allow(non_camel_case_types)]
|
||||||
/// #[repr(u32)]
|
/// #[repr(u32)]
|
||||||
/// enum SyntaxKind {
|
/// enum SyntaxKind {
|
||||||
|
/// #[static_text("+")]
|
||||||
/// Plus, // `+`
|
/// Plus, // `+`
|
||||||
|
/// #[static_text("-")]
|
||||||
/// Minus, // `-`
|
/// Minus, // `-`
|
||||||
/// Integer, // like `15`
|
/// Integer, // like `15`
|
||||||
/// Expression, // combined expression, like `5 + 4 - 3`
|
/// Expression, // combined expression, like `5 + 4 - 3`
|
||||||
/// Whitespace, // whitespaces is explicit
|
/// Whitespace, // whitespace is explicit
|
||||||
/// #[doc(hidden)]
|
|
||||||
/// __LAST,
|
|
||||||
/// }
|
|
||||||
/// use SyntaxKind::*;
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
/// enum Lang {}
|
|
||||||
///
|
|
||||||
/// impl cstree::Language for Lang {
|
|
||||||
/// type Kind = SyntaxKind;
|
|
||||||
///
|
|
||||||
/// fn kind_from_raw(raw: cstree::RawSyntaxKind) -> Self::Kind {
|
|
||||||
/// assert!(raw.0 <= __LAST as u32);
|
|
||||||
/// unsafe { std::mem::transmute::<u32, SyntaxKind>(raw.0) }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn kind_to_raw(kind: Self::Kind) -> cstree::RawSyntaxKind {
|
|
||||||
/// cstree::RawSyntaxKind(kind as u32)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
|
||||||
/// match kind {
|
|
||||||
/// Plus => Some("+"),
|
|
||||||
/// Minus => Some("-"),
|
|
||||||
/// _ => None,
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// `cstree` provides a procedural macro called `cstree_derive` to automatically generate `Syntax` implementations for
|
||||||
|
/// syntax kind enums if its `derive` feature is enabled.
|
||||||
|
///
|
||||||
/// [`SyntaxNode`]: crate::syntax::SyntaxNode
|
/// [`SyntaxNode`]: crate::syntax::SyntaxNode
|
||||||
pub trait Language: Sized + Clone + Copy + fmt::Debug + Eq + Ord + std::hash::Hash {
|
pub trait Syntax: Sized + Copy + fmt::Debug + Eq {
|
||||||
/// A type that represents what items in your Language can be.
|
|
||||||
/// Typically, this is an `enum` with variants such as `Identifier`, `Literal`, ...
|
|
||||||
type Kind: Sized + Clone + Copy + fmt::Debug;
|
|
||||||
|
|
||||||
/// Construct a semantic item kind from the compact representation.
|
/// Construct a semantic item kind from the compact representation.
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind;
|
fn from_raw(raw: RawSyntaxKind) -> Self;
|
||||||
|
|
||||||
/// Convert a semantic item kind into a more compact representation.
|
/// Convert a semantic item kind into a more compact representation.
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind;
|
fn into_raw(self) -> RawSyntaxKind;
|
||||||
|
|
||||||
/// Fixed text for a particular syntax kind.
|
/// Fixed text for a particular syntax kind.
|
||||||
/// Implement for kinds that will only ever represent the same text, such as punctuation (like a
|
/// Implement for kinds that will only ever represent the same text, such as punctuation (like a
|
||||||
|
@ -479,16 +487,25 @@ pub trait Language: Sized + Clone + Copy + fmt::Debug + Eq + Ord + std::hash::Ha
|
||||||
/// Indicating tokens that have a `static_text` this way allows `cstree` to store them more efficiently, which makes
|
/// Indicating tokens that have a `static_text` this way allows `cstree` to store them more efficiently, which makes
|
||||||
/// it faster to add them to a syntax tree and to look up their text. Since there can often be many occurrences
|
/// it faster to add them to a syntax tree and to look up their text. Since there can often be many occurrences
|
||||||
/// of these tokens inside a file, doing so will improve the performance of using `cstree`.
|
/// of these tokens inside a file, doing so will improve the performance of using `cstree`.
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str>;
|
fn static_text(self) -> Option<&'static str>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "derive")]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cstree_derive;
|
||||||
|
|
||||||
|
#[cfg(feature = "derive")]
|
||||||
|
/// Derive macro available if `cstree` is built with `features = ["derive"]`.
|
||||||
|
pub use cstree_derive::Syntax;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(unsafe_code, unused)]
|
#[allow(unsafe_code, unused)]
|
||||||
pub mod testing {
|
pub mod testing {
|
||||||
pub use crate::prelude::*;
|
pub use crate::prelude::*;
|
||||||
pub fn parse<L: Language, I>(_b: &mut GreenNodeBuilder<L, I>, _s: &str) {}
|
pub fn parse<S: Syntax, I>(_b: &mut GreenNodeBuilder<S, I>, _s: &str) {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub enum TestSyntaxKind {
|
pub enum TestSyntaxKind {
|
||||||
|
@ -501,25 +518,21 @@ pub mod testing {
|
||||||
Whitespace,
|
Whitespace,
|
||||||
__LAST,
|
__LAST,
|
||||||
}
|
}
|
||||||
|
pub type MySyntax = TestSyntaxKind;
|
||||||
pub use TestSyntaxKind::*;
|
pub use TestSyntaxKind::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
impl Syntax for TestSyntaxKind {
|
||||||
pub enum TestLang {}
|
fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
pub type MyLanguage = TestLang;
|
|
||||||
impl Language for TestLang {
|
|
||||||
type Kind = TestSyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
|
||||||
assert!(raw.0 <= TestSyntaxKind::__LAST as u32);
|
assert!(raw.0 <= TestSyntaxKind::__LAST as u32);
|
||||||
unsafe { std::mem::transmute::<u32, TestSyntaxKind>(raw.0) }
|
unsafe { std::mem::transmute::<u32, Self>(raw.0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
fn into_raw(self) -> RawSyntaxKind {
|
||||||
RawSyntaxKind(kind as u32)
|
RawSyntaxKind(self as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
fn static_text(self) -> Option<&'static str> {
|
||||||
match kind {
|
match self {
|
||||||
TestSyntaxKind::Plus => Some("+"),
|
TestSyntaxKind::Plus => Some("+"),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
syntax::{ResolvedNode, SyntaxNode},
|
syntax::{ResolvedNode, SyntaxNode},
|
||||||
traversal::WalkEvent,
|
traversal::WalkEvent,
|
||||||
util::NodeOrToken,
|
util::NodeOrToken,
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
use serde::{
|
use serde::{
|
||||||
de::{Error, SeqAccess, Visitor},
|
de::{Error, SeqAccess, Visitor},
|
||||||
|
@ -40,7 +40,7 @@ macro_rules! data_list {
|
||||||
/// contains a boolean which indicates if this node has a data. If it has one,
|
/// contains a boolean which indicates if this node has a data. If it has one,
|
||||||
/// the deserializer should pop the first element from the data list and continue.
|
/// the deserializer should pop the first element from the data list and continue.
|
||||||
///
|
///
|
||||||
/// Takes the `Language` (`$l`), `SyntaxNode` (`$node`), `Resolver` (`$resolver`),
|
/// Takes the `Syntax` (`$l`), `SyntaxNode` (`$node`), `Resolver` (`$resolver`),
|
||||||
/// `Serializer` (`$serializer`), and an optional `data_list` which must be a `mut Vec<D>`.
|
/// `Serializer` (`$serializer`), and an optional `data_list` which must be a `mut Vec<D>`.
|
||||||
macro_rules! gen_serialize {
|
macro_rules! gen_serialize {
|
||||||
($l:ident, $node:expr, $resolver:expr, $ser:ident, $($data_list:ident)?) => {{
|
($l:ident, $node:expr, $resolver:expr, $ser:ident, $($data_list:ident)?) => {{
|
||||||
|
@ -56,9 +56,9 @@ macro_rules! gen_serialize {
|
||||||
})
|
})
|
||||||
.unwrap_or(false);)?
|
.unwrap_or(false);)?
|
||||||
|
|
||||||
Some(Event::EnterNode($l::kind_to_raw(node.kind()), has_data))
|
Some(Event::EnterNode($l::into_raw(node.kind()), has_data))
|
||||||
}
|
}
|
||||||
WalkEvent::Enter(NodeOrToken::Token(tok)) => Some(Event::Token($l::kind_to_raw(tok.kind()), tok.resolve_text($resolver))),
|
WalkEvent::Enter(NodeOrToken::Token(tok)) => Some(Event::Token($l::into_raw(tok.kind()), tok.resolve_text($resolver))),
|
||||||
|
|
||||||
WalkEvent::Leave(NodeOrToken::Node(_)) => Some(Event::LeaveNode),
|
WalkEvent::Leave(NodeOrToken::Node(_)) => Some(Event::LeaveNode),
|
||||||
WalkEvent::Leave(NodeOrToken::Token(_)) => None,
|
WalkEvent::Leave(NodeOrToken::Token(_)) => None,
|
||||||
|
@ -87,53 +87,53 @@ enum Event<'text> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a `SyntaxNode` serializable but without serializing the data.
|
/// Make a `SyntaxNode` serializable but without serializing the data.
|
||||||
pub(crate) struct SerializeWithResolver<'node, 'resolver, L: Language, D: 'static, R: ?Sized> {
|
pub(crate) struct SerializeWithResolver<'node, 'resolver, S: Syntax, D: 'static, R: ?Sized> {
|
||||||
pub(crate) node: &'node SyntaxNode<L, D>,
|
pub(crate) node: &'node SyntaxNode<S, D>,
|
||||||
pub(crate) resolver: &'resolver R,
|
pub(crate) resolver: &'resolver R,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make a `SyntaxNode` serializable which will include the data for serialization.
|
/// Make a `SyntaxNode` serializable which will include the data for serialization.
|
||||||
pub(crate) struct SerializeWithData<'node, 'resolver, L: Language, D: 'static, R: ?Sized> {
|
pub(crate) struct SerializeWithData<'node, 'resolver, S: Syntax, D: 'static, R: ?Sized> {
|
||||||
pub(crate) node: &'node SyntaxNode<L, D>,
|
pub(crate) node: &'node SyntaxNode<S, D>,
|
||||||
pub(crate) resolver: &'resolver R,
|
pub(crate) resolver: &'resolver R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, D, R> Serialize for SerializeWithData<'_, '_, L, D, R>
|
impl<S, D, R> Serialize for SerializeWithData<'_, '_, S, D, R>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
R: Resolver<TokenKey> + ?Sized,
|
R: Resolver<TokenKey> + ?Sized,
|
||||||
D: Serialize,
|
D: Serialize,
|
||||||
{
|
{
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
Ser: serde::Serializer,
|
||||||
{
|
{
|
||||||
let mut data_list = Vec::new();
|
let mut data_list = Vec::new();
|
||||||
gen_serialize!(L, self.node, self.resolver, serializer, data_list)
|
gen_serialize!(S, self.node, self.resolver, serializer, data_list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, D, R> Serialize for SerializeWithResolver<'_, '_, L, D, R>
|
impl<S, D, R> Serialize for SerializeWithResolver<'_, '_, S, D, R>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
R: Resolver<TokenKey> + ?Sized,
|
R: Resolver<TokenKey> + ?Sized,
|
||||||
{
|
{
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
Ser: serde::Serializer,
|
||||||
{
|
{
|
||||||
gen_serialize!(L, self.node, self.resolver, serializer,)
|
gen_serialize!(S, self.node, self.resolver, serializer,)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L, D> Serialize for ResolvedNode<L, D>
|
impl<S, D> Serialize for ResolvedNode<S, D>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
D: Serialize,
|
D: Serialize,
|
||||||
{
|
{
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
Ser: serde::Serializer,
|
||||||
{
|
{
|
||||||
let node = SerializeWithResolver {
|
let node = SerializeWithResolver {
|
||||||
node: self,
|
node: self,
|
||||||
|
@ -143,9 +143,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, L, D> Deserialize<'de> for ResolvedNode<L, D>
|
impl<'de, S, D> Deserialize<'de> for ResolvedNode<S, D>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
D: Deserialize<'de>,
|
D: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
// Deserialization is done by walking down the deserialized event stream,
|
// Deserialization is done by walking down the deserialized event stream,
|
||||||
|
@ -158,20 +158,20 @@ where
|
||||||
// we walk down the nodes, check if the bool at `data_list[idx]` is true,
|
// we walk down the nodes, check if the bool at `data_list[idx]` is true,
|
||||||
// and if so, pop the first element of the data list and attach the data
|
// and if so, pop the first element of the data list and attach the data
|
||||||
// to the current node.
|
// to the current node.
|
||||||
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
|
fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>
|
||||||
where
|
where
|
||||||
DE: serde::Deserializer<'de>,
|
De: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
struct EventVisitor<L: Language, D: 'static> {
|
struct EventVisitor<S: Syntax, D: 'static> {
|
||||||
_marker: PhantomData<fn() -> ResolvedNode<L, D>>,
|
_marker: PhantomData<fn() -> ResolvedNode<S, D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de, L, D> Visitor<'de> for EventVisitor<L, D>
|
impl<'de, S, D> Visitor<'de> for EventVisitor<S, D>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
D: Deserialize<'de>,
|
D: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
type Value = (ResolvedNode<L, D>, VecDeque<bool>);
|
type Value = (ResolvedNode<S, D>, VecDeque<bool>);
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
formatter.write_str("a list of tree events")
|
formatter.write_str("a list of tree events")
|
||||||
|
@ -181,16 +181,16 @@ where
|
||||||
where
|
where
|
||||||
A: SeqAccess<'de>,
|
A: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut builder: GreenNodeBuilder<L> = GreenNodeBuilder::new();
|
let mut builder: GreenNodeBuilder<S> = GreenNodeBuilder::new();
|
||||||
let mut data_indices = VecDeque::new();
|
let mut data_indices = VecDeque::new();
|
||||||
|
|
||||||
while let Some(next) = seq.next_element::<Event<'_>>()? {
|
while let Some(next) = seq.next_element::<Event<'_>>()? {
|
||||||
match next {
|
match next {
|
||||||
Event::EnterNode(kind, has_data) => {
|
Event::EnterNode(kind, has_data) => {
|
||||||
builder.start_node(L::kind_from_raw(kind));
|
builder.start_node(S::from_raw(kind));
|
||||||
data_indices.push_back(has_data);
|
data_indices.push_back(has_data);
|
||||||
}
|
}
|
||||||
Event::Token(kind, text) => builder.token(L::kind_from_raw(kind), text),
|
Event::Token(kind, text) => builder.token(S::from_raw(kind), text),
|
||||||
Event::LeaveNode => builder.finish_node(),
|
Event::LeaveNode => builder.finish_node(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,10 +201,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProcessedEvents<L: Language, D: 'static>(ResolvedNode<L, D>, VecDeque<bool>);
|
struct ProcessedEvents<S: Syntax, D: 'static>(ResolvedNode<S, D>, VecDeque<bool>);
|
||||||
impl<'de, L, D> Deserialize<'de> for ProcessedEvents<L, D>
|
impl<'de, S, D> Deserialize<'de> for ProcessedEvents<S, D>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
D: Deserialize<'de>,
|
D: Deserialize<'de>,
|
||||||
{
|
{
|
||||||
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
|
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
|
||||||
|
@ -217,20 +217,20 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let (ProcessedEvents(tree, data_indices), mut data) =
|
let (ProcessedEvents(tree, data_indices), mut data) =
|
||||||
<(ProcessedEvents<L, D>, VecDeque<D>)>::deserialize(deserializer)?;
|
<(ProcessedEvents<S, D>, VecDeque<D>)>::deserialize(deserializer)?;
|
||||||
|
|
||||||
tree.descendants().zip(data_indices).try_for_each(|(node, has_data)| {
|
tree.descendants().zip(data_indices).try_for_each(|(node, has_data)| {
|
||||||
if has_data {
|
if has_data {
|
||||||
let data = data
|
let data = data
|
||||||
.pop_front()
|
.pop_front()
|
||||||
.ok_or_else(|| DE::Error::custom("invalid serialized tree"))?;
|
.ok_or_else(|| De::Error::custom("invalid serialized tree"))?;
|
||||||
node.set_data(data);
|
node.set_data(data);
|
||||||
}
|
}
|
||||||
<Result<(), DE::Error>>::Ok(())
|
<Result<(), De::Error>>::Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if !data.is_empty() {
|
if !data.is_empty() {
|
||||||
Err(DE::Error::custom(
|
Err(De::Error::custom(
|
||||||
"serialized SyntaxNode contained too many data elements",
|
"serialized SyntaxNode contained too many data elements",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,18 +240,18 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for RawSyntaxKind {
|
impl Serialize for RawSyntaxKind {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
Ser: serde::Serializer,
|
||||||
{
|
{
|
||||||
serializer.serialize_u32(self.0)
|
serializer.serialize_u32(self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for RawSyntaxKind {
|
impl<'de> Deserialize<'de> for RawSyntaxKind {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<De>(deserializer: De) -> Result<Self, De::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
De: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
Ok(Self(u32::deserialize(deserializer)?))
|
Ok(Self(u32::deserialize(deserializer)?))
|
||||||
}
|
}
|
|
@ -7,25 +7,25 @@ use crate::{
|
||||||
green::GreenElementRef,
|
green::GreenElementRef,
|
||||||
interning::{Resolver, TokenKey},
|
interning::{Resolver, TokenKey},
|
||||||
util::{NodeOrToken, TokenAtOffset},
|
util::{NodeOrToken, TokenAtOffset},
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An element of the tree, can be either a node or a token.
|
/// An element of the tree, can be either a node or a token.
|
||||||
pub type SyntaxElement<L, D = ()> = NodeOrToken<SyntaxNode<L, D>, SyntaxToken<L, D>>;
|
pub type SyntaxElement<S, D = ()> = NodeOrToken<SyntaxNode<S, D>, SyntaxToken<S, D>>;
|
||||||
|
|
||||||
impl<L: Language, D> From<SyntaxNode<L, D>> for SyntaxElement<L, D> {
|
impl<S: Syntax, D> From<SyntaxNode<S, D>> for SyntaxElement<S, D> {
|
||||||
fn from(node: SyntaxNode<L, D>) -> SyntaxElement<L, D> {
|
fn from(node: SyntaxNode<S, D>) -> SyntaxElement<S, D> {
|
||||||
NodeOrToken::Node(node)
|
NodeOrToken::Node(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> From<SyntaxToken<L, D>> for SyntaxElement<L, D> {
|
impl<S: Syntax, D> From<SyntaxToken<S, D>> for SyntaxElement<S, D> {
|
||||||
fn from(token: SyntaxToken<L, D>) -> SyntaxElement<L, D> {
|
fn from(token: SyntaxToken<S, D>) -> SyntaxElement<S, D> {
|
||||||
NodeOrToken::Token(token)
|
NodeOrToken::Token(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxElement<L, D> {
|
impl<S: Syntax, D> SyntaxElement<S, D> {
|
||||||
/// Returns this element's [`Display`](fmt::Display) representation as a 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).
|
/// To avoid allocating for every element, see [`write_display`](type.SyntaxElement.html#method.write_display).
|
||||||
|
@ -80,22 +80,22 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A reference to an element of the tree, can be either a reference to a node or one to a token.
|
/// A reference to an element of the tree, can be either a reference to a node or one to a token.
|
||||||
pub type SyntaxElementRef<'a, L, D = ()> = NodeOrToken<&'a SyntaxNode<L, D>, &'a SyntaxToken<L, D>>;
|
pub type SyntaxElementRef<'a, S, D = ()> = NodeOrToken<&'a SyntaxNode<S, D>, &'a SyntaxToken<S, D>>;
|
||||||
|
|
||||||
impl<'a, L: Language, D> From<&'a SyntaxNode<L, D>> for SyntaxElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> From<&'a SyntaxNode<S, D>> for SyntaxElementRef<'a, S, D> {
|
||||||
fn from(node: &'a SyntaxNode<L, D>) -> Self {
|
fn from(node: &'a SyntaxNode<S, D>) -> Self {
|
||||||
NodeOrToken::Node(node)
|
NodeOrToken::Node(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> From<&'a SyntaxToken<L, D>> for SyntaxElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> From<&'a SyntaxToken<S, D>> for SyntaxElementRef<'a, S, D> {
|
||||||
fn from(token: &'a SyntaxToken<L, D>) -> Self {
|
fn from(token: &'a SyntaxToken<S, D>) -> Self {
|
||||||
NodeOrToken::Token(token)
|
NodeOrToken::Token(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> From<&'a SyntaxElement<L, D>> for SyntaxElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> From<&'a SyntaxElement<S, D>> for SyntaxElementRef<'a, S, D> {
|
||||||
fn from(element: &'a SyntaxElement<L, D>) -> Self {
|
fn from(element: &'a SyntaxElement<S, D>) -> Self {
|
||||||
match element {
|
match element {
|
||||||
NodeOrToken::Node(it) => Self::Node(it),
|
NodeOrToken::Node(it) => Self::Node(it),
|
||||||
NodeOrToken::Token(it) => Self::Token(it),
|
NodeOrToken::Token(it) => Self::Token(it),
|
||||||
|
@ -103,7 +103,7 @@ impl<'a, L: Language, D> From<&'a SyntaxElement<L, D>> for SyntaxElementRef<'a,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> SyntaxElementRef<'a, S, D> {
|
||||||
/// Returns this element's [`Display`](fmt::Display) representation as a 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).
|
/// To avoid allocating for every element, see [`write_display`](type.SyntaxElementRef.html#method.write_display).
|
||||||
|
@ -157,14 +157,14 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxElement<L, D> {
|
impl<S: Syntax, D> SyntaxElement<S, D> {
|
||||||
pub(super) fn new(
|
pub(super) fn new(
|
||||||
element: GreenElementRef<'_>,
|
element: GreenElementRef<'_>,
|
||||||
parent: &SyntaxNode<L, D>,
|
parent: &SyntaxNode<S, D>,
|
||||||
index: u32,
|
index: u32,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
ref_count: *mut AtomicU32,
|
ref_count: *mut AtomicU32,
|
||||||
) -> SyntaxElement<L, D> {
|
) -> SyntaxElement<S, D> {
|
||||||
match element {
|
match element {
|
||||||
NodeOrToken::Node(node) => SyntaxNode::new_child(node, parent, index, offset, ref_count).into(),
|
NodeOrToken::Node(node) => SyntaxNode::new_child(node, parent, index, offset, ref_count).into(),
|
||||||
NodeOrToken::Token(_) => SyntaxToken::new(parent, index, offset).into(),
|
NodeOrToken::Token(_) => SyntaxToken::new(parent, index, offset).into(),
|
||||||
|
@ -191,7 +191,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// The kind of this element in terms of your language.
|
/// The kind of this element in terms of your language.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> L::Kind {
|
pub fn kind(&self) -> S {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.kind(),
|
NodeOrToken::Node(it) => it.kind(),
|
||||||
NodeOrToken::Token(it) => it.kind(),
|
NodeOrToken::Token(it) => it.kind(),
|
||||||
|
@ -200,7 +200,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// The parent node of this element, except if this element is the root.
|
/// The parent node of this element, except if this element is the root.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> Option<&SyntaxNode<L, D>> {
|
pub fn parent(&self) -> Option<&SyntaxNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.parent(),
|
NodeOrToken::Node(it) => it.parent(),
|
||||||
NodeOrToken::Token(it) => Some(it.parent()),
|
NodeOrToken::Token(it) => Some(it.parent()),
|
||||||
|
@ -209,7 +209,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this node.
|
/// Returns an iterator along the chain of parents of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.ancestors(),
|
NodeOrToken::Node(it) => it.ancestors(),
|
||||||
NodeOrToken::Token(it) => it.parent().ancestors(),
|
NodeOrToken::Token(it) => it.parent().ancestors(),
|
||||||
|
@ -218,7 +218,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// Return the leftmost token in the subtree of this element.
|
/// Return the leftmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_token(&self) -> Option<&SyntaxToken<L, D>> {
|
pub fn first_token(&self) -> Option<&SyntaxToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.first_token(),
|
NodeOrToken::Node(it) => it.first_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -227,7 +227,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// Return the rightmost token in the subtree of this element.
|
/// Return the rightmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_token(&self) -> Option<&SyntaxToken<L, D>> {
|
pub fn last_token(&self) -> Option<&SyntaxToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.last_token(),
|
NodeOrToken::Node(it) => it.last_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -236,7 +236,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
||||||
|
@ -245,7 +245,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
||||||
|
@ -253,7 +253,7 @@ impl<L: Language, D> SyntaxElement<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> SyntaxElementRef<'a, S, D> {
|
||||||
/// The range this element covers in the source text, in bytes.
|
/// The range this element covers in the source text, in bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text_range(&self) -> TextRange {
|
pub fn text_range(&self) -> TextRange {
|
||||||
|
@ -274,7 +274,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The kind of this element in terms of your language.
|
/// The kind of this element in terms of your language.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> L::Kind {
|
pub fn kind(&self) -> S {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.kind(),
|
NodeOrToken::Node(it) => it.kind(),
|
||||||
NodeOrToken::Token(it) => it.kind(),
|
NodeOrToken::Token(it) => it.kind(),
|
||||||
|
@ -283,7 +283,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The parent node of this element, except if this element is the root.
|
/// The parent node of this element, except if this element is the root.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> Option<&'a SyntaxNode<L, D>> {
|
pub fn parent(&self) -> Option<&'a SyntaxNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.parent(),
|
NodeOrToken::Node(it) => it.parent(),
|
||||||
NodeOrToken::Token(it) => Some(it.parent()),
|
NodeOrToken::Token(it) => Some(it.parent()),
|
||||||
|
@ -292,7 +292,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this node.
|
/// Returns an iterator along the chain of parents of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &'a SyntaxNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &'a SyntaxNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.ancestors(),
|
NodeOrToken::Node(it) => it.ancestors(),
|
||||||
NodeOrToken::Token(it) => it.parent().ancestors(),
|
NodeOrToken::Token(it) => it.parent().ancestors(),
|
||||||
|
@ -301,7 +301,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// Return the leftmost token in the subtree of this element.
|
/// Return the leftmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_token(&self) -> Option<&'a SyntaxToken<L, D>> {
|
pub fn first_token(&self) -> Option<&'a SyntaxToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.first_token(),
|
NodeOrToken::Node(it) => it.first_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -310,7 +310,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// Return the rightmost token in the subtree of this element.
|
/// Return the rightmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_token(&self) -> Option<&'a SyntaxToken<L, D>> {
|
pub fn last_token(&self) -> Option<&'a SyntaxToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.last_token(),
|
NodeOrToken::Node(it) => it.last_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -319,7 +319,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'a, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'a, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
||||||
|
@ -328,7 +328,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'a, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'a, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
||||||
|
@ -336,7 +336,7 @@ impl<'a, L: Language, D> SyntaxElementRef<'a, L, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken<L, D>> {
|
pub(super) fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken<S, D>> {
|
||||||
assert!(self.text_range().start() <= offset && offset <= self.text_range().end());
|
assert!(self.text_range().start() <= offset && offset <= self.text_range().end());
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Token(token) => TokenAtOffset::Single((*token).clone()),
|
NodeOrToken::Token(token) => TokenAtOffset::Single((*token).clone()),
|
|
@ -7,7 +7,7 @@ use text_size::TextSize;
|
||||||
use crate::{
|
use crate::{
|
||||||
green::{GreenElementRef, GreenNodeChildren},
|
green::{GreenElementRef, GreenNodeChildren},
|
||||||
syntax::{SyntaxElementRef, SyntaxNode},
|
syntax::{SyntaxElementRef, SyntaxNode},
|
||||||
Language,
|
Syntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -18,7 +18,7 @@ struct Iter<'n> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n> Iter<'n> {
|
impl<'n> Iter<'n> {
|
||||||
fn new<L: Language, D>(parent: &'n SyntaxNode<L, D>) -> Self {
|
fn new<S: Syntax, D>(parent: &'n SyntaxNode<S, D>) -> Self {
|
||||||
let offset = parent.text_range().start();
|
let offset = parent.text_range().start();
|
||||||
let green: GreenNodeChildren<'_> = parent.green().children();
|
let green: GreenNodeChildren<'_> = parent.green().children();
|
||||||
Iter {
|
Iter {
|
||||||
|
@ -67,14 +67,14 @@ impl<'n> FusedIterator for Iter<'n> {}
|
||||||
|
|
||||||
/// An iterator over the child nodes of a [`SyntaxNode`].
|
/// An iterator over the child nodes of a [`SyntaxNode`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SyntaxNodeChildren<'n, L: Language, D: 'static = ()> {
|
pub struct SyntaxNodeChildren<'n, S: Syntax, D: 'static = ()> {
|
||||||
inner: Iter<'n>,
|
inner: Iter<'n>,
|
||||||
parent: &'n SyntaxNode<L, D>,
|
parent: &'n SyntaxNode<S, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, L: Language, D> SyntaxNodeChildren<'n, L, D> {
|
impl<'n, S: Syntax, D> SyntaxNodeChildren<'n, S, D> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn new(parent: &'n SyntaxNode<L, D>) -> Self {
|
pub(super) fn new(parent: &'n SyntaxNode<S, D>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Iter::new(parent),
|
inner: Iter::new(parent),
|
||||||
parent,
|
parent,
|
||||||
|
@ -82,8 +82,8 @@ impl<'n, L: Language, D> SyntaxNodeChildren<'n, L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, L: Language, D> Iterator for SyntaxNodeChildren<'n, L, D> {
|
impl<'n, S: Syntax, D> Iterator for SyntaxNodeChildren<'n, S, D> {
|
||||||
type Item = &'n SyntaxNode<L, D>;
|
type Item = &'n SyntaxNode<S, D>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
@ -109,24 +109,24 @@ impl<'n, L: Language, D> Iterator for SyntaxNodeChildren<'n, L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, L: Language, D> ExactSizeIterator for SyntaxNodeChildren<'n, L, D> {
|
impl<'n, S: Syntax, D> ExactSizeIterator for SyntaxNodeChildren<'n, S, D> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'n, L: Language, D> FusedIterator for SyntaxNodeChildren<'n, L, D> {}
|
impl<'n, S: Syntax, D> FusedIterator for SyntaxNodeChildren<'n, S, D> {}
|
||||||
|
|
||||||
/// An iterator over the children of a [`SyntaxNode`].
|
/// An iterator over the children of a [`SyntaxNode`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SyntaxElementChildren<'n, L: Language, D: 'static = ()> {
|
pub struct SyntaxElementChildren<'n, S: Syntax, D: 'static = ()> {
|
||||||
inner: Iter<'n>,
|
inner: Iter<'n>,
|
||||||
parent: &'n SyntaxNode<L, D>,
|
parent: &'n SyntaxNode<S, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, L: Language, D> SyntaxElementChildren<'n, L, D> {
|
impl<'n, S: Syntax, D> SyntaxElementChildren<'n, S, D> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn new(parent: &'n SyntaxNode<L, D>) -> Self {
|
pub(super) fn new(parent: &'n SyntaxNode<S, D>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Iter::new(parent),
|
inner: Iter::new(parent),
|
||||||
parent,
|
parent,
|
||||||
|
@ -134,8 +134,8 @@ impl<'n, L: Language, D> SyntaxElementChildren<'n, L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, L: Language, D> Iterator for SyntaxElementChildren<'n, L, D> {
|
impl<'n, S: Syntax, D> Iterator for SyntaxElementChildren<'n, S, D> {
|
||||||
type Item = SyntaxElementRef<'n, L, D>;
|
type Item = SyntaxElementRef<'n, S, D>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
@ -159,10 +159,10 @@ impl<'n, L: Language, D> Iterator for SyntaxElementChildren<'n, L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, L: Language, D> ExactSizeIterator for SyntaxElementChildren<'n, L, D> {
|
impl<'n, S: Syntax, D> ExactSizeIterator for SyntaxElementChildren<'n, S, D> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.inner.len()
|
self.inner.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'n, L: Language, D> FusedIterator for SyntaxElementChildren<'n, L, D> {}
|
impl<'n, S: Syntax, D> FusedIterator for SyntaxElementChildren<'n, S, D> {}
|
|
@ -22,10 +22,10 @@ pub use text::SyntaxText;
|
||||||
// A note on `#[inline]` usage in this module:
|
// A note on `#[inline]` usage in this module:
|
||||||
// In `rowan`, there are two layers of `SyntaxXY`s: the `cursor` layer and the `api` layer.
|
// In `rowan`, there are two layers of `SyntaxXY`s: the `cursor` layer and the `api` layer.
|
||||||
// The `cursor` layer handles all of the actual methods on the tree, while the `api` layer is
|
// The `cursor` layer handles all of the actual methods on the tree, while the `api` layer is
|
||||||
// generic over the `Language` of the tree and otherwise forwards its implementation to the `cursor`
|
// generic over the `Syntax` of the tree and otherwise forwards its implementation to the `cursor`
|
||||||
// layer.
|
// layer.
|
||||||
// Here, we have unified the `cursor` and the `api` layer into the `syntax` layer.
|
// Here, we have unified the `cursor` and the `api` layer into the `syntax` layer.
|
||||||
// This means that all of our types here are generic over a `Language`, including the
|
// This means that all of our types here are generic over a `Syntax`, including the
|
||||||
// implementations which, in `rowan`, are part of the `cursor` layer.
|
// implementations which, in `rowan`, are part of the `cursor` layer.
|
||||||
// Very apparently, this makes the compiler less willing to inline. Almost every "regular use"
|
// Very apparently, this makes the compiler less willing to inline. Almost every "regular use"
|
||||||
// method in this file has some kind of `#[inline]` annotation to counteract that. This is _NOT_
|
// method in this file has some kind of `#[inline]` annotation to counteract that. This is _NOT_
|
||||||
|
@ -43,15 +43,15 @@ mod tests {
|
||||||
#[cfg_attr(miri, ignore)]
|
#[cfg_attr(miri, ignore)]
|
||||||
fn assert_send_sync() {
|
fn assert_send_sync() {
|
||||||
fn f<T: Send + Sync>() {}
|
fn f<T: Send + Sync>() {}
|
||||||
f::<SyntaxNode<TestLang>>();
|
f::<SyntaxNode<TestSyntaxKind>>();
|
||||||
f::<SyntaxToken<TestLang>>();
|
f::<SyntaxToken<TestSyntaxKind>>();
|
||||||
f::<SyntaxElement<TestLang>>();
|
f::<SyntaxElement<TestSyntaxKind>>();
|
||||||
f::<SyntaxElementRef<'static, TestLang>>();
|
f::<SyntaxElementRef<'static, TestSyntaxKind>>();
|
||||||
|
|
||||||
f::<ResolvedNode<TestLang>>();
|
f::<ResolvedNode<TestSyntaxKind>>();
|
||||||
f::<ResolvedToken<TestLang>>();
|
f::<ResolvedToken<TestSyntaxKind>>();
|
||||||
f::<ResolvedElement<TestLang>>();
|
f::<ResolvedElement<TestSyntaxKind>>();
|
||||||
f::<ResolvedElementRef<'static, TestLang>>();
|
f::<ResolvedElementRef<'static, TestSyntaxKind>>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -60,10 +60,10 @@ mod tests {
|
||||||
fn assert_syntax_sizes() {
|
fn assert_syntax_sizes() {
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
assert_eq!(size_of::<SyntaxNode<TestLang>>(), size_of::<*const u8>());
|
assert_eq!(size_of::<SyntaxNode<TestSyntaxKind>>(), size_of::<*const u8>());
|
||||||
// verify niche opt of `NonNull`
|
// verify niche opt of `NonNull`
|
||||||
assert_eq!(size_of::<Option<SyntaxNode<TestLang>>>(), size_of::<*const u8>());
|
assert_eq!(size_of::<Option<SyntaxNode<TestSyntaxKind>>>(), size_of::<*const u8>());
|
||||||
// parent + child index + text len
|
// parent + child index + text len
|
||||||
assert_eq!(size_of::<SyntaxToken<TestLang>>(), size_of::<SyntaxNode<TestLang>>() + size_of::<u32>() * 2);
|
assert_eq!(size_of::<SyntaxToken<TestSyntaxKind>>(), size_of::<SyntaxNode<TestSyntaxKind>>() + size_of::<u32>() * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -7,7 +7,7 @@ use crate::{
|
||||||
text::*,
|
text::*,
|
||||||
traversal::*,
|
traversal::*,
|
||||||
util::*,
|
util::*,
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -29,14 +29,14 @@ use triomphe::Arc;
|
||||||
/// individual nodes is relatively cheap.
|
/// individual nodes is relatively cheap.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct SyntaxNode<L: Language, D: 'static = ()> {
|
pub struct SyntaxNode<S: Syntax, D: 'static = ()> {
|
||||||
data: NonNull<NodeData<L, D>>,
|
data: NonNull<NodeData<S, D>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<L: Language, D: 'static> Send for SyntaxNode<L, D> {}
|
unsafe impl<S: Syntax, D: 'static> Send for SyntaxNode<S, D> {}
|
||||||
unsafe impl<L: Language, D: 'static> Sync for SyntaxNode<L, D> {}
|
unsafe impl<S: Syntax, D: 'static> Sync for SyntaxNode<S, D> {}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxNode<L, D> {
|
impl<S: Syntax, D> SyntaxNode<S, D> {
|
||||||
/// Writes this node's [`Debug`](fmt::Debug) representation into the given `target`.
|
/// Writes this node's [`Debug`](fmt::Debug) representation into the given `target`.
|
||||||
/// If `recursive` is `true`, prints the entire subtree rooted in this node.
|
/// If `recursive` is `true`, prints the entire subtree rooted in this node.
|
||||||
/// Otherwise, only this node's kind and range are written.
|
/// Otherwise, only this node's kind and range are written.
|
||||||
|
@ -120,7 +120,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// Turns this node into a [`ResolvedNode`](crate::syntax::ResolvedNode), but only if there is a resolver associated
|
/// Turns this node into a [`ResolvedNode`](crate::syntax::ResolvedNode), but only if there is a resolver associated
|
||||||
/// with this tree.
|
/// with this tree.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_resolved(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn try_resolved(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
// safety: we only coerce if `resolver` exists
|
// safety: we only coerce if `resolver` exists
|
||||||
self.resolver().map(|_| unsafe { ResolvedNode::coerce_ref(self) })
|
self.resolver().map(|_| unsafe { ResolvedNode::coerce_ref(self) })
|
||||||
}
|
}
|
||||||
|
@ -129,12 +129,12 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// If there is no resolver associated with this tree.
|
/// If there is no resolver associated with this tree.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resolved(&self) -> &ResolvedNode<L, D> {
|
pub fn resolved(&self) -> &ResolvedNode<S, D> {
|
||||||
self.try_resolved().expect("tried to resolve a node without resolver")
|
self.try_resolved().expect("tried to resolve a node without resolver")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Clone for SyntaxNode<L, D> {
|
impl<S: Syntax, D> Clone for SyntaxNode<S, D> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
// safety:: the ref count is only dropped when there are no more external references (see below)
|
// safety:: the ref count is only dropped when there are no more external references (see below)
|
||||||
// since we are currently cloning such a reference, there is still at least one
|
// since we are currently cloning such a reference, there is still at least one
|
||||||
|
@ -144,7 +144,7 @@ impl<L: Language, D> Clone for SyntaxNode<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Drop for SyntaxNode<L, D> {
|
impl<S: Syntax, D> Drop for SyntaxNode<S, D> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// safety:: the ref count is only dropped when there are no more external references (see below)
|
// safety:: the ref count is only dropped when there are no more external references (see below)
|
||||||
// and all nodes but the root have been dropped.
|
// and all nodes but the root have been dropped.
|
||||||
|
@ -169,9 +169,9 @@ impl<L: Language, D> Drop for SyntaxNode<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxNode<L, D> {
|
impl<S: Syntax, D> SyntaxNode<S, D> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn data(&self) -> &NodeData<L, D> {
|
fn data(&self) -> &NodeData<S, D> {
|
||||||
unsafe { self.data.as_ref() }
|
unsafe { self.data.as_ref() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +183,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// The root of the tree this node belongs to.
|
/// The root of the tree this node belongs to.
|
||||||
///
|
///
|
||||||
/// If this node is the root, returns `self`.
|
/// If this node is the root, returns `self`.
|
||||||
pub fn root(&self) -> &SyntaxNode<L, D> {
|
pub fn root(&self) -> &SyntaxNode<S, D> {
|
||||||
let mut current = self;
|
let mut current = self;
|
||||||
while let Some(parent) = current.parent() {
|
while let Some(parent) = current.parent() {
|
||||||
current = parent;
|
current = parent;
|
||||||
|
@ -222,31 +222,31 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identity semantics for hash & eq
|
// Identity semantics for hash & eq
|
||||||
impl<L: Language, D> PartialEq for SyntaxNode<L, D> {
|
impl<S: Syntax, D> PartialEq for SyntaxNode<S, D> {
|
||||||
fn eq(&self, other: &SyntaxNode<L, D>) -> bool {
|
fn eq(&self, other: &SyntaxNode<S, D>) -> bool {
|
||||||
self.data == other.data
|
self.data == other.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Eq for SyntaxNode<L, D> {}
|
impl<S: Syntax, D> Eq for SyntaxNode<S, D> {}
|
||||||
|
|
||||||
impl<L: Language, D> Hash for SyntaxNode<L, D> {
|
impl<S: Syntax, D> Hash for SyntaxNode<S, D> {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.data.hash(state);
|
self.data.hash(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Kind<L: Language, D: 'static> {
|
enum Kind<S: Syntax, D: 'static> {
|
||||||
Root(GreenNode, Option<StdArc<dyn Resolver<TokenKey>>>),
|
Root(GreenNode, Option<StdArc<dyn Resolver<TokenKey>>>),
|
||||||
Child {
|
Child {
|
||||||
parent: SyntaxNode<L, D>,
|
parent: SyntaxNode<S, D>,
|
||||||
index: u32,
|
index: u32,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Kind<L, D> {
|
impl<S: Syntax, D> Kind<S, D> {
|
||||||
fn as_child(&self) -> Option<(&SyntaxNode<L, D>, u32, TextSize)> {
|
fn as_child(&self) -> Option<(&SyntaxNode<S, D>, u32, TextSize)> {
|
||||||
match self {
|
match self {
|
||||||
Kind::Child { parent, index, offset } => Some((parent, *index, *offset)),
|
Kind::Child { parent, index, offset } => Some((parent, *index, *offset)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -254,17 +254,17 @@ impl<L: Language, D> Kind<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct NodeData<L: Language, D: 'static> {
|
pub(super) struct NodeData<S: Syntax, D: 'static> {
|
||||||
kind: Kind<L, D>,
|
kind: Kind<S, D>,
|
||||||
green: NonNull<GreenNode>,
|
green: NonNull<GreenNode>,
|
||||||
ref_count: *mut AtomicU32,
|
ref_count: *mut AtomicU32,
|
||||||
data: RwLock<Option<Arc<D>>>,
|
data: RwLock<Option<Arc<D>>>,
|
||||||
children: Vec<UnsafeCell<Option<SyntaxElement<L, D>>>>,
|
children: Vec<UnsafeCell<Option<SyntaxElement<S, D>>>>,
|
||||||
child_locks: Vec<RwLock<()>>,
|
child_locks: Vec<RwLock<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> NodeData<L, D> {
|
impl<S: Syntax, D> NodeData<S, D> {
|
||||||
fn new(kind: Kind<L, D>, green: NonNull<GreenNode>, ref_count: *mut AtomicU32, n_children: usize) -> NonNull<Self> {
|
fn new(kind: Kind<S, D>, green: NonNull<GreenNode>, ref_count: *mut AtomicU32, n_children: usize) -> NonNull<Self> {
|
||||||
let mut children = Vec::with_capacity(n_children);
|
let mut children = Vec::with_capacity(n_children);
|
||||||
let mut child_locks = Vec::with_capacity(n_children);
|
let mut child_locks = Vec::with_capacity(n_children);
|
||||||
children.extend((0..n_children).map(|_| Default::default()));
|
children.extend((0..n_children).map(|_| Default::default()));
|
||||||
|
@ -282,17 +282,17 @@ impl<L: Language, D> NodeData<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxNode<L, D> {
|
impl<S: Syntax, D> SyntaxNode<S, D> {
|
||||||
/// Build a new syntax tree on top of a green tree.
|
/// Build a new syntax tree on top of a green tree.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// # let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// # let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// # builder.start_node(Root);
|
/// # builder.start_node(Root);
|
||||||
/// # builder.finish_node();
|
/// # builder.finish_node();
|
||||||
/// # let (green_root, _) = builder.finish();
|
/// # let (green_root, _) = builder.finish();
|
||||||
/// let root: SyntaxNode<MyLanguage> = SyntaxNode::new_root(green_root);
|
/// let root: SyntaxNode<MySyntax> = SyntaxNode::new_root(green_root);
|
||||||
/// assert_eq!(root.kind(), Root);
|
/// assert_eq!(root.kind(), Root);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -300,7 +300,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
Self::make_new_root(green, None)
|
Self::make_new_root(green, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(data: NonNull<NodeData<L, D>>) -> Self {
|
fn new(data: NonNull<NodeData<S, D>>) -> Self {
|
||||||
Self { data }
|
Self { data }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +334,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// use cstree::syntax::ResolvedNode;
|
/// use cstree::syntax::ResolvedNode;
|
||||||
///
|
///
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// builder.start_node(Root);
|
/// builder.start_node(Root);
|
||||||
/// builder.token(Identifier, "content");
|
/// builder.token(Identifier, "content");
|
||||||
/// builder.finish_node();
|
/// builder.finish_node();
|
||||||
|
@ -344,11 +344,11 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// // This created a new interner and cache for us owned by the builder,
|
/// // This created a new interner and cache for us owned by the builder,
|
||||||
/// // and `finish` always returns these.
|
/// // and `finish` always returns these.
|
||||||
/// let interner = cache.unwrap().into_interner().unwrap();
|
/// let interner = cache.unwrap().into_interner().unwrap();
|
||||||
/// let root: ResolvedNode<MyLanguage> = SyntaxNode::new_root_with_resolver(green, interner);
|
/// let root: ResolvedNode<MySyntax> = SyntaxNode::new_root_with_resolver(green, interner);
|
||||||
/// assert_eq!(root.text(), "content");
|
/// assert_eq!(root.text(), "content");
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_root_with_resolver(green: GreenNode, resolver: impl Resolver<TokenKey> + 'static) -> ResolvedNode<L, D> {
|
pub fn new_root_with_resolver(green: GreenNode, resolver: impl Resolver<TokenKey> + 'static) -> ResolvedNode<S, D> {
|
||||||
let ptr: StdArc<dyn Resolver<TokenKey>> = StdArc::new(resolver);
|
let ptr: StdArc<dyn Resolver<TokenKey>> = StdArc::new(resolver);
|
||||||
ResolvedNode {
|
ResolvedNode {
|
||||||
syntax: SyntaxNode::make_new_root(green, Some(ptr)),
|
syntax: SyntaxNode::make_new_root(green, Some(ptr)),
|
||||||
|
@ -412,7 +412,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read(&self, index: usize) -> Option<SyntaxElementRef<'_, L, D>> {
|
fn read(&self, index: usize) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
// safety: children are pre-allocated and indices are determined internally
|
// safety: children are pre-allocated and indices are determined internally
|
||||||
let _read = unsafe { self.data().child_locks.get_unchecked(index).read() };
|
let _read = unsafe { self.data().child_locks.get_unchecked(index).read() };
|
||||||
// safety: mutable accesses to the slot only occur below and have to take the lock
|
// safety: mutable accesses to the slot only occur below and have to take the lock
|
||||||
|
@ -420,7 +420,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
slot.as_ref().map(|elem| elem.into())
|
slot.as_ref().map(|elem| elem.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_write(&self, index: usize, elem: SyntaxElement<L, D>) {
|
fn try_write(&self, index: usize, elem: SyntaxElement<S, D>) {
|
||||||
// safety: children are pre-allocated and indices are determined internally
|
// safety: children are pre-allocated and indices are determined internally
|
||||||
let _write = unsafe { self.data().child_locks.get_unchecked(index).write() };
|
let _write = unsafe { self.data().child_locks.get_unchecked(index).write() };
|
||||||
// safety: we are the only writer and there are no readers as evidenced by the write lock
|
// safety: we are the only writer and there are no readers as evidenced by the write lock
|
||||||
|
@ -469,7 +469,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
node: &GreenNode,
|
node: &GreenNode,
|
||||||
index: usize,
|
index: usize,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) -> SyntaxElementRef<'_, L, D> {
|
) -> SyntaxElementRef<'_, S, D> {
|
||||||
if let Some(elem) = self.read(index) {
|
if let Some(elem) = self.read(index) {
|
||||||
debug_assert_eq!(elem.text_range().start(), offset);
|
debug_assert_eq!(elem.text_range().start(), offset);
|
||||||
return elem;
|
return elem;
|
||||||
|
@ -487,7 +487,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
element: GreenElementRef<'_>,
|
element: GreenElementRef<'_>,
|
||||||
index: usize,
|
index: usize,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
) -> SyntaxElementRef<'_, L, D> {
|
) -> SyntaxElementRef<'_, S, D> {
|
||||||
if let Some(elem) = self.read(index) {
|
if let Some(elem) = self.read(index) {
|
||||||
debug_assert_eq!(elem.text_range().start(), offset);
|
debug_assert_eq!(elem.text_range().start(), offset);
|
||||||
return elem;
|
return elem;
|
||||||
|
@ -529,8 +529,8 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// The kind of this node in terms of your language.
|
/// The kind of this node in terms of your language.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> L::Kind {
|
pub fn kind(&self) -> S {
|
||||||
L::kind_from_raw(self.syntax_kind())
|
S::from_raw(self.syntax_kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The range this node covers in the source text, in bytes.
|
/// The range this node covers in the source text, in bytes.
|
||||||
|
@ -547,7 +547,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// by this node, i.e. the combined text of all token leafs of the subtree originating in this
|
/// by this node, i.e. the combined text of all token leafs of the subtree originating in this
|
||||||
/// node.
|
/// node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resolve_text<'n, 'i, I>(&'n self, resolver: &'i I) -> SyntaxText<'n, 'i, I, L, D>
|
pub fn resolve_text<'n, 'i, I>(&'n self, resolver: &'i I) -> SyntaxText<'n, 'i, I, S, D>
|
||||||
where
|
where
|
||||||
I: Resolver<TokenKey> + ?Sized,
|
I: Resolver<TokenKey> + ?Sized,
|
||||||
{
|
{
|
||||||
|
@ -562,7 +562,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// The parent node of this node, except if this node is the root.
|
/// The parent node of this node, except if this node is the root.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> Option<&SyntaxNode<L, D>> {
|
pub fn parent(&self) -> Option<&SyntaxNode<S, D>> {
|
||||||
match &self.data().kind {
|
match &self.data().kind {
|
||||||
Kind::Root(_, _) => None,
|
Kind::Root(_, _) => None,
|
||||||
Kind::Child { parent, .. } => Some(parent),
|
Kind::Child { parent, .. } => Some(parent),
|
||||||
|
@ -585,7 +585,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this node.
|
/// Returns an iterator along the chain of parents of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<S, D>> {
|
||||||
iter::successors(Some(self), |&node| node.parent())
|
iter::successors(Some(self), |&node| node.parent())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -593,13 +593,13 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`children_with_tokens`](SyntaxNode::children_with_tokens).
|
/// If you want to also consider leafs, see [`children_with_tokens`](SyntaxNode::children_with_tokens).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn children(&self) -> SyntaxNodeChildren<'_, L, D> {
|
pub fn children(&self) -> SyntaxNodeChildren<'_, S, D> {
|
||||||
SyntaxNodeChildren::new(self)
|
SyntaxNodeChildren::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over child elements of this node, including tokens.
|
/// Returns an iterator over child elements of this node, including tokens.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn children_with_tokens(&self) -> SyntaxElementChildren<'_, L, D> {
|
pub fn children_with_tokens(&self) -> SyntaxElementChildren<'_, S, D> {
|
||||||
SyntaxElementChildren::new(self)
|
SyntaxElementChildren::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -608,14 +608,14 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// If you want to also consider leafs, see [`first_child_or_token`](SyntaxNode::first_child_or_token).
|
/// If you want to also consider leafs, see [`first_child_or_token`](SyntaxNode::first_child_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::map_clone)]
|
#[allow(clippy::map_clone)]
|
||||||
pub fn first_child(&self) -> Option<&SyntaxNode<L, D>> {
|
pub fn first_child(&self) -> Option<&SyntaxNode<S, D>> {
|
||||||
let (node, (index, offset)) = filter_nodes(self.green().children_from(0, self.text_range().start())).next()?;
|
let (node, (index, offset)) = filter_nodes(self.green().children_from(0, self.text_range().start())).next()?;
|
||||||
self.get_or_add_node(node, index, offset).as_node().map(|node| *node)
|
self.get_or_add_node(node, index, offset).as_node().map(|node| *node)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The first child element of this node, if any, including tokens.
|
/// The first child element of this node, if any, including tokens.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_child_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn first_child_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
let (element, (index, offset)) = self.green().children_from(0, self.text_range().start()).next()?;
|
let (element, (index, offset)) = self.green().children_from(0, self.text_range().start()).next()?;
|
||||||
Some(self.get_or_add_element(element, index, offset))
|
Some(self.get_or_add_element(element, index, offset))
|
||||||
}
|
}
|
||||||
|
@ -625,7 +625,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// If you want to also consider leafs, see [`last_child_or_token`](SyntaxNode::last_child_or_token).
|
/// If you want to also consider leafs, see [`last_child_or_token`](SyntaxNode::last_child_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::map_clone)]
|
#[allow(clippy::map_clone)]
|
||||||
pub fn last_child(&self) -> Option<&SyntaxNode<L, D>> {
|
pub fn last_child(&self) -> Option<&SyntaxNode<S, D>> {
|
||||||
let (node, (index, offset)) = filter_nodes(
|
let (node, (index, offset)) = filter_nodes(
|
||||||
self.green()
|
self.green()
|
||||||
.children_to(self.green().children().len(), self.text_range().end()),
|
.children_to(self.green().children().len(), self.text_range().end()),
|
||||||
|
@ -636,7 +636,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// The last child element of this node, if any, including tokens.
|
/// The last child element of this node, if any, including tokens.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_child_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn last_child_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
let (element, (index, offset)) = self
|
let (element, (index, offset)) = self
|
||||||
.green()
|
.green()
|
||||||
.children_to(self.green().children().len(), self.text_range().end())
|
.children_to(self.green().children().len(), self.text_range().end())
|
||||||
|
@ -650,7 +650,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`next_child_or_token_after`](SyntaxNode::next_child_or_token_after).
|
/// If you want to also consider leafs, see [`next_child_or_token_after`](SyntaxNode::next_child_or_token_after).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_child_after(&self, n: usize, offset: TextSize) -> Option<&SyntaxNode<L, D>> {
|
pub fn next_child_after(&self, n: usize, offset: TextSize) -> Option<&SyntaxNode<S, D>> {
|
||||||
let (node, (index, offset)) = filter_nodes(self.green().children_from(n + 1, offset)).next()?;
|
let (node, (index, offset)) = filter_nodes(self.green().children_from(n + 1, offset)).next()?;
|
||||||
self.get_or_add_node(node, index, offset).as_node().copied()
|
self.get_or_add_node(node, index, offset).as_node().copied()
|
||||||
}
|
}
|
||||||
|
@ -658,7 +658,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// The first child element of this node starting at the (n + 1)-st, if any.
|
/// The first child element of this node starting at the (n + 1)-st, if any.
|
||||||
/// If this method returns `Some`, the contained node is the (n + 1)-st child of this node.
|
/// If this method returns `Some`, the contained node is the (n + 1)-st child of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_child_or_token_after(&self, n: usize, offset: TextSize) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn next_child_or_token_after(&self, n: usize, offset: TextSize) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
let (element, (index, offset)) = self.green().children_from(n + 1, offset).next()?;
|
let (element, (index, offset)) = self.green().children_from(n + 1, offset).next()?;
|
||||||
Some(self.get_or_add_element(element, index, offset))
|
Some(self.get_or_add_element(element, index, offset))
|
||||||
}
|
}
|
||||||
|
@ -669,7 +669,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`prev_child_or_token_before`](SyntaxNode::prev_child_or_token_before).
|
/// If you want to also consider leafs, see [`prev_child_or_token_before`](SyntaxNode::prev_child_or_token_before).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_child_before(&self, n: usize, offset: TextSize) -> Option<&SyntaxNode<L, D>> {
|
pub fn prev_child_before(&self, n: usize, offset: TextSize) -> Option<&SyntaxNode<S, D>> {
|
||||||
let (node, (index, offset)) = filter_nodes(self.green().children_to(n, offset)).next()?;
|
let (node, (index, offset)) = filter_nodes(self.green().children_to(n, offset)).next()?;
|
||||||
self.get_or_add_node(node, index, offset).as_node().copied()
|
self.get_or_add_node(node, index, offset).as_node().copied()
|
||||||
}
|
}
|
||||||
|
@ -677,7 +677,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// The last child node of this node up to the nth, if any.
|
/// The last child node of this node up to the nth, if any.
|
||||||
/// If this method returns `Some`, the contained node is the (n - 1)-st child.
|
/// If this method returns `Some`, the contained node is the (n - 1)-st child.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_child_or_token_before(&self, n: usize, offset: TextSize) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn prev_child_or_token_before(&self, n: usize, offset: TextSize) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
let (element, (index, offset)) = self.green().children_to(n, offset).next()?;
|
let (element, (index, offset)) = self.green().children_to(n, offset).next()?;
|
||||||
Some(self.get_or_add_element(element, index, offset))
|
Some(self.get_or_add_element(element, index, offset))
|
||||||
}
|
}
|
||||||
|
@ -686,7 +686,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`next_sibling_or_token`](SyntaxNode::next_sibling_or_token).
|
/// If you want to also consider leafs, see [`next_sibling_or_token`](SyntaxNode::next_sibling_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling(&self) -> Option<&SyntaxNode<L, D>> {
|
pub fn next_sibling(&self) -> Option<&SyntaxNode<S, D>> {
|
||||||
let (parent, index, _) = self.data().kind.as_child()?;
|
let (parent, index, _) = self.data().kind.as_child()?;
|
||||||
|
|
||||||
let (node, (index, offset)) = filter_nodes(
|
let (node, (index, offset)) = filter_nodes(
|
||||||
|
@ -700,7 +700,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this node's parent after this node.
|
/// The tree element to the right of this one, i.e. the next child of this node's parent after this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
let (parent, index, _) = self.data().kind.as_child()?;
|
let (parent, index, _) = self.data().kind.as_child()?;
|
||||||
|
|
||||||
let (element, (index, offset)) = parent
|
let (element, (index, offset)) = parent
|
||||||
|
@ -714,7 +714,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`prev_sibling_or_token`](SyntaxNode::prev_sibling_or_token).
|
/// If you want to also consider leafs, see [`prev_sibling_or_token`](SyntaxNode::prev_sibling_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling(&self) -> Option<&SyntaxNode<L, D>> {
|
pub fn prev_sibling(&self) -> Option<&SyntaxNode<S, D>> {
|
||||||
let (parent, index, _) = self.data().kind.as_child()?;
|
let (parent, index, _) = self.data().kind.as_child()?;
|
||||||
|
|
||||||
let (node, (index, offset)) =
|
let (node, (index, offset)) =
|
||||||
|
@ -724,7 +724,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this node's parent before this node.
|
/// The tree element to the left of this one, i.e. the previous child of this node's parent before this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
let (parent, index, _) = self.data().kind.as_child()?;
|
let (parent, index, _) = self.data().kind.as_child()?;
|
||||||
|
|
||||||
let (element, (index, offset)) = parent
|
let (element, (index, offset)) = parent
|
||||||
|
@ -736,13 +736,13 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// Return the leftmost token in the subtree of this node
|
/// Return the leftmost token in the subtree of this node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_token(&self) -> Option<&SyntaxToken<L, D>> {
|
pub fn first_token(&self) -> Option<&SyntaxToken<S, D>> {
|
||||||
self.first_child_or_token()?.first_token()
|
self.first_child_or_token()?.first_token()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the rightmost token in the subtree of this node
|
/// Return the rightmost token in the subtree of this node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_token(&self) -> Option<&SyntaxToken<L, D>> {
|
pub fn last_token(&self) -> Option<&SyntaxToken<S, D>> {
|
||||||
self.last_child_or_token()?.last_token()
|
self.last_child_or_token()?.last_token()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,7 +752,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`siblings_with_tokens`](SyntaxNode::siblings_with_tokens).
|
/// If you want to also consider leafs, see [`siblings_with_tokens`](SyntaxNode::siblings_with_tokens).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode<L, D>> {
|
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode<S, D>> {
|
||||||
iter::successors(Some(self), move |node| match direction {
|
iter::successors(Some(self), move |node| match direction {
|
||||||
Direction::Next => node.next_sibling(),
|
Direction::Next => node.next_sibling(),
|
||||||
Direction::Prev => node.prev_sibling(),
|
Direction::Prev => node.prev_sibling(),
|
||||||
|
@ -763,8 +763,8 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// node's parent's children from this node on to the left or the right.
|
/// node's parent's children from this node on to the left or the right.
|
||||||
/// The first item in the iterator will always be this node.
|
/// The first item in the iterator will always be this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElementRef<'_, L, D>> {
|
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElementRef<'_, S, D>> {
|
||||||
let me: SyntaxElementRef<'_, L, D> = self.into();
|
let me: SyntaxElementRef<'_, S, D> = self.into();
|
||||||
iter::successors(Some(me), move |el| match direction {
|
iter::successors(Some(me), move |el| match direction {
|
||||||
Direction::Next => el.next_sibling_or_token(),
|
Direction::Next => el.next_sibling_or_token(),
|
||||||
Direction::Prev => el.prev_sibling_or_token(),
|
Direction::Prev => el.prev_sibling_or_token(),
|
||||||
|
@ -775,7 +775,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`descendants_with_tokens`](SyntaxNode::descendants_with_tokens).
|
/// If you want to also consider leafs, see [`descendants_with_tokens`](SyntaxNode::descendants_with_tokens).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode<L, D>> {
|
pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode<S, D>> {
|
||||||
self.preorder().filter_map(|event| match event {
|
self.preorder().filter_map(|event| match event {
|
||||||
WalkEvent::Enter(node) => Some(node),
|
WalkEvent::Enter(node) => Some(node),
|
||||||
WalkEvent::Leave(_) => None,
|
WalkEvent::Leave(_) => None,
|
||||||
|
@ -784,7 +784,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// Returns an iterator over all elements in the subtree starting at this node, including this node.
|
/// Returns an iterator over all elements in the subtree starting at this node, including this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = SyntaxElementRef<'_, L, D>> {
|
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = SyntaxElementRef<'_, S, D>> {
|
||||||
self.preorder_with_tokens().filter_map(|event| match event {
|
self.preorder_with_tokens().filter_map(|event| match event {
|
||||||
WalkEvent::Enter(it) => Some(it),
|
WalkEvent::Enter(it) => Some(it),
|
||||||
WalkEvent::Leave(_) => None,
|
WalkEvent::Leave(_) => None,
|
||||||
|
@ -794,7 +794,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// Traverse the subtree rooted at the current node (including the current
|
/// Traverse the subtree rooted at the current node (including the current
|
||||||
/// node) in preorder, excluding tokens.
|
/// node) in preorder, excluding tokens.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode<L, D>>> {
|
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode<S, D>>> {
|
||||||
iter::successors(Some(WalkEvent::Enter(self)), move |pos| {
|
iter::successors(Some(WalkEvent::Enter(self)), move |pos| {
|
||||||
let next = match pos {
|
let next = match pos {
|
||||||
WalkEvent::Enter(node) => match node.first_child() {
|
WalkEvent::Enter(node) => match node.first_child() {
|
||||||
|
@ -818,7 +818,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// Traverse the subtree rooted at the current node (including the current
|
/// Traverse the subtree rooted at the current node (including the current
|
||||||
/// node) in preorder, including tokens.
|
/// node) in preorder, including tokens.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<SyntaxElementRef<'_, L, D>>> {
|
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<SyntaxElementRef<'_, S, D>>> {
|
||||||
let me = self.into();
|
let me = self.into();
|
||||||
iter::successors(Some(WalkEvent::Enter(me)), move |pos| {
|
iter::successors(Some(WalkEvent::Enter(me)), move |pos| {
|
||||||
let next = match pos {
|
let next = match pos {
|
||||||
|
@ -845,7 +845,7 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
|
|
||||||
/// Find a token in the subtree corresponding to this node, which covers the offset.
|
/// Find a token in the subtree corresponding to this node, which covers the offset.
|
||||||
/// Precondition: offset must be withing node's range.
|
/// Precondition: offset must be withing node's range.
|
||||||
pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken<L, D>> {
|
pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<SyntaxToken<S, D>> {
|
||||||
// TODO: this could be faster if we first drill-down to node, and only
|
// TODO: this could be faster if we first drill-down to node, and only
|
||||||
// then switch to token search. We should also replace explicit
|
// then switch to token search. We should also replace explicit
|
||||||
// recursion with a loop.
|
// recursion with a loop.
|
||||||
|
@ -883,8 +883,8 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
/// contains the range. If the range is empty and is contained in two leaf
|
/// contains the range. If the range is empty and is contained in two leaf
|
||||||
/// nodes, either one can be returned. Precondition: range must be contained
|
/// nodes, either one can be returned. Precondition: range must be contained
|
||||||
/// withing the current node
|
/// withing the current node
|
||||||
pub fn covering_element(&self, range: TextRange) -> SyntaxElementRef<'_, L, D> {
|
pub fn covering_element(&self, range: TextRange) -> SyntaxElementRef<'_, S, D> {
|
||||||
let mut res: SyntaxElementRef<'_, L, D> = self.into();
|
let mut res: SyntaxElementRef<'_, S, D> = self.into();
|
||||||
loop {
|
loop {
|
||||||
assert!(
|
assert!(
|
||||||
res.text_range().contains_range(range),
|
res.text_range().contains_range(range),
|
||||||
|
@ -909,9 +909,9 @@ impl<L: Language, D> SyntaxNode<L, D> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl<L, D> SyntaxNode<L, D>
|
impl<S, D> SyntaxNode<S, D>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
{
|
{
|
||||||
/// Return an anonymous object that can be used to serialize this node,
|
/// Return an anonymous object that can be used to serialize this node,
|
||||||
/// including the data and by using an external resolver.
|
/// including the data and by using an external resolver.
|
|
@ -17,7 +17,7 @@ use crate::{
|
||||||
syntax::*,
|
syntax::*,
|
||||||
traversal::*,
|
traversal::*,
|
||||||
util::*,
|
util::*,
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Syntax tree node that is guaranteed to belong to a tree that contains an associated
|
/// Syntax tree node that is guaranteed to belong to a tree that contains an associated
|
||||||
|
@ -26,24 +26,24 @@ use crate::{
|
||||||
/// [`SyntaxNode`]
|
/// [`SyntaxNode`]
|
||||||
/// [`SyntaxNode::new_root_with_resolver`]
|
/// [`SyntaxNode::new_root_with_resolver`]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ResolvedNode<L: Language, D: 'static = ()> {
|
pub struct ResolvedNode<S: Syntax, D: 'static = ()> {
|
||||||
pub(super) syntax: SyntaxNode<L, D>,
|
pub(super) syntax: SyntaxNode<S, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedNode<L, D> {
|
impl<S: Syntax, D> ResolvedNode<S, D> {
|
||||||
/// # Safety:
|
/// # Safety:
|
||||||
/// `syntax` must belong to a tree that contains an associated inline resolver.
|
/// `syntax` must belong to a tree that contains an associated inline resolver.
|
||||||
pub(super) unsafe fn coerce_ref(syntax: &SyntaxNode<L, D>) -> &Self {
|
pub(super) unsafe fn coerce_ref(syntax: &SyntaxNode<S, D>) -> &Self {
|
||||||
&*(syntax as *const _ as *const Self)
|
&*(syntax as *const _ as *const Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns this node as a [`SyntaxNode`].
|
/// Returns this node as a [`SyntaxNode`].
|
||||||
pub fn syntax(&self) -> &SyntaxNode<L, D> {
|
pub fn syntax(&self) -> &SyntaxNode<S, D> {
|
||||||
&self.syntax
|
&self.syntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Clone for ResolvedNode<L, D> {
|
impl<S: Syntax, D> Clone for ResolvedNode<S, D> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
syntax: self.syntax.clone(),
|
syntax: self.syntax.clone(),
|
||||||
|
@ -51,15 +51,15 @@ impl<L: Language, D> Clone for ResolvedNode<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Deref for ResolvedNode<L, D> {
|
impl<S: Syntax, D> Deref for ResolvedNode<S, D> {
|
||||||
type Target = SyntaxNode<L, D>;
|
type Target = SyntaxNode<S, D>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.syntax
|
&self.syntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> DerefMut for ResolvedNode<L, D> {
|
impl<S: Syntax, D> DerefMut for ResolvedNode<S, D> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.syntax
|
&mut self.syntax
|
||||||
}
|
}
|
||||||
|
@ -70,24 +70,24 @@ impl<L: Language, D> DerefMut for ResolvedNode<L, D> {
|
||||||
/// # See also
|
/// # See also
|
||||||
/// [`SyntaxToken`]
|
/// [`SyntaxToken`]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct ResolvedToken<L: Language, D: 'static = ()> {
|
pub struct ResolvedToken<S: Syntax, D: 'static = ()> {
|
||||||
syntax: SyntaxToken<L, D>,
|
syntax: SyntaxToken<S, D>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedToken<L, D> {
|
impl<S: Syntax, D> ResolvedToken<S, D> {
|
||||||
/// # Safety:
|
/// # Safety:
|
||||||
/// `syntax` must belong to a tree that contains an associated inline resolver.
|
/// `syntax` must belong to a tree that contains an associated inline resolver.
|
||||||
pub(super) unsafe fn coerce_ref(syntax: &SyntaxToken<L, D>) -> &Self {
|
pub(super) unsafe fn coerce_ref(syntax: &SyntaxToken<S, D>) -> &Self {
|
||||||
&*(syntax as *const _ as *const Self)
|
&*(syntax as *const _ as *const Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns this token as a [`SyntaxToken`].
|
/// Returns this token as a [`SyntaxToken`].
|
||||||
pub fn syntax(&self) -> &SyntaxToken<L, D> {
|
pub fn syntax(&self) -> &SyntaxToken<S, D> {
|
||||||
&self.syntax
|
&self.syntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Clone for ResolvedToken<L, D> {
|
impl<S: Syntax, D> Clone for ResolvedToken<S, D> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
syntax: self.syntax.clone(),
|
syntax: self.syntax.clone(),
|
||||||
|
@ -95,15 +95,15 @@ impl<L: Language, D> Clone for ResolvedToken<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Deref for ResolvedToken<L, D> {
|
impl<S: Syntax, D> Deref for ResolvedToken<S, D> {
|
||||||
type Target = SyntaxToken<L, D>;
|
type Target = SyntaxToken<S, D>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.syntax
|
&self.syntax
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> DerefMut for ResolvedToken<L, D> {
|
impl<S: Syntax, D> DerefMut for ResolvedToken<S, D> {
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
&mut self.syntax
|
&mut self.syntax
|
||||||
}
|
}
|
||||||
|
@ -113,21 +113,21 @@ impl<L: Language, D> DerefMut for ResolvedToken<L, D> {
|
||||||
/// [`Resolver`](lasso::Resolver), can be either a node or a token.
|
/// [`Resolver`](lasso::Resolver), can be either a node or a token.
|
||||||
/// # See also
|
/// # See also
|
||||||
/// [`SyntaxElement`](crate::syntax::SyntaxElement)
|
/// [`SyntaxElement`](crate::syntax::SyntaxElement)
|
||||||
pub type ResolvedElement<L, D = ()> = NodeOrToken<ResolvedNode<L, D>, ResolvedToken<L, D>>;
|
pub type ResolvedElement<S, D = ()> = NodeOrToken<ResolvedNode<S, D>, ResolvedToken<S, D>>;
|
||||||
|
|
||||||
impl<L: Language, D> From<ResolvedNode<L, D>> for ResolvedElement<L, D> {
|
impl<S: Syntax, D> From<ResolvedNode<S, D>> for ResolvedElement<S, D> {
|
||||||
fn from(node: ResolvedNode<L, D>) -> ResolvedElement<L, D> {
|
fn from(node: ResolvedNode<S, D>) -> ResolvedElement<S, D> {
|
||||||
NodeOrToken::Node(node)
|
NodeOrToken::Node(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> From<ResolvedToken<L, D>> for ResolvedElement<L, D> {
|
impl<S: Syntax, D> From<ResolvedToken<S, D>> for ResolvedElement<S, D> {
|
||||||
fn from(token: ResolvedToken<L, D>) -> ResolvedElement<L, D> {
|
fn from(token: ResolvedToken<S, D>) -> ResolvedElement<S, D> {
|
||||||
NodeOrToken::Token(token)
|
NodeOrToken::Token(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedElement<L, D> {
|
impl<S: Syntax, D> ResolvedElement<S, D> {
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub fn display(&self, resolver: &impl Resolver<TokenKey>) -> String {
|
pub fn display(&self, resolver: &impl Resolver<TokenKey>) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
@ -141,12 +141,12 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
/// associated [`Resolver`](lasso::Resolver), can be either a reference to a node or one to a token.
|
/// associated [`Resolver`](lasso::Resolver), can be either a reference to a node or one to a token.
|
||||||
/// # See also
|
/// # See also
|
||||||
/// [`SyntaxElementRef`]
|
/// [`SyntaxElementRef`]
|
||||||
pub type ResolvedElementRef<'a, L, D = ()> = NodeOrToken<&'a ResolvedNode<L, D>, &'a ResolvedToken<L, D>>;
|
pub type ResolvedElementRef<'a, S, D = ()> = NodeOrToken<&'a ResolvedNode<S, D>, &'a ResolvedToken<S, D>>;
|
||||||
|
|
||||||
impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> ResolvedElementRef<'a, S, D> {
|
||||||
/// # Safety:
|
/// # Safety:
|
||||||
/// `syntax` must belong to a tree that contains an associated inline resolver.
|
/// `syntax` must belong to a tree that contains an associated inline resolver.
|
||||||
pub(super) unsafe fn coerce_ref(syntax: SyntaxElementRef<'a, L, D>) -> Self {
|
pub(super) unsafe fn coerce_ref(syntax: SyntaxElementRef<'a, S, D>) -> Self {
|
||||||
match syntax {
|
match syntax {
|
||||||
NodeOrToken::Node(node) => Self::Node(ResolvedNode::coerce_ref(node)),
|
NodeOrToken::Node(node) => Self::Node(ResolvedNode::coerce_ref(node)),
|
||||||
NodeOrToken::Token(token) => Self::Token(ResolvedToken::coerce_ref(token)),
|
NodeOrToken::Token(token) => Self::Token(ResolvedToken::coerce_ref(token)),
|
||||||
|
@ -154,20 +154,20 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> From<&'a ResolvedNode<L, D>> for ResolvedElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> From<&'a ResolvedNode<S, D>> for ResolvedElementRef<'a, S, D> {
|
||||||
fn from(node: &'a ResolvedNode<L, D>) -> Self {
|
fn from(node: &'a ResolvedNode<S, D>) -> Self {
|
||||||
NodeOrToken::Node(node)
|
NodeOrToken::Node(node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> From<&'a ResolvedToken<L, D>> for ResolvedElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> From<&'a ResolvedToken<S, D>> for ResolvedElementRef<'a, S, D> {
|
||||||
fn from(token: &'a ResolvedToken<L, D>) -> Self {
|
fn from(token: &'a ResolvedToken<S, D>) -> Self {
|
||||||
NodeOrToken::Token(token)
|
NodeOrToken::Token(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> From<&'a ResolvedElement<L, D>> for ResolvedElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> From<&'a ResolvedElement<S, D>> for ResolvedElementRef<'a, S, D> {
|
||||||
fn from(element: &'a ResolvedElement<L, D>) -> Self {
|
fn from(element: &'a ResolvedElement<S, D>) -> Self {
|
||||||
match element {
|
match element {
|
||||||
NodeOrToken::Node(it) => Self::Node(it),
|
NodeOrToken::Node(it) => Self::Node(it),
|
||||||
NodeOrToken::Token(it) => Self::Token(it),
|
NodeOrToken::Token(it) => Self::Token(it),
|
||||||
|
@ -175,29 +175,29 @@ impl<'a, L: Language, D> From<&'a ResolvedElement<L, D>> for ResolvedElementRef<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedNode<L, D> {
|
impl<S: Syntax, D> ResolvedNode<S, D> {
|
||||||
/// Uses the resolver associated with this tree to return an efficient representation of all
|
/// Uses the resolver associated with this tree to return an efficient representation of all
|
||||||
/// source text covered by this node, i.e. the combined text of all token leafs of the subtree
|
/// source text covered by this node, i.e. the combined text of all token leafs of the subtree
|
||||||
/// originating in this node.
|
/// originating in this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text(&self) -> SyntaxText<'_, '_, dyn Resolver<TokenKey>, L, D> {
|
pub fn text(&self) -> SyntaxText<'_, '_, dyn Resolver<TokenKey>, S, D> {
|
||||||
SyntaxText::new(self, &**self.resolver())
|
SyntaxText::new(self, &**self.resolver())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> fmt::Debug for ResolvedNode<L, D> {
|
impl<S: Syntax, D> fmt::Debug for ResolvedNode<S, D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_debug(&**self.resolver(), f, f.alternate())
|
self.write_debug(&**self.resolver(), f, f.alternate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> fmt::Display for ResolvedNode<L, D> {
|
impl<S: Syntax, D> fmt::Display for ResolvedNode<S, D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_display(&**self.resolver(), f)
|
self.write_display(&**self.resolver(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedToken<L, D> {
|
impl<S: Syntax, D> ResolvedToken<S, D> {
|
||||||
/// Uses the resolver associated with this tree to return the source text of this token.
|
/// Uses the resolver associated with this tree to return the source text of this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text(&self) -> &str {
|
pub fn text(&self) -> &str {
|
||||||
|
@ -208,22 +208,22 @@ impl<L: Language, D> ResolvedToken<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> fmt::Debug for ResolvedToken<L, D> {
|
impl<S: Syntax, D> fmt::Debug for ResolvedToken<S, D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_debug(&**self.resolver(), f)
|
self.write_debug(&**self.resolver(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> fmt::Display for ResolvedToken<L, D> {
|
impl<S: Syntax, D> fmt::Display for ResolvedToken<S, D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.write_display(&**self.resolver(), f)
|
self.write_display(&**self.resolver(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
impl<L, D> ResolvedNode<L, D>
|
impl<S, D> ResolvedNode<S, D>
|
||||||
where
|
where
|
||||||
L: Language,
|
S: Syntax,
|
||||||
{
|
{
|
||||||
/// Return an anonymous object that can be used to serialize this node,
|
/// Return an anonymous object that can be used to serialize this node,
|
||||||
/// including the data for each node.
|
/// including the data for each node.
|
||||||
|
@ -267,7 +267,7 @@ macro_rules! forward_node {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedNode<L, D> {
|
impl<S: Syntax, D> ResolvedNode<S, D> {
|
||||||
/// Returns the [`Resolver`] associated with this tree.
|
/// Returns the [`Resolver`] associated with this tree.
|
||||||
pub fn resolver(&self) -> &StdArc<dyn Resolver<TokenKey>> {
|
pub fn resolver(&self) -> &StdArc<dyn Resolver<TokenKey>> {
|
||||||
self.syntax.resolver().unwrap()
|
self.syntax.resolver().unwrap()
|
||||||
|
@ -283,7 +283,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxNode`] vs [`ResolvedNode`].
|
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxNode`] vs [`ResolvedNode`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_resolved(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn try_resolved(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxNode`] vs [`ResolvedNode`].
|
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxNode`] vs [`ResolvedNode`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resolved(&self) -> &ResolvedNode<L, D> {
|
pub fn resolved(&self) -> &ResolvedNode<S, D> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If this node is the root, returns `self`.
|
/// If this node is the root, returns `self`.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn root(&self) -> &SyntaxNode<L, D> {
|
pub fn root(&self) -> &SyntaxNode<S, D> {
|
||||||
unsafe { Self::coerce_ref(self.syntax.root()) }
|
unsafe { Self::coerce_ref(self.syntax.root()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,7 +325,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
|
|
||||||
/// Returns an iterator over child elements of this node, including tokens.
|
/// Returns an iterator over child elements of this node, including tokens.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn children_with_tokens(&self) -> impl Iterator<Item = ResolvedElementRef<'_, L, D>> {
|
pub fn children_with_tokens(&self) -> impl Iterator<Item = ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.children_with_tokens())
|
forward_as_elem!(self.syntax.children_with_tokens())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,13 +333,13 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`first_child_or_token`](ResolvedNode::first_child_or_token).
|
/// If you want to also consider leafs, see [`first_child_or_token`](ResolvedNode::first_child_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_child(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn first_child(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.first_child())
|
forward!(self.syntax.first_child())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The first child element of this node, if any, including tokens.
|
/// The first child element of this node, if any, including tokens.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_child_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn first_child_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.first_child_or_token())
|
forward_as_elem!(self.syntax.first_child_or_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,13 +347,13 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`last_child_or_token`](ResolvedNode::last_child_or_token).
|
/// If you want to also consider leafs, see [`last_child_or_token`](ResolvedNode::last_child_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_child(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn last_child(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.last_child())
|
forward!(self.syntax.last_child())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The last child element of this node, if any, including tokens.
|
/// The last child element of this node, if any, including tokens.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_child_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn last_child_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.last_child_or_token())
|
forward_as_elem!(self.syntax.last_child_or_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,14 +363,14 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`next_child_or_token_after`](ResolvedNode::next_child_or_token_after).
|
/// If you want to also consider leafs, see [`next_child_or_token_after`](ResolvedNode::next_child_or_token_after).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_child_after(&self, n: usize, offset: TextSize) -> Option<&ResolvedNode<L, D>> {
|
pub fn next_child_after(&self, n: usize, offset: TextSize) -> Option<&ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.next_child_after(n, offset))
|
forward!(self.syntax.next_child_after(n, offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The first child element of this node starting at the (n + 1)-st, if any.
|
/// The first child element of this node starting at the (n + 1)-st, if any.
|
||||||
/// If this method returns `Some`, the contained node is the (n + 1)-st child of this node.
|
/// If this method returns `Some`, the contained node is the (n + 1)-st child of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_child_or_token_after(&self, n: usize, offset: TextSize) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn next_child_or_token_after(&self, n: usize, offset: TextSize) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.next_child_or_token_after(n, offset))
|
forward_as_elem!(self.syntax.next_child_or_token_after(n, offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,14 +381,14 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
/// If you want to also consider leafs, see
|
/// If you want to also consider leafs, see
|
||||||
/// [`prev_child_or_token_before`](ResolvedNode::prev_child_or_token_before).
|
/// [`prev_child_or_token_before`](ResolvedNode::prev_child_or_token_before).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_child_before(&self, n: usize, offset: TextSize) -> Option<&ResolvedNode<L, D>> {
|
pub fn prev_child_before(&self, n: usize, offset: TextSize) -> Option<&ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.prev_child_before(n, offset))
|
forward!(self.syntax.prev_child_before(n, offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The last child node of this node up to the nth, if any.
|
/// The last child node of this node up to the nth, if any.
|
||||||
/// If this method returns `Some`, the contained node is the (n - 1)-st child.
|
/// If this method returns `Some`, the contained node is the (n - 1)-st child.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_child_or_token_before(&self, n: usize, offset: TextSize) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn prev_child_or_token_before(&self, n: usize, offset: TextSize) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.prev_child_or_token_before(n, offset))
|
forward_as_elem!(self.syntax.prev_child_or_token_before(n, offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,13 +396,13 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`next_sibling_or_token`](ResolvedNode::next_sibling_or_token).
|
/// If you want to also consider leafs, see [`next_sibling_or_token`](ResolvedNode::next_sibling_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn next_sibling(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.next_sibling())
|
forward!(self.syntax.next_sibling())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this node's parent after this node.
|
/// The tree element to the right of this one, i.e. the next child of this node's parent after this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.next_sibling_or_token())
|
forward_as_elem!(self.syntax.next_sibling_or_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,25 +410,25 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`prev_sibling_or_token`](ResolvedNode::prev_sibling_or_token).
|
/// If you want to also consider leafs, see [`prev_sibling_or_token`](ResolvedNode::prev_sibling_or_token).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn prev_sibling(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.prev_sibling())
|
forward!(self.syntax.prev_sibling())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this node's parent before this node.
|
/// The tree element to the left of this one, i.e. the previous child of this node's parent before this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.prev_sibling_or_token())
|
forward_as_elem!(self.syntax.prev_sibling_or_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the leftmost token in the subtree of this node
|
/// Return the leftmost token in the subtree of this node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_token(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn first_token(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
forward_token!(self.syntax.first_token())
|
forward_token!(self.syntax.first_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the rightmost token in the subtree of this node
|
/// Return the rightmost token in the subtree of this node
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_token(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn last_token(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
forward_token!(self.syntax.last_token())
|
forward_token!(self.syntax.last_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,7 +438,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`siblings_with_tokens`](ResolvedNode::siblings_with_tokens).
|
/// If you want to also consider leafs, see [`siblings_with_tokens`](ResolvedNode::siblings_with_tokens).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &ResolvedNode<L, D>> {
|
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.siblings(direction))
|
forward!(self.syntax.siblings(direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +446,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
/// node's parent's children from this node on to the left or the right.
|
/// node's parent's children from this node on to the left or the right.
|
||||||
/// The first item in the iterator will always be this node.
|
/// The first item in the iterator will always be this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = ResolvedElementRef<'_, L, D>> {
|
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.siblings_with_tokens(direction))
|
forward_as_elem!(self.syntax.siblings_with_tokens(direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,20 +454,20 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
///
|
///
|
||||||
/// If you want to also consider leafs, see [`descendants_with_tokens`](ResolvedNode::descendants_with_tokens).
|
/// If you want to also consider leafs, see [`descendants_with_tokens`](ResolvedNode::descendants_with_tokens).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn descendants(&self) -> impl Iterator<Item = &ResolvedNode<L, D>> {
|
pub fn descendants(&self) -> impl Iterator<Item = &ResolvedNode<S, D>> {
|
||||||
forward!(self.syntax.descendants())
|
forward!(self.syntax.descendants())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over all elements in the subtree starting at this node, including this node.
|
/// Returns an iterator over all elements in the subtree starting at this node, including this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = ResolvedElementRef<'_, L, D>> {
|
pub fn descendants_with_tokens(&self) -> impl Iterator<Item = ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.descendants_with_tokens())
|
forward_as_elem!(self.syntax.descendants_with_tokens())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Traverse the subtree rooted at the current node (including the current
|
/// Traverse the subtree rooted at the current node (including the current
|
||||||
/// node) in preorder, excluding tokens.
|
/// node) in preorder, excluding tokens.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&ResolvedNode<L, D>>> {
|
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&ResolvedNode<S, D>>> {
|
||||||
self.syntax
|
self.syntax
|
||||||
.preorder()
|
.preorder()
|
||||||
.map(|event| event.map(|node| unsafe { Self::coerce_ref(node) }))
|
.map(|event| event.map(|node| unsafe { Self::coerce_ref(node) }))
|
||||||
|
@ -476,7 +476,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
/// Traverse the subtree rooted at the current node (including the current
|
/// Traverse the subtree rooted at the current node (including the current
|
||||||
/// node) in preorder, including tokens.
|
/// node) in preorder, including tokens.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<ResolvedElementRef<'_, L, D>>> {
|
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<ResolvedElementRef<'_, S, D>>> {
|
||||||
self.syntax
|
self.syntax
|
||||||
.preorder_with_tokens()
|
.preorder_with_tokens()
|
||||||
.map(|event| event.map(|elem| unsafe { ResolvedElementRef::coerce_ref(elem) }))
|
.map(|event| event.map(|elem| unsafe { ResolvedElementRef::coerce_ref(elem) }))
|
||||||
|
@ -484,7 +484,7 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
|
|
||||||
/// Find a token in the subtree corresponding to this node, which covers the offset.
|
/// Find a token in the subtree corresponding to this node, which covers the offset.
|
||||||
/// Precondition: offset must be withing node's range.
|
/// Precondition: offset must be withing node's range.
|
||||||
pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<ResolvedToken<L, D>> {
|
pub fn token_at_offset(&self, offset: TextSize) -> TokenAtOffset<ResolvedToken<S, D>> {
|
||||||
self.syntax
|
self.syntax
|
||||||
.token_at_offset(offset)
|
.token_at_offset(offset)
|
||||||
.map(|token| ResolvedToken { syntax: token })
|
.map(|token| ResolvedToken { syntax: token })
|
||||||
|
@ -494,12 +494,12 @@ impl<L: Language, D> ResolvedNode<L, D> {
|
||||||
/// contains the range. If the range is empty and is contained in two leaf
|
/// contains the range. If the range is empty and is contained in two leaf
|
||||||
/// nodes, either one can be returned. Precondition: range must be contained
|
/// nodes, either one can be returned. Precondition: range must be contained
|
||||||
/// withing the current node
|
/// withing the current node
|
||||||
pub fn covering_element(&self, range: TextRange) -> ResolvedElementRef<'_, L, D> {
|
pub fn covering_element(&self, range: TextRange) -> ResolvedElementRef<'_, S, D> {
|
||||||
unsafe { ResolvedElementRef::coerce_ref(self.syntax.covering_element(range)) }
|
unsafe { ResolvedElementRef::coerce_ref(self.syntax.covering_element(range)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedToken<L, D> {
|
impl<S: Syntax, D> ResolvedToken<S, D> {
|
||||||
/// Returns the [`Resolver`] associated with this tree.
|
/// Returns the [`Resolver`] associated with this tree.
|
||||||
pub fn resolver(&self) -> &StdArc<dyn Resolver<TokenKey>> {
|
pub fn resolver(&self) -> &StdArc<dyn Resolver<TokenKey>> {
|
||||||
self.syntax.resolver().unwrap()
|
self.syntax.resolver().unwrap()
|
||||||
|
@ -509,7 +509,7 @@ impl<L: Language, D> ResolvedToken<L, D> {
|
||||||
///
|
///
|
||||||
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxToken`] vs [`ResolvedToken`].
|
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxToken`] vs [`ResolvedToken`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_resolved(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn try_resolved(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,31 +517,31 @@ impl<L: Language, D> ResolvedToken<L, D> {
|
||||||
///
|
///
|
||||||
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxToken`] vs [`ResolvedToken`].
|
/// This method mostly exists to allow the convenience of being agnostic over [`SyntaxToken`] vs [`ResolvedToken`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resolved(&self) -> &ResolvedToken<L, D> {
|
pub fn resolved(&self) -> &ResolvedToken<S, D> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The parent node of this token.
|
/// The parent node of this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> &ResolvedNode<L, D> {
|
pub fn parent(&self) -> &ResolvedNode<S, D> {
|
||||||
unsafe { ResolvedNode::coerce_ref(self.syntax.parent()) }
|
unsafe { ResolvedNode::coerce_ref(self.syntax.parent()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this token.
|
/// Returns an iterator along the chain of parents of this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &ResolvedNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &ResolvedNode<S, D>> {
|
||||||
forward_node!(self.syntax.ancestors())
|
forward_node!(self.syntax.ancestors())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this token's parent after this token.
|
/// The tree element to the right of this one, i.e. the next child of this token's parent after this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.next_sibling_or_token())
|
forward_as_elem!(self.syntax.next_sibling_or_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this token's parent after this token.
|
/// The tree element to the left of this one, i.e. the previous child of this token's parent after this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.prev_sibling_or_token())
|
forward_as_elem!(self.syntax.prev_sibling_or_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,24 +549,24 @@ impl<L: Language, D> ResolvedToken<L, D> {
|
||||||
/// token's parent's children from this token on to the left or the right.
|
/// token's parent's children from this token on to the left or the right.
|
||||||
/// The first item in the iterator will always be this token.
|
/// The first item in the iterator will always be this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = ResolvedElementRef<'_, L, D>> {
|
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = ResolvedElementRef<'_, S, D>> {
|
||||||
forward_as_elem!(self.syntax.siblings_with_tokens(direction))
|
forward_as_elem!(self.syntax.siblings_with_tokens(direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the next token in the tree.
|
/// Returns the next token in the tree.
|
||||||
/// This is not necessary a direct sibling of this token, but will always be further right in the tree.
|
/// This is not necessary a direct sibling of this token, but will always be further right in the tree.
|
||||||
pub fn next_token(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn next_token(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
forward!(self.syntax.next_token())
|
forward!(self.syntax.next_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the previous token in the tree.
|
/// Returns the previous token in the tree.
|
||||||
/// This is not necessary a direct sibling of this token, but will always be further left in the tree.
|
/// This is not necessary a direct sibling of this token, but will always be further left in the tree.
|
||||||
pub fn prev_token(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn prev_token(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
forward!(self.syntax.prev_token())
|
forward!(self.syntax.prev_token())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> ResolvedElement<L, D> {
|
impl<S: Syntax, D> ResolvedElement<S, D> {
|
||||||
/// The range this element covers in the source text, in bytes.
|
/// The range this element covers in the source text, in bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text_range(&self) -> TextRange {
|
pub fn text_range(&self) -> TextRange {
|
||||||
|
@ -587,7 +587,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// The kind of this element in terms of your language.
|
/// The kind of this element in terms of your language.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> L::Kind {
|
pub fn kind(&self) -> S {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.kind(),
|
NodeOrToken::Node(it) => it.kind(),
|
||||||
NodeOrToken::Token(it) => it.kind(),
|
NodeOrToken::Token(it) => it.kind(),
|
||||||
|
@ -596,7 +596,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// The parent node of this element, except if this element is the root.
|
/// The parent node of this element, except if this element is the root.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> Option<&ResolvedNode<L, D>> {
|
pub fn parent(&self) -> Option<&ResolvedNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.parent(),
|
NodeOrToken::Node(it) => it.parent(),
|
||||||
NodeOrToken::Token(it) => Some(it.parent()),
|
NodeOrToken::Token(it) => Some(it.parent()),
|
||||||
|
@ -605,7 +605,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this node.
|
/// Returns an iterator along the chain of parents of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &ResolvedNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &ResolvedNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.ancestors(),
|
NodeOrToken::Node(it) => it.ancestors(),
|
||||||
NodeOrToken::Token(it) => it.parent().ancestors(),
|
NodeOrToken::Token(it) => it.parent().ancestors(),
|
||||||
|
@ -614,7 +614,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// Return the leftmost token in the subtree of this element.
|
/// Return the leftmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_token(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn first_token(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.first_token(),
|
NodeOrToken::Node(it) => it.first_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -623,7 +623,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// Return the rightmost token in the subtree of this element.
|
/// Return the rightmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_token(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn last_token(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.last_token(),
|
NodeOrToken::Node(it) => it.last_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -632,7 +632,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
||||||
|
@ -641,7 +641,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'_, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
||||||
|
@ -649,7 +649,7 @@ impl<L: Language, D> ResolvedElement<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
impl<'a, S: Syntax, D> ResolvedElementRef<'a, S, D> {
|
||||||
/// The range this element covers in the source text, in bytes.
|
/// The range this element covers in the source text, in bytes.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn text_range(&self) -> TextRange {
|
pub fn text_range(&self) -> TextRange {
|
||||||
|
@ -670,7 +670,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The kind of this element in terms of your language.
|
/// The kind of this element in terms of your language.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> L::Kind {
|
pub fn kind(&self) -> S {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.kind(),
|
NodeOrToken::Node(it) => it.kind(),
|
||||||
NodeOrToken::Token(it) => it.kind(),
|
NodeOrToken::Token(it) => it.kind(),
|
||||||
|
@ -679,7 +679,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The parent node of this element, except if this element is the root.
|
/// The parent node of this element, except if this element is the root.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> Option<&'a ResolvedNode<L, D>> {
|
pub fn parent(&self) -> Option<&'a ResolvedNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.parent(),
|
NodeOrToken::Node(it) => it.parent(),
|
||||||
NodeOrToken::Token(it) => Some(it.parent()),
|
NodeOrToken::Token(it) => Some(it.parent()),
|
||||||
|
@ -688,7 +688,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this node.
|
/// Returns an iterator along the chain of parents of this node.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &'a ResolvedNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &'a ResolvedNode<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.ancestors(),
|
NodeOrToken::Node(it) => it.ancestors(),
|
||||||
NodeOrToken::Token(it) => it.parent().ancestors(),
|
NodeOrToken::Token(it) => it.parent().ancestors(),
|
||||||
|
@ -697,7 +697,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// Return the leftmost token in the subtree of this element.
|
/// Return the leftmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn first_token(&self) -> Option<&'a ResolvedToken<L, D>> {
|
pub fn first_token(&self) -> Option<&'a ResolvedToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.first_token(),
|
NodeOrToken::Node(it) => it.first_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -706,7 +706,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// Return the rightmost token in the subtree of this element.
|
/// Return the rightmost token in the subtree of this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn last_token(&self) -> Option<&'a ResolvedToken<L, D>> {
|
pub fn last_token(&self) -> Option<&'a ResolvedToken<S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.last_token(),
|
NodeOrToken::Node(it) => it.last_token(),
|
||||||
NodeOrToken::Token(it) => Some(it),
|
NodeOrToken::Token(it) => Some(it),
|
||||||
|
@ -715,7 +715,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
/// The tree element to the right of this one, i.e. the next child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'a, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<ResolvedElementRef<'a, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
NodeOrToken::Node(it) => it.next_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
NodeOrToken::Token(it) => it.next_sibling_or_token(),
|
||||||
|
@ -724,7 +724,7 @@ impl<'a, L: Language, D> ResolvedElementRef<'a, L, D> {
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
/// The tree element to the left of this one, i.e. the previous child of this element's parent after this element.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'a, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<ResolvedElementRef<'a, S, D>> {
|
||||||
match self {
|
match self {
|
||||||
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Node(it) => it.prev_sibling_or_token(),
|
||||||
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
NodeOrToken::Token(it) => it.prev_sibling_or_token(),
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
interning::{Resolver, TokenKey},
|
interning::{Resolver, TokenKey},
|
||||||
syntax::{SyntaxNode, SyntaxToken},
|
syntax::{SyntaxNode, SyntaxToken},
|
||||||
text::{TextRange, TextSize},
|
text::{TextRange, TextSize},
|
||||||
Language,
|
Syntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An efficient representation of the text that is covered by a [`SyntaxNode`], i.e. the combined
|
/// An efficient representation of the text that is covered by a [`SyntaxNode`], i.e. the combined
|
||||||
|
@ -21,9 +21,9 @@ use crate::{
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::syntax::ResolvedNode;
|
/// # use cstree::syntax::ResolvedNode;
|
||||||
/// #
|
/// #
|
||||||
/// fn parse_float_literal(s: &str) -> ResolvedNode<MyLanguage> {
|
/// fn parse_float_literal(s: &str) -> ResolvedNode<MySyntax> {
|
||||||
/// // parsing...
|
/// // parsing...
|
||||||
/// # let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// # let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// # builder.start_node(Float);
|
/// # builder.start_node(Float);
|
||||||
/// # builder.token(Float, s);
|
/// # builder.token(Float, s);
|
||||||
/// # builder.finish_node();
|
/// # builder.finish_node();
|
||||||
|
@ -41,14 +41,14 @@ use crate::{
|
||||||
/// assert_eq!(sub, "748");
|
/// assert_eq!(sub, "748");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SyntaxText<'n, 'i, I: ?Sized, L: Language, D: 'static = ()> {
|
pub struct SyntaxText<'n, 'i, I: ?Sized, S: Syntax, D: 'static = ()> {
|
||||||
node: &'n SyntaxNode<L, D>,
|
node: &'n SyntaxNode<S, D>,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
resolver: &'i I,
|
resolver: &'i I,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n, 'i, I: Resolver<TokenKey> + ?Sized, L: Language, D> SyntaxText<'n, 'i, I, L, D> {
|
impl<'n, 'i, I: Resolver<TokenKey> + ?Sized, S: Syntax, D> SyntaxText<'n, 'i, I, S, D> {
|
||||||
pub(crate) fn new(node: &'n SyntaxNode<L, D>, resolver: &'i I) -> Self {
|
pub(crate) fn new(node: &'n SyntaxNode<S, D>, resolver: &'i I) -> Self {
|
||||||
let range = node.text_range();
|
let range = node.text_range();
|
||||||
SyntaxText { node, range, resolver }
|
SyntaxText { node, range, resolver }
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ impl<'n, 'i, I: Resolver<TokenKey> + ?Sized, L: Language, D> SyntaxText<'n, 'i,
|
||||||
self.fold_chunks((), |(), chunk| f(chunk))
|
self.fold_chunks((), |(), chunk| f(chunk))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tokens_with_ranges(&self) -> impl Iterator<Item = (&SyntaxToken<L, D>, TextRange)> {
|
fn tokens_with_ranges(&self) -> impl Iterator<Item = (&SyntaxToken<S, D>, TextRange)> {
|
||||||
let text_range = self.range;
|
let text_range = self.range;
|
||||||
self.node
|
self.node
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
|
@ -208,25 +208,25 @@ fn found<T>(res: Result<(), T>) -> Option<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> fmt::Debug for SyntaxText<'_, '_, I, L, D> {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> fmt::Debug for SyntaxText<'_, '_, I, S, D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.to_string(), f)
|
fmt::Debug::fmt(&self.to_string(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> fmt::Display for SyntaxText<'_, '_, I, L, D> {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> fmt::Display for SyntaxText<'_, '_, I, S, D> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f))
|
self.try_for_each_chunk(|chunk| fmt::Display::fmt(chunk, f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> From<SyntaxText<'_, '_, I, L, D>> for String {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> From<SyntaxText<'_, '_, I, S, D>> for String {
|
||||||
fn from(text: SyntaxText<'_, '_, I, L, D>) -> String {
|
fn from(text: SyntaxText<'_, '_, I, S, D>) -> String {
|
||||||
text.to_string()
|
text.to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> PartialEq<str> for SyntaxText<'_, '_, I, L, D> {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> PartialEq<str> for SyntaxText<'_, '_, I, S, D> {
|
||||||
fn eq(&self, mut rhs: &str) -> bool {
|
fn eq(&self, mut rhs: &str) -> bool {
|
||||||
self.try_for_each_chunk(|chunk| {
|
self.try_for_each_chunk(|chunk| {
|
||||||
if !rhs.starts_with(chunk) {
|
if !rhs.starts_with(chunk) {
|
||||||
|
@ -240,33 +240,33 @@ impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> PartialEq<str> for SyntaxTe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> PartialEq<SyntaxText<'_, '_, I, L, D>> for str {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> PartialEq<SyntaxText<'_, '_, I, S, D>> for str {
|
||||||
fn eq(&self, rhs: &SyntaxText<'_, '_, I, L, D>) -> bool {
|
fn eq(&self, rhs: &SyntaxText<'_, '_, I, S, D>) -> bool {
|
||||||
rhs == self
|
rhs == self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> PartialEq<&'_ str> for SyntaxText<'_, '_, I, L, D> {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> PartialEq<&'_ str> for SyntaxText<'_, '_, I, S, D> {
|
||||||
fn eq(&self, rhs: &&str) -> bool {
|
fn eq(&self, rhs: &&str) -> bool {
|
||||||
self == *rhs
|
self == *rhs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> PartialEq<SyntaxText<'_, '_, I, L, D>> for &'_ str {
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> PartialEq<SyntaxText<'_, '_, I, S, D>> for &'_ str {
|
||||||
fn eq(&self, rhs: &SyntaxText<'_, '_, I, L, D>) -> bool {
|
fn eq(&self, rhs: &SyntaxText<'_, '_, I, S, D>) -> bool {
|
||||||
rhs == self
|
rhs == self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'n1, 'i1, 'n2, 'i2, I1, I2, L1, L2, D1, D2> PartialEq<SyntaxText<'n2, 'i2, I2, L2, D2>>
|
impl<'n1, 'i1, 'n2, 'i2, I1, I2, S1, S2, D1, D2> PartialEq<SyntaxText<'n2, 'i2, I2, S2, D2>>
|
||||||
for SyntaxText<'n1, 'i1, I1, L1, D1>
|
for SyntaxText<'n1, 'i1, I1, S1, D1>
|
||||||
where
|
where
|
||||||
L1: Language,
|
S1: Syntax,
|
||||||
L2: Language,
|
S2: Syntax,
|
||||||
I1: Resolver<TokenKey> + ?Sized,
|
I1: Resolver<TokenKey> + ?Sized,
|
||||||
I2: Resolver<TokenKey> + ?Sized,
|
I2: Resolver<TokenKey> + ?Sized,
|
||||||
{
|
{
|
||||||
fn eq(&self, other: &SyntaxText<'_, '_, I2, L2, D2>) -> bool {
|
fn eq(&self, other: &SyntaxText<'_, '_, I2, S2, D2>) -> bool {
|
||||||
if self.range.len() != other.range.len() {
|
if self.range.len() != other.range.len() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -278,21 +278,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zip_texts<'it1, 'it2, It1, It2, I1, I2, L1, L2, D1, D2>(
|
fn zip_texts<'it1, 'it2, It1, It2, I1, I2, S1, S2, D1, D2>(
|
||||||
xs: &mut It1,
|
xs: &mut It1,
|
||||||
ys: &mut It2,
|
ys: &mut It2,
|
||||||
resolver_x: &I1,
|
resolver_x: &I1,
|
||||||
resolver_y: &I2,
|
resolver_y: &I2,
|
||||||
) -> Option<()>
|
) -> Option<()>
|
||||||
where
|
where
|
||||||
It1: Iterator<Item = (&'it1 SyntaxToken<L1, D1>, TextRange)>,
|
It1: Iterator<Item = (&'it1 SyntaxToken<S1, D1>, TextRange)>,
|
||||||
It2: Iterator<Item = (&'it2 SyntaxToken<L2, D2>, TextRange)>,
|
It2: Iterator<Item = (&'it2 SyntaxToken<S2, D2>, TextRange)>,
|
||||||
I1: Resolver<TokenKey> + ?Sized,
|
I1: Resolver<TokenKey> + ?Sized,
|
||||||
I2: Resolver<TokenKey> + ?Sized,
|
I2: Resolver<TokenKey> + ?Sized,
|
||||||
D1: 'static,
|
D1: 'static,
|
||||||
D2: 'static,
|
D2: 'static,
|
||||||
L1: Language + 'it1,
|
S1: Syntax + 'it1,
|
||||||
L2: Language + 'it2,
|
S2: Syntax + 'it2,
|
||||||
{
|
{
|
||||||
let mut x = xs.next()?;
|
let mut x = xs.next()?;
|
||||||
let mut y = ys.next()?;
|
let mut y = ys.next()?;
|
||||||
|
@ -314,7 +314,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I: Resolver<TokenKey> + ?Sized, L: Language, D> Eq for SyntaxText<'_, '_, I, L, D> {}
|
impl<I: Resolver<TokenKey> + ?Sized, S: Syntax, D> Eq for SyntaxText<'_, '_, I, S, D> {}
|
||||||
|
|
||||||
mod private {
|
mod private {
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
@ -383,40 +383,38 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum TestLang {}
|
#[repr(transparent)]
|
||||||
impl Language for TestLang {
|
pub struct SyntaxKind(u32);
|
||||||
type Kind = RawSyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
impl Syntax for SyntaxKind {
|
||||||
raw
|
fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
|
Self(raw.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
fn into_raw(self) -> RawSyntaxKind {
|
||||||
kind
|
RawSyntaxKind(self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn static_text(kind: Self::Kind) -> Option<&'static str> {
|
fn static_text(self) -> Option<&'static str> {
|
||||||
if kind == RawSyntaxKind(1) {
|
match self.0 {
|
||||||
Some("{")
|
1 => Some("{"),
|
||||||
} else if kind == RawSyntaxKind(2) {
|
2 => Some("}"),
|
||||||
Some("}")
|
_ => None,
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tree(chunks: &[&str]) -> (SyntaxNode<TestLang, ()>, impl Resolver<TokenKey>) {
|
fn build_tree(chunks: &[&str]) -> (SyntaxNode<SyntaxKind, ()>, impl Resolver<TokenKey>) {
|
||||||
let mut builder: GreenNodeBuilder<TestLang> = GreenNodeBuilder::new();
|
let mut builder: GreenNodeBuilder<SyntaxKind> = GreenNodeBuilder::new();
|
||||||
builder.start_node(RawSyntaxKind(62));
|
builder.start_node(SyntaxKind(62));
|
||||||
for &chunk in chunks.iter() {
|
for &chunk in chunks.iter() {
|
||||||
let kind = match chunk {
|
let kind = match chunk {
|
||||||
"{" => 1,
|
"{" => 1,
|
||||||
"}" => 2,
|
"}" => 2,
|
||||||
_ => 3,
|
_ => 3,
|
||||||
};
|
};
|
||||||
builder.token(RawSyntaxKind(kind), chunk);
|
builder.token(SyntaxKind(kind), chunk);
|
||||||
}
|
}
|
||||||
builder.finish_node();
|
builder.finish_node();
|
||||||
let (node, cache) = builder.finish();
|
let (node, cache) = builder.finish();
|
|
@ -12,18 +12,18 @@ use crate::{
|
||||||
green::{GreenNode, GreenToken},
|
green::{GreenNode, GreenToken},
|
||||||
interning::{Resolver, TokenKey},
|
interning::{Resolver, TokenKey},
|
||||||
traversal::Direction,
|
traversal::Direction,
|
||||||
Language, RawSyntaxKind,
|
RawSyntaxKind, Syntax,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Syntax tree token.
|
/// Syntax tree token.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SyntaxToken<L: Language, D: 'static = ()> {
|
pub struct SyntaxToken<S: Syntax, D: 'static = ()> {
|
||||||
parent: SyntaxNode<L, D>,
|
parent: SyntaxNode<S, D>,
|
||||||
index: u32,
|
index: u32,
|
||||||
offset: TextSize,
|
offset: TextSize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Clone for SyntaxToken<L, D> {
|
impl<S: Syntax, D> Clone for SyntaxToken<S, D> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
parent: self.parent.clone(),
|
parent: self.parent.clone(),
|
||||||
|
@ -33,7 +33,7 @@ impl<L: Language, D> Clone for SyntaxToken<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Hash for SyntaxToken<L, D> {
|
impl<S: Syntax, D> Hash for SyntaxToken<S, D> {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.parent.hash(state);
|
self.parent.hash(state);
|
||||||
self.index.hash(state);
|
self.index.hash(state);
|
||||||
|
@ -41,15 +41,15 @@ impl<L: Language, D> Hash for SyntaxToken<L, D> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> PartialEq for SyntaxToken<L, D> {
|
impl<S: Syntax, D> PartialEq for SyntaxToken<S, D> {
|
||||||
fn eq(&self, other: &SyntaxToken<L, D>) -> bool {
|
fn eq(&self, other: &SyntaxToken<S, D>) -> bool {
|
||||||
self.parent == other.parent && self.index == other.index && self.offset == other.offset
|
self.parent == other.parent && self.index == other.index && self.offset == other.offset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> Eq for SyntaxToken<L, D> {}
|
impl<S: Syntax, D> Eq for SyntaxToken<S, D> {}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxToken<L, D> {
|
impl<S: Syntax, D> SyntaxToken<S, D> {
|
||||||
/// Writes this token's [`Debug`](fmt::Debug) representation into the given `target`.
|
/// 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
|
pub fn write_debug<R>(&self, resolver: &R, target: &mut impl fmt::Write) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
@ -113,7 +113,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// Turns this token into a [`ResolvedToken`](crate::syntax::ResolvedToken), but only if there is a resolver
|
/// Turns this token into a [`ResolvedToken`](crate::syntax::ResolvedToken), but only if there is a resolver
|
||||||
/// associated with this tree.
|
/// associated with this tree.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn try_resolved(&self) -> Option<&ResolvedToken<L, D>> {
|
pub fn try_resolved(&self) -> Option<&ResolvedToken<S, D>> {
|
||||||
// safety: we only coerce if `resolver` exists
|
// safety: we only coerce if `resolver` exists
|
||||||
self.resolver().map(|_| unsafe { ResolvedToken::coerce_ref(self) })
|
self.resolver().map(|_| unsafe { ResolvedToken::coerce_ref(self) })
|
||||||
}
|
}
|
||||||
|
@ -122,13 +122,13 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// If there is no resolver associated with this tree.
|
/// If there is no resolver associated with this tree.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resolved(&self) -> &ResolvedToken<L, D> {
|
pub fn resolved(&self) -> &ResolvedToken<S, D> {
|
||||||
self.try_resolved().expect("tried to resolve a node without resolver")
|
self.try_resolved().expect("tried to resolve a node without resolver")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language, D> SyntaxToken<L, D> {
|
impl<S: Syntax, D> SyntaxToken<S, D> {
|
||||||
pub(super) fn new(parent: &SyntaxNode<L, D>, index: u32, offset: TextSize) -> SyntaxToken<L, D> {
|
pub(super) fn new(parent: &SyntaxNode<S, D>, index: u32, offset: TextSize) -> SyntaxToken<S, D> {
|
||||||
Self {
|
Self {
|
||||||
parent: parent.clone_uncounted(),
|
parent: parent.clone_uncounted(),
|
||||||
index,
|
index,
|
||||||
|
@ -164,8 +164,8 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
|
|
||||||
/// The kind of this token in terms of your language.
|
/// The kind of this token in terms of your language.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> L::Kind {
|
pub fn kind(&self) -> S {
|
||||||
L::kind_from_raw(self.syntax_kind())
|
S::from_raw(self.syntax_kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The range this token covers in the source text, in bytes.
|
/// The range this token covers in the source text, in bytes.
|
||||||
|
@ -187,17 +187,17 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
self.static_text().or_else(|| self.green().text(resolver)).unwrap()
|
self.static_text().or_else(|| self.green().text(resolver)).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the [syntax kind](Language::Kind) of this token always represents the same text, returns
|
/// If the [syntax kind](Syntax) of this token always represents the same text, returns
|
||||||
/// that text.
|
/// that text.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// If there is a syntax kind `Plus` that represents just the `+` operator and we implement
|
/// If there is a syntax kind `Plus` that represents just the `+` operator and we implement
|
||||||
/// [`Language::static_text`] for it, we can retrieve this text in the resulting syntax tree.
|
/// [`Syntax::static_text`] for it, we can retrieve this text in the resulting syntax tree.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// # use cstree::build::*;
|
/// # use cstree::build::*;
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// # builder.start_node(Root);
|
/// # builder.start_node(Root);
|
||||||
/// # builder.token(Identifier, "x");
|
/// # builder.token(Identifier, "x");
|
||||||
/// # builder.token(Whitespace, " ");
|
/// # builder.token(Whitespace, " ");
|
||||||
|
@ -206,7 +206,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// # builder.token(Int, "3");
|
/// # builder.token(Int, "3");
|
||||||
/// # builder.finish_node();
|
/// # builder.finish_node();
|
||||||
/// let tree = parse(&mut builder, "x + 3");
|
/// let tree = parse(&mut builder, "x + 3");
|
||||||
/// # let tree: SyntaxNode<MyLanguage> = SyntaxNode::new_root(builder.finish().0);
|
/// # let tree: SyntaxNode<MySyntax> = SyntaxNode::new_root(builder.finish().0);
|
||||||
/// let plus = tree
|
/// let plus = tree
|
||||||
/// .children_with_tokens()
|
/// .children_with_tokens()
|
||||||
/// .nth(2) // `x`, then a space, then `+`
|
/// .nth(2) // `x`, then a space, then `+`
|
||||||
|
@ -217,7 +217,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn static_text(&self) -> Option<&'static str> {
|
pub fn static_text(&self) -> Option<&'static str> {
|
||||||
L::static_text(self.kind())
|
S::static_text(self.kind())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` if `self` and `other` represent equal source text.
|
/// Returns `true` if `self` and `other` represent equal source text.
|
||||||
|
@ -235,7 +235,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// # use cstree::testing::*;
|
/// # use cstree::testing::*;
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage> = GreenNodeBuilder::new();
|
/// let mut builder: GreenNodeBuilder<MySyntax> = GreenNodeBuilder::new();
|
||||||
/// # builder.start_node(Root);
|
/// # builder.start_node(Root);
|
||||||
/// # builder.token(Identifier, "x");
|
/// # builder.token(Identifier, "x");
|
||||||
/// # builder.token(Whitespace, " ");
|
/// # builder.token(Whitespace, " ");
|
||||||
|
@ -247,7 +247,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// # builder.token(Int, "3");
|
/// # builder.token(Int, "3");
|
||||||
/// # builder.finish_node();
|
/// # builder.finish_node();
|
||||||
/// let tree = parse(&mut builder, "x + x + 3");
|
/// let tree = parse(&mut builder, "x + x + 3");
|
||||||
/// # let tree: SyntaxNode<MyLanguage> = SyntaxNode::new_root(builder.finish().0);
|
/// # let tree: SyntaxNode<MySyntax> = SyntaxNode::new_root(builder.finish().0);
|
||||||
/// let mut tokens = tree.children_with_tokens();
|
/// let mut tokens = tree.children_with_tokens();
|
||||||
/// let tokens = tokens.by_ref();
|
/// let tokens = tokens.by_ref();
|
||||||
/// let first_x = tokens.next().unwrap().into_token().unwrap();
|
/// let first_x = tokens.next().unwrap().into_token().unwrap();
|
||||||
|
@ -303,14 +303,14 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// interner,
|
/// interner,
|
||||||
/// type_table: TypeTable{ /* stuff */},
|
/// type_table: TypeTable{ /* stuff */},
|
||||||
/// };
|
/// };
|
||||||
/// let mut builder: GreenNodeBuilder<MyLanguage, TokenInterner> =
|
/// let mut builder: GreenNodeBuilder<MySyntax, TokenInterner> =
|
||||||
/// GreenNodeBuilder::with_interner(&mut state.interner);
|
/// GreenNodeBuilder::with_interner(&mut state.interner);
|
||||||
/// # let input = "";
|
/// # let input = "";
|
||||||
/// # builder.start_node(Root);
|
/// # builder.start_node(Root);
|
||||||
/// # builder.token(Identifier, "x");
|
/// # builder.token(Identifier, "x");
|
||||||
/// # builder.finish_node();
|
/// # builder.finish_node();
|
||||||
/// let tree = parse(&mut builder, "x");
|
/// let tree = parse(&mut builder, "x");
|
||||||
/// # let tree: SyntaxNode<MyLanguage> = SyntaxNode::new_root(builder.finish().0);
|
/// # let tree: SyntaxNode<MySyntax> = SyntaxNode::new_root(builder.finish().0);
|
||||||
/// let type_table = &state.type_table;
|
/// let type_table = &state.type_table;
|
||||||
/// let ident = tree
|
/// let ident = tree
|
||||||
/// .children_with_tokens()
|
/// .children_with_tokens()
|
||||||
|
@ -339,26 +339,26 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
|
|
||||||
/// The parent node of this token.
|
/// The parent node of this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent(&self) -> &SyntaxNode<L, D> {
|
pub fn parent(&self) -> &SyntaxNode<S, D> {
|
||||||
&self.parent
|
&self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator along the chain of parents of this token.
|
/// Returns an iterator along the chain of parents of this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<L, D>> {
|
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode<S, D>> {
|
||||||
self.parent().ancestors()
|
self.parent().ancestors()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tree element to the right of this one, i.e. the next child of this token's parent after this token.
|
/// The tree element to the right of this one, i.e. the next child of this token's parent after this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
self.parent()
|
self.parent()
|
||||||
.next_child_or_token_after(self.index as usize, self.text_range().end())
|
.next_child_or_token_after(self.index as usize, self.text_range().end())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The tree element to the left of this one, i.e. the previous child of this token's parent after this token.
|
/// The tree element to the left of this one, i.e. the previous child of this token's parent after this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, L, D>> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElementRef<'_, S, D>> {
|
||||||
self.parent()
|
self.parent()
|
||||||
.prev_child_or_token_before(self.index as usize, self.text_range().start())
|
.prev_child_or_token_before(self.index as usize, self.text_range().start())
|
||||||
}
|
}
|
||||||
|
@ -367,8 +367,8 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// token's parent's children from this token on to the left or the right.
|
/// token's parent's children from this token on to the left or the right.
|
||||||
/// The first item in the iterator will always be this token.
|
/// The first item in the iterator will always be this token.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElementRef<'_, L, D>> {
|
pub fn siblings_with_tokens(&self, direction: Direction) -> impl Iterator<Item = SyntaxElementRef<'_, S, D>> {
|
||||||
let me: SyntaxElementRef<'_, L, D> = self.into();
|
let me: SyntaxElementRef<'_, S, D> = self.into();
|
||||||
iter::successors(Some(me), move |el| match direction {
|
iter::successors(Some(me), move |el| match direction {
|
||||||
Direction::Next => el.next_sibling_or_token(),
|
Direction::Next => el.next_sibling_or_token(),
|
||||||
Direction::Prev => el.prev_sibling_or_token(),
|
Direction::Prev => el.prev_sibling_or_token(),
|
||||||
|
@ -378,7 +378,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// Returns the next token in the tree.
|
/// Returns the next token in the tree.
|
||||||
/// This is not necessary a direct sibling of this token, but will always be further right in the tree.
|
/// This is not necessary a direct sibling of this token, but will always be further right in the tree.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn next_token(&self) -> Option<&SyntaxToken<L, D>> {
|
pub fn next_token(&self) -> Option<&SyntaxToken<S, D>> {
|
||||||
match self.next_sibling_or_token() {
|
match self.next_sibling_or_token() {
|
||||||
Some(element) => element.first_token(),
|
Some(element) => element.first_token(),
|
||||||
None => self
|
None => self
|
||||||
|
@ -392,7 +392,7 @@ impl<L: Language, D> SyntaxToken<L, D> {
|
||||||
/// Returns the previous token in the tree.
|
/// Returns the previous token in the tree.
|
||||||
/// This is not necessary a direct sibling of this token, but will always be further left in the tree.
|
/// This is not necessary a direct sibling of this token, but will always be further left in the tree.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn prev_token(&self) -> Option<&SyntaxToken<L, D>> {
|
pub fn prev_token(&self) -> Option<&SyntaxToken<S, D>> {
|
||||||
match self.prev_sibling_or_token() {
|
match self.prev_sibling_or_token() {
|
||||||
Some(element) => element.last_token(),
|
Some(element) => element.last_token(),
|
||||||
None => self
|
None => self
|
|
@ -7,7 +7,7 @@ use cstree::{
|
||||||
};
|
};
|
||||||
|
|
||||||
fn build_tree<D>(root: &Element<'_>) -> (SyntaxNode<D>, impl Resolver) {
|
fn build_tree<D>(root: &Element<'_>) -> (SyntaxNode<D>, impl Resolver) {
|
||||||
let mut builder: GreenNodeBuilder<TestLang> = GreenNodeBuilder::new();
|
let mut builder: GreenNodeBuilder<SyntaxKind> = GreenNodeBuilder::new();
|
||||||
build_recursive(root, &mut builder, 0);
|
build_recursive(root, &mut builder, 0);
|
||||||
let (node, cache) = builder.finish();
|
let (node, cache) = builder.finish();
|
||||||
(SyntaxNode::new_root(node), cache.unwrap().into_interner().unwrap())
|
(SyntaxNode::new_root(node), cache.unwrap().into_interner().unwrap())
|
||||||
|
@ -36,19 +36,19 @@ fn create() {
|
||||||
let tree = two_level_tree();
|
let tree = two_level_tree();
|
||||||
let (tree, resolver) = build_tree::<()>(&tree);
|
let (tree, resolver) = build_tree::<()>(&tree);
|
||||||
assert_eq!(tree.syntax_kind(), RawSyntaxKind(0));
|
assert_eq!(tree.syntax_kind(), RawSyntaxKind(0));
|
||||||
assert_eq!(tree.kind(), RawSyntaxKind(0));
|
assert_eq!(tree.kind(), SyntaxKind(0));
|
||||||
{
|
{
|
||||||
let leaf1_0 = tree.children().nth(1).unwrap().children_with_tokens().next().unwrap();
|
let leaf1_0 = tree.children().nth(1).unwrap().children_with_tokens().next().unwrap();
|
||||||
let leaf1_0 = leaf1_0.into_token().unwrap();
|
let leaf1_0 = leaf1_0.into_token().unwrap();
|
||||||
assert_eq!(leaf1_0.syntax_kind(), RawSyntaxKind(5));
|
assert_eq!(leaf1_0.syntax_kind(), RawSyntaxKind(5));
|
||||||
assert_eq!(leaf1_0.kind(), RawSyntaxKind(5));
|
assert_eq!(leaf1_0.kind(), SyntaxKind(5));
|
||||||
assert_eq!(leaf1_0.resolve_text(&resolver), "1.0");
|
assert_eq!(leaf1_0.resolve_text(&resolver), "1.0");
|
||||||
assert_eq!(leaf1_0.text_range(), TextRange::at(6.into(), 3.into()));
|
assert_eq!(leaf1_0.text_range(), TextRange::at(6.into(), 3.into()));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let node2 = tree.children().nth(2).unwrap();
|
let node2 = tree.children().nth(2).unwrap();
|
||||||
assert_eq!(node2.syntax_kind(), RawSyntaxKind(6));
|
assert_eq!(node2.syntax_kind(), RawSyntaxKind(6));
|
||||||
assert_eq!(node2.kind(), RawSyntaxKind(6));
|
assert_eq!(node2.kind(), SyntaxKind(6));
|
||||||
assert_eq!(node2.children_with_tokens().count(), 3);
|
assert_eq!(node2.children_with_tokens().count(), 3);
|
||||||
assert_eq!(node2.resolve_text(&resolver), "2.02.12.2");
|
assert_eq!(node2.resolve_text(&resolver), "2.02.12.2");
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ fn create() {
|
||||||
fn token_text_eq() {
|
fn token_text_eq() {
|
||||||
let tree = tree_with_eq_tokens();
|
let tree = tree_with_eq_tokens();
|
||||||
let (tree, _) = build_tree::<()>(&tree);
|
let (tree, _) = build_tree::<()>(&tree);
|
||||||
assert_eq!(tree.kind(), RawSyntaxKind(0));
|
assert_eq!(tree.kind(), SyntaxKind(0));
|
||||||
|
|
||||||
let leaf0_0 = tree.children().next().unwrap().children_with_tokens().next().unwrap();
|
let leaf0_0 = tree.children().next().unwrap().children_with_tokens().next().unwrap();
|
||||||
let leaf0_0 = leaf0_0.into_token().unwrap();
|
let leaf0_0 = leaf0_0.into_token().unwrap();
|
||||||
|
@ -150,7 +150,7 @@ fn inline_resolver() {
|
||||||
assert_eq!(leaf1_0.text(), "1.0");
|
assert_eq!(leaf1_0.text(), "1.0");
|
||||||
assert_eq!(leaf1_0.text_range(), TextRange::at(6.into(), 3.into()));
|
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), leaf1_0.text());
|
||||||
assert_eq!(format!("{:?}", leaf1_0), "RawSyntaxKind(5)@6..9 \"1.0\"");
|
assert_eq!(format!("{:?}", leaf1_0), "SyntaxKind(5)@6..9 \"1.0\"");
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let node2 = tree.children().nth(2).unwrap();
|
let node2 = tree.children().nth(2).unwrap();
|
||||||
|
@ -158,13 +158,13 @@ fn inline_resolver() {
|
||||||
let resolver = node2.resolver();
|
let resolver = node2.resolver();
|
||||||
assert_eq!(node2.resolve_text(resolver.as_ref()), node2.text());
|
assert_eq!(node2.resolve_text(resolver.as_ref()), node2.text());
|
||||||
assert_eq!(format!("{}", node2).as_str(), node2.text());
|
assert_eq!(format!("{}", node2).as_str(), node2.text());
|
||||||
assert_eq!(format!("{:?}", node2), "RawSyntaxKind(6)@9..18");
|
assert_eq!(format!("{:?}", node2), "SyntaxKind(6)@9..18");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:#?}", node2),
|
format!("{:#?}", node2),
|
||||||
r#"RawSyntaxKind(6)@9..18
|
r#"SyntaxKind(6)@9..18
|
||||||
RawSyntaxKind(7)@9..12 "2.0"
|
SyntaxKind(7)@9..12 "2.0"
|
||||||
RawSyntaxKind(8)@12..15 "2.1"
|
SyntaxKind(8)@12..15 "2.1"
|
||||||
RawSyntaxKind(9)@15..18 "2.2"
|
SyntaxKind(9)@15..18 "2.2"
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -182,5 +182,5 @@ fn assert_debug_display() {
|
||||||
f::<cstree::util::NodeOrToken<String, u128>>();
|
f::<cstree::util::NodeOrToken<String, u128>>();
|
||||||
|
|
||||||
fn dbg<T: fmt::Debug>() {}
|
fn dbg<T: fmt::Debug>() {}
|
||||||
dbg::<GreenNodeBuilder<'static, 'static, TestLang>>();
|
dbg::<GreenNodeBuilder<'static, 'static, SyntaxKind>>();
|
||||||
}
|
}
|
80
cstree/tests/it/main.rs
Normal file
80
cstree/tests/it/main.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
mod basic;
|
||||||
|
mod regressions;
|
||||||
|
mod sendsync;
|
||||||
|
#[cfg(feature = "serialize")]
|
||||||
|
mod serde;
|
||||||
|
|
||||||
|
use cstree::{
|
||||||
|
build::{GreenNodeBuilder, NodeCache},
|
||||||
|
green::GreenNode,
|
||||||
|
interning::Interner,
|
||||||
|
RawSyntaxKind, Syntax,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type SyntaxNode<D = ()> = cstree::syntax::SyntaxNode<SyntaxKind, D>;
|
||||||
|
pub type SyntaxToken<D = ()> = cstree::syntax::SyntaxToken<SyntaxKind, D>;
|
||||||
|
pub type SyntaxElement<D = ()> = cstree::syntax::SyntaxElement<SyntaxKind, D>;
|
||||||
|
pub type SyntaxElementRef<'a, D = ()> = cstree::syntax::SyntaxElementRef<'a, SyntaxKind, D>;
|
||||||
|
|
||||||
|
pub type ResolvedNode<D = ()> = cstree::syntax::ResolvedNode<SyntaxKind, D>;
|
||||||
|
pub type ResolvedToken<D = ()> = cstree::syntax::ResolvedToken<SyntaxKind, D>;
|
||||||
|
pub type ResolvedElement<D = ()> = cstree::syntax::ResolvedElement<SyntaxKind, D>;
|
||||||
|
pub type ResolvedElementRef<'a, D = ()> = cstree::syntax::ResolvedElementRef<'a, SyntaxKind, D>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Element<'s> {
|
||||||
|
Node(Vec<Element<'s>>),
|
||||||
|
Token(&'s str),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct SyntaxKind(u32);
|
||||||
|
|
||||||
|
impl Syntax for SyntaxKind {
|
||||||
|
fn from_raw(raw: RawSyntaxKind) -> Self {
|
||||||
|
Self(raw.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw(self) -> RawSyntaxKind {
|
||||||
|
RawSyntaxKind(self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn static_text(self) -> Option<&'static str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_tree_with_cache<I>(root: &Element<'_>, cache: &mut NodeCache<'_, I>) -> GreenNode
|
||||||
|
where
|
||||||
|
I: Interner,
|
||||||
|
{
|
||||||
|
let mut builder: GreenNodeBuilder<SyntaxKind, I> = GreenNodeBuilder::with_cache(cache);
|
||||||
|
build_recursive(root, &mut builder, 0);
|
||||||
|
let (node, cache) = builder.finish();
|
||||||
|
assert!(cache.is_none());
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_recursive<I>(
|
||||||
|
root: &Element<'_>,
|
||||||
|
builder: &mut GreenNodeBuilder<'_, '_, SyntaxKind, I>,
|
||||||
|
mut from: u32,
|
||||||
|
) -> u32
|
||||||
|
where
|
||||||
|
I: Interner,
|
||||||
|
{
|
||||||
|
match root {
|
||||||
|
Element::Node(children) => {
|
||||||
|
builder.start_node(SyntaxKind(from));
|
||||||
|
for child in children {
|
||||||
|
from = build_recursive(child, builder, from + 1);
|
||||||
|
}
|
||||||
|
builder.finish_node();
|
||||||
|
}
|
||||||
|
Element::Token(text) => {
|
||||||
|
builder.token(SyntaxKind(from), text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
from
|
||||||
|
}
|
21
cstree/tests/it/regressions.rs
Normal file
21
cstree/tests/it/regressions.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_tree_arc() {
|
||||||
|
// this test is not here for the test itself, but to run it through MIRI, who complained about out-of-bound
|
||||||
|
// `ThinArc` pointers for a root `GreenNode` with no children
|
||||||
|
|
||||||
|
use cstree::{build::GreenNodeBuilder, syntax::SyntaxNode};
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
enum SyntaxKind {
|
||||||
|
Root,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut builder: GreenNodeBuilder<SyntaxKind> = GreenNodeBuilder::new();
|
||||||
|
builder.start_node(SyntaxKind::Root);
|
||||||
|
builder.finish_node();
|
||||||
|
let (green, _) = builder.finish();
|
||||||
|
let root: SyntaxNode<SyntaxKind> = SyntaxNode::new_root(green);
|
||||||
|
assert_eq!(root.kind(), SyntaxKind::Root);
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
use crossbeam_utils::thread::scope;
|
use crossbeam_utils::thread::scope;
|
||||||
use std::{thread, time::Duration};
|
use std::{thread, time::Duration};
|
||||||
|
|
||||||
use super::{build_recursive, Element, ResolvedNode, SyntaxNode, TestLang};
|
use super::{build_recursive, Element, ResolvedNode, SyntaxKind, SyntaxNode};
|
||||||
use cstree::build::GreenNodeBuilder;
|
use cstree::build::GreenNodeBuilder;
|
||||||
|
|
||||||
// Excercise the multi-threaded interner when the corresponding feature is enabled.
|
// Excercise the multi-threaded interner when the corresponding feature is enabled.
|
||||||
|
@ -12,12 +12,12 @@ use cstree::build::GreenNodeBuilder;
|
||||||
use cstree::interning::{new_threaded_interner, MultiThreadedTokenInterner};
|
use cstree::interning::{new_threaded_interner, MultiThreadedTokenInterner};
|
||||||
|
|
||||||
#[cfg(not(feature = "multi_threaded_interning"))]
|
#[cfg(not(feature = "multi_threaded_interning"))]
|
||||||
fn get_builder() -> GreenNodeBuilder<'static, 'static, TestLang> {
|
fn get_builder() -> GreenNodeBuilder<'static, 'static, SyntaxKind> {
|
||||||
GreenNodeBuilder::new()
|
GreenNodeBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "multi_threaded_interning")]
|
#[cfg(feature = "multi_threaded_interning")]
|
||||||
fn get_builder() -> GreenNodeBuilder<'static, 'static, TestLang, MultiThreadedTokenInterner> {
|
fn get_builder() -> GreenNodeBuilder<'static, 'static, SyntaxKind, MultiThreadedTokenInterner> {
|
||||||
let interner = new_threaded_interner();
|
let interner = new_threaded_interner();
|
||||||
GreenNodeBuilder::from_interner(interner)
|
GreenNodeBuilder::from_interner(interner)
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{build_recursive, build_tree_with_cache, ResolvedNode};
|
use crate::{build_recursive, build_tree_with_cache, ResolvedNode};
|
||||||
|
|
||||||
use super::{Element, SyntaxNode, TestLang};
|
use super::{Element, SyntaxKind, SyntaxNode};
|
||||||
use cstree::{
|
use cstree::{
|
||||||
build::{GreenNodeBuilder, NodeCache},
|
build::{GreenNodeBuilder, NodeCache},
|
||||||
interning::new_interner,
|
interning::new_interner,
|
||||||
|
@ -225,7 +225,7 @@ fn three_level_tree() -> Element<'static> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tree(root: Element<'_>) -> ResolvedNode<String> {
|
fn build_tree(root: Element<'_>) -> ResolvedNode<String> {
|
||||||
let mut builder: GreenNodeBuilder<TestLang> = GreenNodeBuilder::new();
|
let mut builder: GreenNodeBuilder<SyntaxKind> = GreenNodeBuilder::new();
|
||||||
build_recursive(&root, &mut builder, 0);
|
build_recursive(&root, &mut builder, 0);
|
||||||
let (node, cache) = builder.finish();
|
let (node, cache) = builder.finish();
|
||||||
SyntaxNode::new_root_with_resolver(node, cache.unwrap().into_interner().unwrap())
|
SyntaxNode::new_root_with_resolver(node, cache.unwrap().into_interner().unwrap())
|
16
test_suite/Cargo.toml
Normal file
16
test_suite/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
[package]
|
||||||
|
name = "cstree_test_suite"
|
||||||
|
publish = false
|
||||||
|
version = "0.0.0"
|
||||||
|
edition.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
readme.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cstree = { path = "../cstree", features = ["derive"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
trybuild = { version = "1.0.80", features = ["diff"] }
|
22
test_suite/tests/derive.rs
Normal file
22
test_suite/tests/derive.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use cstree::{RawSyntaxKind, Syntax};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic() {
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text("b")]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
pub type MySyntax = SyntaxKind;
|
||||||
|
|
||||||
|
assert_eq!(MySyntax::into_raw(SyntaxKind::A), RawSyntaxKind(0));
|
||||||
|
assert_eq!(MySyntax::into_raw(SyntaxKind::B), RawSyntaxKind(1));
|
||||||
|
|
||||||
|
assert_eq!(MySyntax::from_raw(RawSyntaxKind(0)), SyntaxKind::A);
|
||||||
|
assert_eq!(MySyntax::from_raw(RawSyntaxKind(1)), SyntaxKind::B);
|
||||||
|
|
||||||
|
assert!(MySyntax::static_text(SyntaxKind::A).is_none());
|
||||||
|
assert_eq!(MySyntax::static_text(SyntaxKind::B), Some("b"));
|
||||||
|
}
|
6
test_suite/tests/ui.rs
Normal file
6
test_suite/tests/ui.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(miri, ignore)]
|
||||||
|
fn ui() {
|
||||||
|
let t = trybuild::TestCases::new();
|
||||||
|
t.compile_fail("tests/ui/**/*.rs");
|
||||||
|
}
|
10
test_suite/tests/ui/repr/missing_repr.rs
Normal file
10
test_suite/tests/ui/repr/missing_repr.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text("b")]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
9
test_suite/tests/ui/repr/missing_repr.stderr
Normal file
9
test_suite/tests/ui/repr/missing_repr.stderr
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
error: syntax kind definitions must be `#[repr(u32)]` to derive `Syntax`
|
||||||
|
--> tests/ui/repr/missing_repr.rs:4:1
|
||||||
|
|
|
||||||
|
4 | / pub enum SyntaxKind {
|
||||||
|
5 | | A,
|
||||||
|
6 | | #[static_text("b")]
|
||||||
|
7 | | B,
|
||||||
|
8 | | }
|
||||||
|
| |_^
|
11
test_suite/tests/ui/repr/wrong_repr_c.rs
Normal file
11
test_suite/tests/ui/repr/wrong_repr_c.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text("b")]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
10
test_suite/tests/ui/repr/wrong_repr_c.stderr
Normal file
10
test_suite/tests/ui/repr/wrong_repr_c.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
error: syntax kind definitions must be `#[repr(u32)]` to derive `Syntax`
|
||||||
|
--> tests/ui/repr/wrong_repr_c.rs:4:1
|
||||||
|
|
|
||||||
|
4 | / #[repr(C)]
|
||||||
|
5 | | pub enum SyntaxKind {
|
||||||
|
6 | | A,
|
||||||
|
7 | | #[static_text("b")]
|
||||||
|
8 | | B,
|
||||||
|
9 | | }
|
||||||
|
| |_^
|
11
test_suite/tests/ui/repr/wrong_repr_u16.rs
Normal file
11
test_suite/tests/ui/repr/wrong_repr_u16.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text("b")]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
10
test_suite/tests/ui/repr/wrong_repr_u16.stderr
Normal file
10
test_suite/tests/ui/repr/wrong_repr_u16.stderr
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
error: syntax kind definitions must be `#[repr(u32)]` to derive `Syntax`
|
||||||
|
--> tests/ui/repr/wrong_repr_u16.rs:4:1
|
||||||
|
|
|
||||||
|
4 | / #[repr(u16)]
|
||||||
|
5 | | pub enum SyntaxKind {
|
||||||
|
6 | | A,
|
||||||
|
7 | | #[static_text("b")]
|
||||||
|
8 | | B,
|
||||||
|
9 | | }
|
||||||
|
| |_^
|
11
test_suite/tests/ui/static_text/empty_expr.rs
Normal file
11
test_suite/tests/ui/static_text/empty_expr.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text()]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
11
test_suite/tests/ui/static_text/empty_expr.stderr
Normal file
11
test_suite/tests/ui/static_text/empty_expr.stderr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
error: argument to `static_text` must be a string literal: `#[static_text("...")]`
|
||||||
|
--> tests/ui/static_text/empty_expr.rs:7:7
|
||||||
|
|
|
||||||
|
7 | #[static_text()]
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: unexpected end of input, expected string literal
|
||||||
|
--> tests/ui/static_text/empty_expr.rs:7:19
|
||||||
|
|
|
||||||
|
7 | #[static_text()]
|
||||||
|
| ^
|
11
test_suite/tests/ui/static_text/missing_text.rs
Normal file
11
test_suite/tests/ui/static_text/missing_text.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
5
test_suite/tests/ui/static_text/missing_text.stderr
Normal file
5
test_suite/tests/ui/static_text/missing_text.stderr
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
error: missing text for `static_text`: try `#[static_text("...")]`
|
||||||
|
--> tests/ui/static_text/missing_text.rs:7:5
|
||||||
|
|
|
||||||
|
7 | #[static_text]
|
||||||
|
| ^^^^^^^^^^^^^^
|
11
test_suite/tests/ui/static_text/non_expr.rs
Normal file
11
test_suite/tests/ui/static_text/non_expr.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text(SyntaxKind)]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
11
test_suite/tests/ui/static_text/non_expr.stderr
Normal file
11
test_suite/tests/ui/static_text/non_expr.stderr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
error: argument to `static_text` must be a string literal: `#[static_text("...")]`
|
||||||
|
--> tests/ui/static_text/non_expr.rs:7:7
|
||||||
|
|
|
||||||
|
7 | #[static_text(SyntaxKind)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected string literal
|
||||||
|
--> tests/ui/static_text/non_expr.rs:7:19
|
||||||
|
|
|
||||||
|
7 | #[static_text(SyntaxKind)]
|
||||||
|
| ^^^^^^^^^^
|
11
test_suite/tests/ui/static_text/non_string_expr.rs
Normal file
11
test_suite/tests/ui/static_text/non_string_expr.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text(foo + 3)]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
11
test_suite/tests/ui/static_text/non_string_expr.stderr
Normal file
11
test_suite/tests/ui/static_text/non_string_expr.stderr
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
error: argument to `static_text` must be a string literal: `#[static_text("...")]`
|
||||||
|
--> tests/ui/static_text/non_string_expr.rs:7:7
|
||||||
|
|
|
||||||
|
7 | #[static_text(foo + 3)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected string literal
|
||||||
|
--> tests/ui/static_text/non_string_expr.rs:7:19
|
||||||
|
|
|
||||||
|
7 | #[static_text(foo + 3)]
|
||||||
|
| ^^^
|
11
test_suite/tests/ui/static_text/text_assigned.rs
Normal file
11
test_suite/tests/ui/static_text/text_assigned.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use cstree::Syntax;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Syntax)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum SyntaxKind {
|
||||||
|
A,
|
||||||
|
#[static_text = "b"]
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
5
test_suite/tests/ui/static_text/text_assigned.stderr
Normal file
5
test_suite/tests/ui/static_text/text_assigned.stderr
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
error: `static_text` takes the text as a function argument: `#[static_text("...")]`
|
||||||
|
--> tests/ui/static_text/text_assigned.rs:7:5
|
||||||
|
|
|
||||||
|
7 | #[static_text = "b"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^
|
|
@ -1,77 +0,0 @@
|
||||||
mod basic;
|
|
||||||
mod regressions;
|
|
||||||
mod sendsync;
|
|
||||||
#[cfg(feature = "serialize")]
|
|
||||||
mod serde;
|
|
||||||
|
|
||||||
use cstree::{
|
|
||||||
build::{GreenNodeBuilder, NodeCache},
|
|
||||||
green::GreenNode,
|
|
||||||
interning::Interner,
|
|
||||||
Language, RawSyntaxKind,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type SyntaxNode<D = ()> = cstree::syntax::SyntaxNode<TestLang, D>;
|
|
||||||
pub type SyntaxToken<D = ()> = cstree::syntax::SyntaxToken<TestLang, D>;
|
|
||||||
pub type SyntaxElement<D = ()> = cstree::syntax::SyntaxElement<TestLang, D>;
|
|
||||||
pub type SyntaxElementRef<'a, D = ()> = cstree::syntax::SyntaxElementRef<'a, TestLang, D>;
|
|
||||||
|
|
||||||
pub type ResolvedNode<D = ()> = cstree::syntax::ResolvedNode<TestLang, D>;
|
|
||||||
pub type ResolvedToken<D = ()> = cstree::syntax::ResolvedToken<TestLang, D>;
|
|
||||||
pub type ResolvedElement<D = ()> = cstree::syntax::ResolvedElement<TestLang, D>;
|
|
||||||
pub type ResolvedElementRef<'a, D = ()> = cstree::syntax::ResolvedElementRef<'a, TestLang, D>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Element<'s> {
|
|
||||||
Node(Vec<Element<'s>>),
|
|
||||||
Token(&'s str),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
||||||
pub enum TestLang {}
|
|
||||||
impl Language for TestLang {
|
|
||||||
type Kind = RawSyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: RawSyntaxKind) -> Self::Kind {
|
|
||||||
raw
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> RawSyntaxKind {
|
|
||||||
kind
|
|
||||||
}
|
|
||||||
|
|
||||||
fn static_text(_kind: Self::Kind) -> Option<&'static str> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_tree_with_cache<I>(root: &Element<'_>, cache: &mut NodeCache<'_, I>) -> GreenNode
|
|
||||||
where
|
|
||||||
I: Interner,
|
|
||||||
{
|
|
||||||
let mut builder: GreenNodeBuilder<TestLang, I> = GreenNodeBuilder::with_cache(cache);
|
|
||||||
build_recursive(root, &mut builder, 0);
|
|
||||||
let (node, cache) = builder.finish();
|
|
||||||
assert!(cache.is_none());
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_recursive<L, I>(root: &Element<'_>, builder: &mut GreenNodeBuilder<'_, '_, L, I>, mut from: u32) -> u32
|
|
||||||
where
|
|
||||||
L: Language<Kind = RawSyntaxKind>,
|
|
||||||
I: Interner,
|
|
||||||
{
|
|
||||||
match root {
|
|
||||||
Element::Node(children) => {
|
|
||||||
builder.start_node(RawSyntaxKind(from));
|
|
||||||
for child in children {
|
|
||||||
from = build_recursive(child, builder, from + 1);
|
|
||||||
}
|
|
||||||
builder.finish_node();
|
|
||||||
}
|
|
||||||
Element::Token(text) => {
|
|
||||||
builder.token(RawSyntaxKind(from), text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
from
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
#[test]
|
|
||||||
fn empty_tree_arc() {
|
|
||||||
// this test is not here for the test itself, but to run it through MIRI, who complained about out-of-bound
|
|
||||||
// `ThinArc` pointers for a root `GreenNode` with no children
|
|
||||||
|
|
||||||
use cstree::{build::GreenNodeBuilder, syntax::SyntaxNode};
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[repr(u32)]
|
|
||||||
enum SyntaxKind {
|
|
||||||
Root,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
enum Lang {}
|
|
||||||
impl cstree::Language for Lang {
|
|
||||||
// ...
|
|
||||||
type Kind = SyntaxKind;
|
|
||||||
|
|
||||||
fn kind_from_raw(raw: cstree::RawSyntaxKind) -> Self::Kind {
|
|
||||||
assert!(raw.0 <= SyntaxKind::Root as u32);
|
|
||||||
unsafe { std::mem::transmute::<u32, SyntaxKind>(raw.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn kind_to_raw(kind: Self::Kind) -> cstree::RawSyntaxKind {
|
|
||||||
cstree::RawSyntaxKind(kind as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn static_text(_kind: Self::Kind) -> Option<&'static str> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut builder: GreenNodeBuilder<Lang> = GreenNodeBuilder::new();
|
|
||||||
builder.start_node(SyntaxKind::Root);
|
|
||||||
builder.finish_node();
|
|
||||||
let (green, _) = builder.finish();
|
|
||||||
let root: SyntaxNode<Lang> = SyntaxNode::new_root(green);
|
|
||||||
assert_eq!(root.kind(), SyntaxKind::Root);
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue