aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assists/split_import.rs6
-rw-r--r--crates/ra_syntax/src/algo.rs2
-rw-r--r--crates/ra_syntax/src/algo/visit.rs112
-rw-r--r--crates/ra_syntax/src/lib.rs43
-rw-r--r--docs/dev/architecture.md4
-rw-r--r--docs/user/features.md4
6 files changed, 27 insertions, 144 deletions
diff --git a/crates/ra_assists/src/assists/split_import.rs b/crates/ra_assists/src/assists/split_import.rs
index 19d429daf..fe3e64af5 100644
--- a/crates/ra_assists/src/assists/split_import.rs
+++ b/crates/ra_assists/src/assists/split_import.rs
@@ -51,13 +51,13 @@ mod tests {
51 fn split_import_works_with_trees() { 51 fn split_import_works_with_trees() {
52 check_assist( 52 check_assist(
53 split_import, 53 split_import,
54 "use algo:<|>:visitor::{Visitor, visit}", 54 "use crate:<|>:db::{RootDatabase, FileSymbol}",
55 "use algo::{<|>visitor::{Visitor, visit}}", 55 "use crate::{<|>db::{RootDatabase, FileSymbol}}",
56 ) 56 )
57 } 57 }
58 58
59 #[test] 59 #[test]
60 fn split_import_target() { 60 fn split_import_target() {
61 check_assist_target(split_import, "use algo::<|>visitor::{Visitor, visit}", "::"); 61 check_assist_target(split_import, "use crate::<|>db::{RootDatabase, FileSymbol}", "::");
62 } 62 }
63} 63}
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index d55534ede..7cfea70f9 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -1,7 +1,5 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3pub mod visit;
4
5use std::ops::RangeInclusive; 3use std::ops::RangeInclusive;
6 4
7use itertools::Itertools; 5use itertools::Itertools;
diff --git a/crates/ra_syntax/src/algo/visit.rs b/crates/ra_syntax/src/algo/visit.rs
deleted file mode 100644
index 4df275ba4..000000000
--- a/crates/ra_syntax/src/algo/visit.rs
+++ /dev/null
@@ -1,112 +0,0 @@
1//! FIXME: write short doc here
2
3use crate::{AstNode, SyntaxNode};
4
5use std::marker::PhantomData;
6
7pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> {
8 EmptyVisitor { ph: PhantomData }
9}
10
11pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> {
12 EmptyVisitorCtx { ph: PhantomData, ctx }
13}
14
15pub trait Visitor<'a>: Sized {
16 type Output;
17 fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output>;
18 fn visit<N, F>(self, f: F) -> Vis<Self, N, F>
19 where
20 N: AstNode + 'a,
21 F: FnOnce(N) -> Self::Output,
22 {
23 Vis { inner: self, f, ph: PhantomData }
24 }
25}
26
27pub trait VisitorCtx<'a>: Sized {
28 type Output;
29 type Ctx;
30 fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx>;
31 fn visit<N, F>(self, f: F) -> VisCtx<Self, N, F>
32 where
33 N: AstNode + 'a,
34 F: FnOnce(N, Self::Ctx) -> Self::Output,
35 {
36 VisCtx { inner: self, f, ph: PhantomData }
37 }
38}
39
40#[derive(Debug)]
41struct EmptyVisitor<T> {
42 ph: PhantomData<fn() -> T>,
43}
44
45impl<'a, T> Visitor<'a> for EmptyVisitor<T> {
46 type Output = T;
47
48 fn accept(self, _node: &'a SyntaxNode) -> Option<T> {
49 None
50 }
51}
52
53#[derive(Debug)]
54struct EmptyVisitorCtx<T, C> {
55 ctx: C,
56 ph: PhantomData<fn() -> T>,
57}
58
59impl<'a, T, C> VisitorCtx<'a> for EmptyVisitorCtx<T, C> {
60 type Output = T;
61 type Ctx = C;
62
63 fn accept(self, _node: &'a SyntaxNode) -> Result<T, C> {
64 Err(self.ctx)
65 }
66}
67
68#[derive(Debug)]
69pub struct Vis<V, N, F> {
70 inner: V,
71 f: F,
72 ph: PhantomData<fn(N)>,
73}
74
75impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F>
76where
77 V: Visitor<'a>,
78 N: AstNode + 'a,
79 F: FnOnce(N) -> <V as Visitor<'a>>::Output,
80{
81 type Output = <V as Visitor<'a>>::Output;
82
83 fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output> {
84 let Vis { inner, f, .. } = self;
85 inner.accept(node).or_else(|| N::cast(node.clone()).map(f))
86 }
87}
88
89#[derive(Debug)]
90pub struct VisCtx<V, N, F> {
91 inner: V,
92 f: F,
93 ph: PhantomData<fn(N)>,
94}
95
96impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F>
97where
98 V: VisitorCtx<'a>,
99 N: AstNode + 'a,
100 F: FnOnce(N, <V as VisitorCtx<'a>>::Ctx) -> <V as VisitorCtx<'a>>::Output,
101{
102 type Output = <V as VisitorCtx<'a>>::Output;
103 type Ctx = <V as VisitorCtx<'a>>::Ctx;
104
105 fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx> {
106 let VisCtx { inner, f, .. } = self;
107 inner.accept(node).or_else(|ctx| match N::cast(node.clone()) {
108 None => Err(ctx),
109 Some(node) => Ok(f(node, ctx)),
110 })
111 }
112}
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 09230ccb2..c315ba552 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -160,6 +160,17 @@ impl SourceFile {
160 } 160 }
161} 161}
162 162
163#[macro_export]
164macro_rules! match_ast {
165 (match $node:ident {
166 $( ast::$ast:ident($it:ident) => $res:block, )*
167 _ => $catch_all:expr,
168 }) => {{
169 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
170 { $catch_all }
171 }};
172}
173
163/// This test does not assert anything and instead just shows off the crate's 174/// This test does not assert anything and instead just shows off the crate's
164/// API. 175/// API.
165#[test] 176#[test]
@@ -294,8 +305,7 @@ fn api_walkthrough() {
294 // To recursively process the tree, there are three approaches: 305 // To recursively process the tree, there are three approaches:
295 // 1. explicitly call getter methods on AST nodes. 306 // 1. explicitly call getter methods on AST nodes.
296 // 2. use descendants and `AstNode::cast`. 307 // 2. use descendants and `AstNode::cast`.
297 // 3. use descendants and the visitor. 308 // 3. use descendants and `match_ast!`.
298 // 4. use descendants and `match_ast!`.
299 // 309 //
300 // Here's how the first one looks like: 310 // Here's how the first one looks like:
301 let exprs_cast: Vec<String> = file 311 let exprs_cast: Vec<String> = file
@@ -305,29 +315,18 @@ fn api_walkthrough() {
305 .map(|expr| expr.syntax().text().to_string()) 315 .map(|expr| expr.syntax().text().to_string())
306 .collect(); 316 .collect();
307 317
308 // An alternative is to use a visitor. The visitor does not do traversal 318 // An alternative is to use a macro.
309 // automatically (so it's more akin to a generic lambda) and is constructed
310 // from closures. This seems more flexible than a single generated visitor
311 // trait.
312 use algo::visit::{visitor, Visitor};
313 let mut exprs_visit = Vec::new(); 319 let mut exprs_visit = Vec::new();
314 for node in file.syntax().descendants() { 320 for node in file.syntax().descendants() {
315 if let Some(result) = 321 match_ast! {
316 visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(&node) 322 match node {
317 { 323 ast::Expr(it) => {
318 exprs_visit.push(result); 324 let res = it.syntax().text().to_string();
325 exprs_visit.push(res);
326 },
327 _ => (),
328 }
319 } 329 }
320 } 330 }
321 assert_eq!(exprs_cast, exprs_visit); 331 assert_eq!(exprs_cast, exprs_visit);
322} 332}
323
324#[macro_export]
325macro_rules! match_ast {
326 (match $node:ident {
327 $( ast::$ast:ident($it:ident) => $res:block, )*
328 _ => $catch_all:expr,
329 }) => {{
330 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
331 { $catch_all }
332 }};
333}
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md
index 1201f6e5a..1ffabc6ef 100644
--- a/docs/dev/architecture.md
+++ b/docs/dev/architecture.md
@@ -79,9 +79,7 @@ Rust syntax tree structure and parser. See
79- `grammar.ron` RON description of the grammar, which is used to 79- `grammar.ron` RON description of the grammar, which is used to
80 generate `syntax_kinds` and `ast` modules, using `cargo gen-syntax` command. 80 generate `syntax_kinds` and `ast` modules, using `cargo gen-syntax` command.
81- `algo`: generic tree algorithms, including `walk` for O(1) stack 81- `algo`: generic tree algorithms, including `walk` for O(1) stack
82 space tree traversal (this is cool) and `visit` for type-driven 82 space tree traversal (this is cool).
83 visiting the nodes (this is double plus cool, if you understand how
84 `Visitor` works, you understand the design of syntax trees).
85 83
86Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs` 84Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs`
87(test vectors) and `.txt` files with corresponding syntax trees. During testing, we check 85(test vectors) and `.txt` files with corresponding syntax trees. During testing, we check
diff --git a/docs/user/features.md b/docs/user/features.md
index 0ce8f577b..757a02838 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -367,9 +367,9 @@ impl VariantData {
367 367
368```rust 368```rust
369// before: 369// before:
370use algo:<|>:visitor::{Visitor, visit}; 370use crate:<|>:db::{RootDatabase, FileSymbol};
371// after: 371// after:
372use algo::{<|>visitor::{Visitor, visit}}; 372use crate::{<|>db::{RootDatabase, FileSymbol}};
373``` 373```
374 374
375- Flip binary expression 375- Flip binary expression