diff options
-rw-r--r-- | crates/ra_assists/src/assists/split_import.rs | 6 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo/visit.rs | 112 | ||||
-rw-r--r-- | crates/ra_syntax/src/lib.rs | 43 | ||||
-rw-r--r-- | docs/dev/architecture.md | 4 | ||||
-rw-r--r-- | docs/user/features.md | 4 |
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 | ||
3 | pub mod visit; | ||
4 | |||
5 | use std::ops::RangeInclusive; | 3 | use std::ops::RangeInclusive; |
6 | 4 | ||
7 | use itertools::Itertools; | 5 | use 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 | |||
3 | use crate::{AstNode, SyntaxNode}; | ||
4 | |||
5 | use std::marker::PhantomData; | ||
6 | |||
7 | pub fn visitor<'a, T>() -> impl Visitor<'a, Output = T> { | ||
8 | EmptyVisitor { ph: PhantomData } | ||
9 | } | ||
10 | |||
11 | pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output = T, Ctx = C> { | ||
12 | EmptyVisitorCtx { ph: PhantomData, ctx } | ||
13 | } | ||
14 | |||
15 | pub 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 | |||
27 | pub 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)] | ||
41 | struct EmptyVisitor<T> { | ||
42 | ph: PhantomData<fn() -> T>, | ||
43 | } | ||
44 | |||
45 | impl<'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)] | ||
54 | struct EmptyVisitorCtx<T, C> { | ||
55 | ctx: C, | ||
56 | ph: PhantomData<fn() -> T>, | ||
57 | } | ||
58 | |||
59 | impl<'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)] | ||
69 | pub struct Vis<V, N, F> { | ||
70 | inner: V, | ||
71 | f: F, | ||
72 | ph: PhantomData<fn(N)>, | ||
73 | } | ||
74 | |||
75 | impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F> | ||
76 | where | ||
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)] | ||
90 | pub struct VisCtx<V, N, F> { | ||
91 | inner: V, | ||
92 | f: F, | ||
93 | ph: PhantomData<fn(N)>, | ||
94 | } | ||
95 | |||
96 | impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F> | ||
97 | where | ||
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] | ||
164 | macro_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] | ||
325 | macro_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 | ||
86 | Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs` | 84 | Tests 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: |
370 | use algo:<|>:visitor::{Visitor, visit}; | 370 | use crate:<|>:db::{RootDatabase, FileSymbol}; |
371 | // after: | 371 | // after: |
372 | use algo::{<|>visitor::{Visitor, visit}}; | 372 | use crate::{<|>db::{RootDatabase, FileSymbol}}; |
373 | ``` | 373 | ``` |
374 | 374 | ||
375 | - Flip binary expression | 375 | - Flip binary expression |