diff options
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 12 | ||||
-rw-r--r-- | crates/ra_editor/src/assists.rs | 34 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/add_derive.rs | 97 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/add_impl.rs | 78 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/change_visibility.rs | 90 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/flip_comma.rs | 45 | ||||
-rw-r--r-- | crates/ra_editor/src/assists/introduce_variable.rs | 156 | ||||
-rw-r--r-- | crates/ra_editor/src/code_actions.rs | 415 | ||||
-rw-r--r-- | crates/ra_editor/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 6 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 47 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 15 |
12 files changed, 573 insertions, 428 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index b812c3441..136e7f7dc 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -6,7 +6,7 @@ use hir::{ | |||
6 | self, FnSignatureInfo, Problem, source_binder, | 6 | self, FnSignatureInfo, Problem, source_binder, |
7 | }; | 7 | }; |
8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
9 | use ra_editor::{self, find_node_at_offset, LocalEdit, Severity}; | 9 | use ra_editor::{self, find_node_at_offset, assists, LocalEdit, Severity}; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | algo::{find_covering_node, visit::{visitor, Visitor}}, | 11 | algo::{find_covering_node, visit::{visitor, Visitor}}, |
12 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | 12 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, |
@@ -335,11 +335,11 @@ impl db::RootDatabase { | |||
335 | let file = self.source_file(frange.file_id); | 335 | let file = self.source_file(frange.file_id); |
336 | let offset = frange.range.start(); | 336 | let offset = frange.range.start(); |
337 | let actions = vec![ | 337 | let actions = vec![ |
338 | ra_editor::flip_comma(&file, offset).map(|f| f()), | 338 | assists::flip_comma(&file, offset).map(|f| f()), |
339 | ra_editor::add_derive(&file, offset).map(|f| f()), | 339 | assists::add_derive(&file, offset).map(|f| f()), |
340 | ra_editor::add_impl(&file, offset).map(|f| f()), | 340 | assists::add_impl(&file, offset).map(|f| f()), |
341 | ra_editor::make_pub_crate(&file, offset).map(|f| f()), | 341 | assists::change_visibility(&file, offset).map(|f| f()), |
342 | ra_editor::introduce_variable(&file, frange.range).map(|f| f()), | 342 | assists::introduce_variable(&file, frange.range).map(|f| f()), |
343 | ]; | 343 | ]; |
344 | actions | 344 | actions |
345 | .into_iter() | 345 | .into_iter() |
diff --git a/crates/ra_editor/src/assists.rs b/crates/ra_editor/src/assists.rs new file mode 100644 index 000000000..b6e6dd628 --- /dev/null +++ b/crates/ra_editor/src/assists.rs | |||
@@ -0,0 +1,34 @@ | |||
1 | //! This modules contains various "assits": suggestions for source code edits | ||
2 | //! which are likely to occur at a given cursor positon. For example, if the | ||
3 | //! cursor is on the `,`, a possible assist is swapping the elments around the | ||
4 | //! comma. | ||
5 | |||
6 | mod flip_comma; | ||
7 | mod add_derive; | ||
8 | mod add_impl; | ||
9 | mod introduce_variable; | ||
10 | mod change_visibility; | ||
11 | |||
12 | use ra_text_edit::TextEdit; | ||
13 | use ra_syntax::{Direction, SyntaxNodeRef, TextUnit}; | ||
14 | |||
15 | pub use self::{ | ||
16 | flip_comma::flip_comma, | ||
17 | add_derive::add_derive, | ||
18 | add_impl::add_impl, | ||
19 | introduce_variable::introduce_variable, | ||
20 | change_visibility::change_visibility, | ||
21 | }; | ||
22 | |||
23 | #[derive(Debug)] | ||
24 | pub struct LocalEdit { | ||
25 | pub label: String, | ||
26 | pub edit: TextEdit, | ||
27 | pub cursor_position: Option<TextUnit>, | ||
28 | } | ||
29 | |||
30 | fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { | ||
31 | node.siblings(direction) | ||
32 | .skip(1) | ||
33 | .find(|node| !node.kind().is_trivia()) | ||
34 | } | ||
diff --git a/crates/ra_editor/src/assists/add_derive.rs b/crates/ra_editor/src/assists/add_derive.rs new file mode 100644 index 000000000..33d9d2c31 --- /dev/null +++ b/crates/ra_editor/src/assists/add_derive.rs | |||
@@ -0,0 +1,97 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | ||
3 | ast::{self, AstNode, AttrsOwner}, | ||
4 | SourceFileNode, | ||
5 | SyntaxKind::{WHITESPACE, COMMENT}, | ||
6 | TextUnit, | ||
7 | }; | ||
8 | |||
9 | use crate::{ | ||
10 | find_node_at_offset, | ||
11 | assists::LocalEdit, | ||
12 | }; | ||
13 | |||
14 | pub fn add_derive<'a>( | ||
15 | file: &'a SourceFileNode, | ||
16 | offset: TextUnit, | ||
17 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
18 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | ||
19 | let node_start = derive_insertion_offset(nominal)?; | ||
20 | return Some(move || { | ||
21 | let derive_attr = nominal | ||
22 | .attrs() | ||
23 | .filter_map(|x| x.as_call()) | ||
24 | .filter(|(name, _arg)| name == "derive") | ||
25 | .map(|(_name, arg)| arg) | ||
26 | .next(); | ||
27 | let mut edit = TextEditBuilder::new(); | ||
28 | let offset = match derive_attr { | ||
29 | None => { | ||
30 | edit.insert(node_start, "#[derive()]\n".to_string()); | ||
31 | node_start + TextUnit::of_str("#[derive(") | ||
32 | } | ||
33 | Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), | ||
34 | }; | ||
35 | LocalEdit { | ||
36 | label: "add `#[derive]`".to_string(), | ||
37 | edit: edit.finish(), | ||
38 | cursor_position: Some(offset), | ||
39 | } | ||
40 | }); | ||
41 | |||
42 | // Insert `derive` after doc comments. | ||
43 | fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> { | ||
44 | let non_ws_child = nominal | ||
45 | .syntax() | ||
46 | .children() | ||
47 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
48 | Some(non_ws_child.range().start()) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | #[cfg(test)] | ||
53 | mod tests { | ||
54 | use super::*; | ||
55 | use crate::test_utils::check_action; | ||
56 | |||
57 | #[test] | ||
58 | fn add_derive_new() { | ||
59 | check_action( | ||
60 | "struct Foo { a: i32, <|>}", | ||
61 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
62 | |file, off| add_derive(file, off).map(|f| f()), | ||
63 | ); | ||
64 | check_action( | ||
65 | "struct Foo { <|> a: i32, }", | ||
66 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
67 | |file, off| add_derive(file, off).map(|f| f()), | ||
68 | ); | ||
69 | } | ||
70 | |||
71 | #[test] | ||
72 | fn add_derive_existing() { | ||
73 | check_action( | ||
74 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | ||
75 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | ||
76 | |file, off| add_derive(file, off).map(|f| f()), | ||
77 | ); | ||
78 | } | ||
79 | |||
80 | #[test] | ||
81 | fn add_derive_new_with_doc_comment() { | ||
82 | check_action( | ||
83 | " | ||
84 | /// `Foo` is a pretty important struct. | ||
85 | /// It does stuff. | ||
86 | struct Foo { a: i32<|>, } | ||
87 | ", | ||
88 | " | ||
89 | /// `Foo` is a pretty important struct. | ||
90 | /// It does stuff. | ||
91 | #[derive(<|>)] | ||
92 | struct Foo { a: i32, } | ||
93 | ", | ||
94 | |file, off| add_derive(file, off).map(|f| f()), | ||
95 | ); | ||
96 | } | ||
97 | } | ||
diff --git a/crates/ra_editor/src/assists/add_impl.rs b/crates/ra_editor/src/assists/add_impl.rs new file mode 100644 index 000000000..50e00688e --- /dev/null +++ b/crates/ra_editor/src/assists/add_impl.rs | |||
@@ -0,0 +1,78 @@ | |||
1 | use join_to_string::join; | ||
2 | use ra_text_edit::TextEditBuilder; | ||
3 | use ra_syntax::{ | ||
4 | ast::{self, AstNode, NameOwner, TypeParamsOwner}, | ||
5 | SourceFileNode, | ||
6 | TextUnit, | ||
7 | }; | ||
8 | |||
9 | use crate::{find_node_at_offset, assists::LocalEdit}; | ||
10 | |||
11 | pub fn add_impl<'a>( | ||
12 | file: &'a SourceFileNode, | ||
13 | offset: TextUnit, | ||
14 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
15 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | ||
16 | let name = nominal.name()?; | ||
17 | |||
18 | Some(move || { | ||
19 | let type_params = nominal.type_param_list(); | ||
20 | let mut edit = TextEditBuilder::new(); | ||
21 | let start_offset = nominal.syntax().range().end(); | ||
22 | let mut buf = String::new(); | ||
23 | buf.push_str("\n\nimpl"); | ||
24 | if let Some(type_params) = type_params { | ||
25 | type_params.syntax().text().push_to(&mut buf); | ||
26 | } | ||
27 | buf.push_str(" "); | ||
28 | buf.push_str(name.text().as_str()); | ||
29 | if let Some(type_params) = type_params { | ||
30 | let lifetime_params = type_params | ||
31 | .lifetime_params() | ||
32 | .filter_map(|it| it.lifetime()) | ||
33 | .map(|it| it.text()); | ||
34 | let type_params = type_params | ||
35 | .type_params() | ||
36 | .filter_map(|it| it.name()) | ||
37 | .map(|it| it.text()); | ||
38 | join(lifetime_params.chain(type_params)) | ||
39 | .surround_with("<", ">") | ||
40 | .to_buf(&mut buf); | ||
41 | } | ||
42 | buf.push_str(" {\n"); | ||
43 | let offset = start_offset + TextUnit::of_str(&buf); | ||
44 | buf.push_str("\n}"); | ||
45 | edit.insert(start_offset, buf); | ||
46 | LocalEdit { | ||
47 | label: "add impl".to_string(), | ||
48 | edit: edit.finish(), | ||
49 | cursor_position: Some(offset), | ||
50 | } | ||
51 | }) | ||
52 | } | ||
53 | |||
54 | #[cfg(test)] | ||
55 | mod tests { | ||
56 | use super::*; | ||
57 | use crate::test_utils::check_action; | ||
58 | |||
59 | #[test] | ||
60 | fn test_add_impl() { | ||
61 | check_action( | ||
62 | "struct Foo {<|>}\n", | ||
63 | "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", | ||
64 | |file, off| add_impl(file, off).map(|f| f()), | ||
65 | ); | ||
66 | check_action( | ||
67 | "struct Foo<T: Clone> {<|>}", | ||
68 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", | ||
69 | |file, off| add_impl(file, off).map(|f| f()), | ||
70 | ); | ||
71 | check_action( | ||
72 | "struct Foo<'a, T: Foo<'a>> {<|>}", | ||
73 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", | ||
74 | |file, off| add_impl(file, off).map(|f| f()), | ||
75 | ); | ||
76 | } | ||
77 | |||
78 | } | ||
diff --git a/crates/ra_editor/src/assists/change_visibility.rs b/crates/ra_editor/src/assists/change_visibility.rs new file mode 100644 index 000000000..98c218f32 --- /dev/null +++ b/crates/ra_editor/src/assists/change_visibility.rs | |||
@@ -0,0 +1,90 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | ||
3 | SourceFileNode, | ||
4 | algo::find_leaf_at_offset, | ||
5 | SyntaxKind::{VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF}, | ||
6 | TextUnit, | ||
7 | }; | ||
8 | |||
9 | use crate::assists::LocalEdit; | ||
10 | |||
11 | pub fn change_visibility<'a>( | ||
12 | file: &'a SourceFileNode, | ||
13 | offset: TextUnit, | ||
14 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
15 | let syntax = file.syntax(); | ||
16 | |||
17 | let keyword = find_leaf_at_offset(syntax, offset).find(|leaf| match leaf.kind() { | ||
18 | FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true, | ||
19 | _ => false, | ||
20 | })?; | ||
21 | let parent = keyword.parent()?; | ||
22 | let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; | ||
23 | let node_start = parent.range().start(); | ||
24 | Some(move || { | ||
25 | let mut edit = TextEditBuilder::new(); | ||
26 | |||
27 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) | ||
28 | || parent.children().any(|child| child.kind() == VISIBILITY) | ||
29 | { | ||
30 | return LocalEdit { | ||
31 | label: "make pub crate".to_string(), | ||
32 | edit: edit.finish(), | ||
33 | cursor_position: Some(offset), | ||
34 | }; | ||
35 | } | ||
36 | |||
37 | edit.insert(node_start, "pub(crate) ".to_string()); | ||
38 | LocalEdit { | ||
39 | label: "make pub crate".to_string(), | ||
40 | edit: edit.finish(), | ||
41 | cursor_position: Some(node_start), | ||
42 | } | ||
43 | }) | ||
44 | } | ||
45 | |||
46 | #[cfg(test)] | ||
47 | mod tests { | ||
48 | use super::*; | ||
49 | use crate::test_utils::check_action; | ||
50 | |||
51 | #[test] | ||
52 | fn test_change_visibility() { | ||
53 | check_action( | ||
54 | "<|>fn foo() {}", | ||
55 | "<|>pub(crate) fn foo() {}", | ||
56 | |file, off| change_visibility(file, off).map(|f| f()), | ||
57 | ); | ||
58 | check_action( | ||
59 | "f<|>n foo() {}", | ||
60 | "<|>pub(crate) fn foo() {}", | ||
61 | |file, off| change_visibility(file, off).map(|f| f()), | ||
62 | ); | ||
63 | check_action( | ||
64 | "<|>struct Foo {}", | ||
65 | "<|>pub(crate) struct Foo {}", | ||
66 | |file, off| change_visibility(file, off).map(|f| f()), | ||
67 | ); | ||
68 | check_action("<|>mod foo {}", "<|>pub(crate) mod foo {}", |file, off| { | ||
69 | change_visibility(file, off).map(|f| f()) | ||
70 | }); | ||
71 | check_action( | ||
72 | "<|>trait Foo {}", | ||
73 | "<|>pub(crate) trait Foo {}", | ||
74 | |file, off| change_visibility(file, off).map(|f| f()), | ||
75 | ); | ||
76 | check_action("m<|>od {}", "<|>pub(crate) mod {}", |file, off| { | ||
77 | change_visibility(file, off).map(|f| f()) | ||
78 | }); | ||
79 | check_action( | ||
80 | "pub(crate) f<|>n foo() {}", | ||
81 | "pub(crate) f<|>n foo() {}", | ||
82 | |file, off| change_visibility(file, off).map(|f| f()), | ||
83 | ); | ||
84 | check_action( | ||
85 | "unsafe f<|>n foo() {}", | ||
86 | "<|>pub(crate) unsafe fn foo() {}", | ||
87 | |file, off| change_visibility(file, off).map(|f| f()), | ||
88 | ); | ||
89 | } | ||
90 | } | ||
diff --git a/crates/ra_editor/src/assists/flip_comma.rs b/crates/ra_editor/src/assists/flip_comma.rs new file mode 100644 index 000000000..d8727db0d --- /dev/null +++ b/crates/ra_editor/src/assists/flip_comma.rs | |||
@@ -0,0 +1,45 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | ||
3 | algo::find_leaf_at_offset, | ||
4 | Direction, SourceFileNode, | ||
5 | SyntaxKind::COMMA, | ||
6 | TextUnit, | ||
7 | }; | ||
8 | |||
9 | use crate::assists::{LocalEdit, non_trivia_sibling}; | ||
10 | |||
11 | pub fn flip_comma<'a>( | ||
12 | file: &'a SourceFileNode, | ||
13 | offset: TextUnit, | ||
14 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
15 | let syntax = file.syntax(); | ||
16 | |||
17 | let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; | ||
18 | let prev = non_trivia_sibling(comma, Direction::Prev)?; | ||
19 | let next = non_trivia_sibling(comma, Direction::Next)?; | ||
20 | Some(move || { | ||
21 | let mut edit = TextEditBuilder::new(); | ||
22 | edit.replace(prev.range(), next.text().to_string()); | ||
23 | edit.replace(next.range(), prev.text().to_string()); | ||
24 | LocalEdit { | ||
25 | label: "flip comma".to_string(), | ||
26 | edit: edit.finish(), | ||
27 | cursor_position: None, | ||
28 | } | ||
29 | }) | ||
30 | } | ||
31 | |||
32 | #[cfg(test)] | ||
33 | mod tests { | ||
34 | use super::*; | ||
35 | use crate::test_utils::check_action; | ||
36 | |||
37 | #[test] | ||
38 | fn test_swap_comma() { | ||
39 | check_action( | ||
40 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", | ||
41 | "fn foo(y: Result<(), ()>,<|> x: i32) {}", | ||
42 | |file, off| flip_comma(file, off).map(|f| f()), | ||
43 | ) | ||
44 | } | ||
45 | } | ||
diff --git a/crates/ra_editor/src/assists/introduce_variable.rs b/crates/ra_editor/src/assists/introduce_variable.rs new file mode 100644 index 000000000..17ab521fa --- /dev/null +++ b/crates/ra_editor/src/assists/introduce_variable.rs | |||
@@ -0,0 +1,156 @@ | |||
1 | use ra_text_edit::TextEditBuilder; | ||
2 | use ra_syntax::{ | ||
3 | algo::{find_covering_node}, | ||
4 | ast::{self, AstNode}, | ||
5 | SourceFileNode, | ||
6 | SyntaxKind::{WHITESPACE}, | ||
7 | SyntaxNodeRef, TextRange, TextUnit, | ||
8 | }; | ||
9 | |||
10 | use crate::assists::LocalEdit; | ||
11 | |||
12 | pub fn introduce_variable<'a>( | ||
13 | file: &'a SourceFileNode, | ||
14 | range: TextRange, | ||
15 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
16 | let node = find_covering_node(file.syntax(), range); | ||
17 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | ||
18 | |||
19 | let anchor_stmt = anchor_stmt(expr)?; | ||
20 | let indent = anchor_stmt.prev_sibling()?; | ||
21 | if indent.kind() != WHITESPACE { | ||
22 | return None; | ||
23 | } | ||
24 | return Some(move || { | ||
25 | let mut buf = String::new(); | ||
26 | let mut edit = TextEditBuilder::new(); | ||
27 | |||
28 | buf.push_str("let var_name = "); | ||
29 | expr.syntax().text().push_to(&mut buf); | ||
30 | let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) { | ||
31 | Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax()) | ||
32 | } else { | ||
33 | false | ||
34 | }; | ||
35 | if is_full_stmt { | ||
36 | edit.replace(expr.syntax().range(), buf); | ||
37 | } else { | ||
38 | buf.push_str(";"); | ||
39 | indent.text().push_to(&mut buf); | ||
40 | edit.replace(expr.syntax().range(), "var_name".to_string()); | ||
41 | edit.insert(anchor_stmt.range().start(), buf); | ||
42 | } | ||
43 | let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let "); | ||
44 | LocalEdit { | ||
45 | label: "introduce variable".to_string(), | ||
46 | edit: edit.finish(), | ||
47 | cursor_position: Some(cursor_position), | ||
48 | } | ||
49 | }); | ||
50 | |||
51 | /// Statement or last in the block expression, which will follow | ||
52 | /// the freshly introduced var. | ||
53 | fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { | ||
54 | expr.syntax().ancestors().find(|&node| { | ||
55 | if ast::Stmt::cast(node).is_some() { | ||
56 | return true; | ||
57 | } | ||
58 | if let Some(expr) = node | ||
59 | .parent() | ||
60 | .and_then(ast::Block::cast) | ||
61 | .and_then(|it| it.expr()) | ||
62 | { | ||
63 | if expr.syntax() == node { | ||
64 | return true; | ||
65 | } | ||
66 | } | ||
67 | false | ||
68 | }) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[cfg(test)] | ||
73 | mod tests { | ||
74 | use super::*; | ||
75 | use crate::test_utils::check_action_range; | ||
76 | |||
77 | #[test] | ||
78 | fn test_introduce_var_simple() { | ||
79 | check_action_range( | ||
80 | " | ||
81 | fn foo() { | ||
82 | foo(<|>1 + 1<|>); | ||
83 | }", | ||
84 | " | ||
85 | fn foo() { | ||
86 | let <|>var_name = 1 + 1; | ||
87 | foo(var_name); | ||
88 | }", | ||
89 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
90 | ); | ||
91 | } | ||
92 | |||
93 | #[test] | ||
94 | fn test_introduce_var_expr_stmt() { | ||
95 | check_action_range( | ||
96 | " | ||
97 | fn foo() { | ||
98 | <|>1 + 1<|>; | ||
99 | }", | ||
100 | " | ||
101 | fn foo() { | ||
102 | let <|>var_name = 1 + 1; | ||
103 | }", | ||
104 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
105 | ); | ||
106 | } | ||
107 | |||
108 | #[test] | ||
109 | fn test_introduce_var_part_of_expr_stmt() { | ||
110 | check_action_range( | ||
111 | " | ||
112 | fn foo() { | ||
113 | <|>1<|> + 1; | ||
114 | }", | ||
115 | " | ||
116 | fn foo() { | ||
117 | let <|>var_name = 1; | ||
118 | var_name + 1; | ||
119 | }", | ||
120 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
121 | ); | ||
122 | } | ||
123 | |||
124 | #[test] | ||
125 | fn test_introduce_var_last_expr() { | ||
126 | check_action_range( | ||
127 | " | ||
128 | fn foo() { | ||
129 | bar(<|>1 + 1<|>) | ||
130 | }", | ||
131 | " | ||
132 | fn foo() { | ||
133 | let <|>var_name = 1 + 1; | ||
134 | bar(var_name) | ||
135 | }", | ||
136 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
137 | ); | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn test_introduce_var_last_full_expr() { | ||
142 | check_action_range( | ||
143 | " | ||
144 | fn foo() { | ||
145 | <|>bar(1 + 1)<|> | ||
146 | }", | ||
147 | " | ||
148 | fn foo() { | ||
149 | let <|>var_name = bar(1 + 1); | ||
150 | var_name | ||
151 | }", | ||
152 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
153 | ); | ||
154 | } | ||
155 | |||
156 | } | ||
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs deleted file mode 100644 index 7615f37a6..000000000 --- a/crates/ra_editor/src/code_actions.rs +++ /dev/null | |||
@@ -1,415 +0,0 @@ | |||
1 | use join_to_string::join; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | algo::{find_covering_node, find_leaf_at_offset}, | ||
5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, | ||
6 | Direction, SourceFileNode, | ||
7 | SyntaxKind::{COMMA, WHITESPACE, COMMENT, VISIBILITY, FN_KW, MOD_KW, STRUCT_KW, ENUM_KW, TRAIT_KW, FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF}, | ||
8 | SyntaxNodeRef, TextRange, TextUnit, | ||
9 | }; | ||
10 | |||
11 | use crate::{find_node_at_offset, TextEdit, TextEditBuilder}; | ||
12 | |||
13 | #[derive(Debug)] | ||
14 | pub struct LocalEdit { | ||
15 | pub label: String, | ||
16 | pub edit: TextEdit, | ||
17 | pub cursor_position: Option<TextUnit>, | ||
18 | } | ||
19 | |||
20 | pub fn flip_comma<'a>( | ||
21 | file: &'a SourceFileNode, | ||
22 | offset: TextUnit, | ||
23 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
24 | let syntax = file.syntax(); | ||
25 | |||
26 | let comma = find_leaf_at_offset(syntax, offset).find(|leaf| leaf.kind() == COMMA)?; | ||
27 | let prev = non_trivia_sibling(comma, Direction::Prev)?; | ||
28 | let next = non_trivia_sibling(comma, Direction::Next)?; | ||
29 | Some(move || { | ||
30 | let mut edit = TextEditBuilder::new(); | ||
31 | edit.replace(prev.range(), next.text().to_string()); | ||
32 | edit.replace(next.range(), prev.text().to_string()); | ||
33 | LocalEdit { | ||
34 | label: "flip comma".to_string(), | ||
35 | edit: edit.finish(), | ||
36 | cursor_position: None, | ||
37 | } | ||
38 | }) | ||
39 | } | ||
40 | |||
41 | pub fn add_derive<'a>( | ||
42 | file: &'a SourceFileNode, | ||
43 | offset: TextUnit, | ||
44 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
45 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | ||
46 | let node_start = derive_insertion_offset(nominal)?; | ||
47 | return Some(move || { | ||
48 | let derive_attr = nominal | ||
49 | .attrs() | ||
50 | .filter_map(|x| x.as_call()) | ||
51 | .filter(|(name, _arg)| name == "derive") | ||
52 | .map(|(_name, arg)| arg) | ||
53 | .next(); | ||
54 | let mut edit = TextEditBuilder::new(); | ||
55 | let offset = match derive_attr { | ||
56 | None => { | ||
57 | edit.insert(node_start, "#[derive()]\n".to_string()); | ||
58 | node_start + TextUnit::of_str("#[derive(") | ||
59 | } | ||
60 | Some(tt) => tt.syntax().range().end() - TextUnit::of_char(')'), | ||
61 | }; | ||
62 | LocalEdit { | ||
63 | label: "add `#[derive]`".to_string(), | ||
64 | edit: edit.finish(), | ||
65 | cursor_position: Some(offset), | ||
66 | } | ||
67 | }); | ||
68 | |||
69 | // Insert `derive` after doc comments. | ||
70 | fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> { | ||
71 | let non_ws_child = nominal | ||
72 | .syntax() | ||
73 | .children() | ||
74 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
75 | Some(non_ws_child.range().start()) | ||
76 | } | ||
77 | } | ||
78 | |||
79 | pub fn add_impl<'a>( | ||
80 | file: &'a SourceFileNode, | ||
81 | offset: TextUnit, | ||
82 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
83 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | ||
84 | let name = nominal.name()?; | ||
85 | |||
86 | Some(move || { | ||
87 | let type_params = nominal.type_param_list(); | ||
88 | let mut edit = TextEditBuilder::new(); | ||
89 | let start_offset = nominal.syntax().range().end(); | ||
90 | let mut buf = String::new(); | ||
91 | buf.push_str("\n\nimpl"); | ||
92 | if let Some(type_params) = type_params { | ||
93 | type_params.syntax().text().push_to(&mut buf); | ||
94 | } | ||
95 | buf.push_str(" "); | ||
96 | buf.push_str(name.text().as_str()); | ||
97 | if let Some(type_params) = type_params { | ||
98 | let lifetime_params = type_params | ||
99 | .lifetime_params() | ||
100 | .filter_map(|it| it.lifetime()) | ||
101 | .map(|it| it.text()); | ||
102 | let type_params = type_params | ||
103 | .type_params() | ||
104 | .filter_map(|it| it.name()) | ||
105 | .map(|it| it.text()); | ||
106 | join(lifetime_params.chain(type_params)) | ||
107 | .surround_with("<", ">") | ||
108 | .to_buf(&mut buf); | ||
109 | } | ||
110 | buf.push_str(" {\n"); | ||
111 | let offset = start_offset + TextUnit::of_str(&buf); | ||
112 | buf.push_str("\n}"); | ||
113 | edit.insert(start_offset, buf); | ||
114 | LocalEdit { | ||
115 | label: "add impl".to_string(), | ||
116 | edit: edit.finish(), | ||
117 | cursor_position: Some(offset), | ||
118 | } | ||
119 | }) | ||
120 | } | ||
121 | |||
122 | pub fn introduce_variable<'a>( | ||
123 | file: &'a SourceFileNode, | ||
124 | range: TextRange, | ||
125 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
126 | let node = find_covering_node(file.syntax(), range); | ||
127 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | ||
128 | |||
129 | let anchor_stmt = anchor_stmt(expr)?; | ||
130 | let indent = anchor_stmt.prev_sibling()?; | ||
131 | if indent.kind() != WHITESPACE { | ||
132 | return None; | ||
133 | } | ||
134 | return Some(move || { | ||
135 | let mut buf = String::new(); | ||
136 | let mut edit = TextEditBuilder::new(); | ||
137 | |||
138 | buf.push_str("let var_name = "); | ||
139 | expr.syntax().text().push_to(&mut buf); | ||
140 | let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) { | ||
141 | Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax()) | ||
142 | } else { | ||
143 | false | ||
144 | }; | ||
145 | if is_full_stmt { | ||
146 | edit.replace(expr.syntax().range(), buf); | ||
147 | } else { | ||
148 | buf.push_str(";"); | ||
149 | indent.text().push_to(&mut buf); | ||
150 | edit.replace(expr.syntax().range(), "var_name".to_string()); | ||
151 | edit.insert(anchor_stmt.range().start(), buf); | ||
152 | } | ||
153 | let cursor_position = anchor_stmt.range().start() + TextUnit::of_str("let "); | ||
154 | LocalEdit { | ||
155 | label: "introduce variable".to_string(), | ||
156 | edit: edit.finish(), | ||
157 | cursor_position: Some(cursor_position), | ||
158 | } | ||
159 | }); | ||
160 | |||
161 | /// Statement or last in the block expression, which will follow | ||
162 | /// the freshly introduced var. | ||
163 | fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { | ||
164 | expr.syntax().ancestors().find(|&node| { | ||
165 | if ast::Stmt::cast(node).is_some() { | ||
166 | return true; | ||
167 | } | ||
168 | if let Some(expr) = node | ||
169 | .parent() | ||
170 | .and_then(ast::Block::cast) | ||
171 | .and_then(|it| it.expr()) | ||
172 | { | ||
173 | if expr.syntax() == node { | ||
174 | return true; | ||
175 | } | ||
176 | } | ||
177 | false | ||
178 | }) | ||
179 | } | ||
180 | } | ||
181 | |||
182 | pub fn make_pub_crate<'a>( | ||
183 | file: &'a SourceFileNode, | ||
184 | offset: TextUnit, | ||
185 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | ||
186 | let syntax = file.syntax(); | ||
187 | |||
188 | let keyword = find_leaf_at_offset(syntax, offset).find(|leaf| match leaf.kind() { | ||
189 | FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true, | ||
190 | _ => false, | ||
191 | })?; | ||
192 | let parent = keyword.parent()?; | ||
193 | let def_kws = vec![FN_DEF, MODULE, STRUCT_DEF, ENUM_DEF, TRAIT_DEF]; | ||
194 | let node_start = parent.range().start(); | ||
195 | Some(move || { | ||
196 | let mut edit = TextEditBuilder::new(); | ||
197 | |||
198 | if !def_kws.iter().any(|&def_kw| def_kw == parent.kind()) | ||
199 | || parent.children().any(|child| child.kind() == VISIBILITY) | ||
200 | { | ||
201 | return LocalEdit { | ||
202 | label: "make pub crate".to_string(), | ||
203 | edit: edit.finish(), | ||
204 | cursor_position: Some(offset), | ||
205 | }; | ||
206 | } | ||
207 | |||
208 | edit.insert(node_start, "pub(crate) ".to_string()); | ||
209 | LocalEdit { | ||
210 | label: "make pub crate".to_string(), | ||
211 | edit: edit.finish(), | ||
212 | cursor_position: Some(node_start), | ||
213 | } | ||
214 | }) | ||
215 | } | ||
216 | |||
217 | fn non_trivia_sibling(node: SyntaxNodeRef, direction: Direction) -> Option<SyntaxNodeRef> { | ||
218 | node.siblings(direction) | ||
219 | .skip(1) | ||
220 | .find(|node| !node.kind().is_trivia()) | ||
221 | } | ||
222 | |||
223 | #[cfg(test)] | ||
224 | mod tests { | ||
225 | use super::*; | ||
226 | use crate::test_utils::{check_action, check_action_range}; | ||
227 | |||
228 | #[test] | ||
229 | fn test_swap_comma() { | ||
230 | check_action( | ||
231 | "fn foo(x: i32,<|> y: Result<(), ()>) {}", | ||
232 | "fn foo(y: Result<(), ()>,<|> x: i32) {}", | ||
233 | |file, off| flip_comma(file, off).map(|f| f()), | ||
234 | ) | ||
235 | } | ||
236 | |||
237 | #[test] | ||
238 | fn add_derive_new() { | ||
239 | check_action( | ||
240 | "struct Foo { a: i32, <|>}", | ||
241 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
242 | |file, off| add_derive(file, off).map(|f| f()), | ||
243 | ); | ||
244 | check_action( | ||
245 | "struct Foo { <|> a: i32, }", | ||
246 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | ||
247 | |file, off| add_derive(file, off).map(|f| f()), | ||
248 | ); | ||
249 | } | ||
250 | |||
251 | #[test] | ||
252 | fn add_derive_existing() { | ||
253 | check_action( | ||
254 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | ||
255 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | ||
256 | |file, off| add_derive(file, off).map(|f| f()), | ||
257 | ); | ||
258 | } | ||
259 | |||
260 | #[test] | ||
261 | fn add_derive_new_with_doc_comment() { | ||
262 | check_action( | ||
263 | " | ||
264 | /// `Foo` is a pretty important struct. | ||
265 | /// It does stuff. | ||
266 | struct Foo { a: i32<|>, } | ||
267 | ", | ||
268 | " | ||
269 | /// `Foo` is a pretty important struct. | ||
270 | /// It does stuff. | ||
271 | #[derive(<|>)] | ||
272 | struct Foo { a: i32, } | ||
273 | ", | ||
274 | |file, off| add_derive(file, off).map(|f| f()), | ||
275 | ); | ||
276 | } | ||
277 | |||
278 | #[test] | ||
279 | fn test_add_impl() { | ||
280 | check_action( | ||
281 | "struct Foo {<|>}\n", | ||
282 | "struct Foo {}\n\nimpl Foo {\n<|>\n}\n", | ||
283 | |file, off| add_impl(file, off).map(|f| f()), | ||
284 | ); | ||
285 | check_action( | ||
286 | "struct Foo<T: Clone> {<|>}", | ||
287 | "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}", | ||
288 | |file, off| add_impl(file, off).map(|f| f()), | ||
289 | ); | ||
290 | check_action( | ||
291 | "struct Foo<'a, T: Foo<'a>> {<|>}", | ||
292 | "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", | ||
293 | |file, off| add_impl(file, off).map(|f| f()), | ||
294 | ); | ||
295 | } | ||
296 | |||
297 | #[test] | ||
298 | fn test_introduce_var_simple() { | ||
299 | check_action_range( | ||
300 | " | ||
301 | fn foo() { | ||
302 | foo(<|>1 + 1<|>); | ||
303 | }", | ||
304 | " | ||
305 | fn foo() { | ||
306 | let <|>var_name = 1 + 1; | ||
307 | foo(var_name); | ||
308 | }", | ||
309 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn test_introduce_var_expr_stmt() { | ||
315 | check_action_range( | ||
316 | " | ||
317 | fn foo() { | ||
318 | <|>1 + 1<|>; | ||
319 | }", | ||
320 | " | ||
321 | fn foo() { | ||
322 | let <|>var_name = 1 + 1; | ||
323 | }", | ||
324 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
325 | ); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn test_introduce_var_part_of_expr_stmt() { | ||
330 | check_action_range( | ||
331 | " | ||
332 | fn foo() { | ||
333 | <|>1<|> + 1; | ||
334 | }", | ||
335 | " | ||
336 | fn foo() { | ||
337 | let <|>var_name = 1; | ||
338 | var_name + 1; | ||
339 | }", | ||
340 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
341 | ); | ||
342 | } | ||
343 | |||
344 | #[test] | ||
345 | fn test_introduce_var_last_expr() { | ||
346 | check_action_range( | ||
347 | " | ||
348 | fn foo() { | ||
349 | bar(<|>1 + 1<|>) | ||
350 | }", | ||
351 | " | ||
352 | fn foo() { | ||
353 | let <|>var_name = 1 + 1; | ||
354 | bar(var_name) | ||
355 | }", | ||
356 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
357 | ); | ||
358 | } | ||
359 | |||
360 | #[test] | ||
361 | fn test_introduce_var_last_full_expr() { | ||
362 | check_action_range( | ||
363 | " | ||
364 | fn foo() { | ||
365 | <|>bar(1 + 1)<|> | ||
366 | }", | ||
367 | " | ||
368 | fn foo() { | ||
369 | let <|>var_name = bar(1 + 1); | ||
370 | var_name | ||
371 | }", | ||
372 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
373 | ); | ||
374 | } | ||
375 | |||
376 | #[test] | ||
377 | fn test_make_pub_crate() { | ||
378 | check_action( | ||
379 | "<|>fn foo() {}", | ||
380 | "<|>pub(crate) fn foo() {}", | ||
381 | |file, off| make_pub_crate(file, off).map(|f| f()), | ||
382 | ); | ||
383 | check_action( | ||
384 | "f<|>n foo() {}", | ||
385 | "<|>pub(crate) fn foo() {}", | ||
386 | |file, off| make_pub_crate(file, off).map(|f| f()), | ||
387 | ); | ||
388 | check_action( | ||
389 | "<|>struct Foo {}", | ||
390 | "<|>pub(crate) struct Foo {}", | ||
391 | |file, off| make_pub_crate(file, off).map(|f| f()), | ||
392 | ); | ||
393 | check_action("<|>mod foo {}", "<|>pub(crate) mod foo {}", |file, off| { | ||
394 | make_pub_crate(file, off).map(|f| f()) | ||
395 | }); | ||
396 | check_action( | ||
397 | "<|>trait Foo {}", | ||
398 | "<|>pub(crate) trait Foo {}", | ||
399 | |file, off| make_pub_crate(file, off).map(|f| f()), | ||
400 | ); | ||
401 | check_action("m<|>od {}", "<|>pub(crate) mod {}", |file, off| { | ||
402 | make_pub_crate(file, off).map(|f| f()) | ||
403 | }); | ||
404 | check_action( | ||
405 | "pub(crate) f<|>n foo() {}", | ||
406 | "pub(crate) f<|>n foo() {}", | ||
407 | |file, off| make_pub_crate(file, off).map(|f| f()), | ||
408 | ); | ||
409 | check_action( | ||
410 | "unsafe f<|>n foo() {}", | ||
411 | "<|>pub(crate) unsafe fn foo() {}", | ||
412 | |file, off| make_pub_crate(file, off).map(|f| f()), | ||
413 | ); | ||
414 | } | ||
415 | } | ||
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index bfc745e58..ac283e2e0 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | mod code_actions; | 1 | pub mod assists; |
2 | mod extend_selection; | 2 | mod extend_selection; |
3 | mod folding_ranges; | 3 | mod folding_ranges; |
4 | mod line_index; | 4 | mod line_index; |
@@ -10,7 +10,7 @@ mod typing; | |||
10 | mod diagnostics; | 10 | mod diagnostics; |
11 | 11 | ||
12 | pub use self::{ | 12 | pub use self::{ |
13 | code_actions::{add_derive, add_impl, flip_comma, introduce_variable, make_pub_crate, LocalEdit}, | 13 | assists::LocalEdit, |
14 | extend_selection::extend_selection, | 14 | extend_selection::extend_selection, |
15 | folding_ranges::{folding_ranges, Fold, FoldKind}, | 15 | folding_ranges::{folding_ranges, Fold, FoldKind}, |
16 | line_index::{LineCol, LineIndex}, | 16 | line_index::{LineCol, LineIndex}, |
@@ -19,7 +19,7 @@ pub use self::{ | |||
19 | typing::{join_lines, on_enter, on_eq_typed}, | 19 | typing::{join_lines, on_enter, on_eq_typed}, |
20 | diagnostics::diagnostics | 20 | diagnostics::diagnostics |
21 | }; | 21 | }; |
22 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 22 | use ra_text_edit::TextEditBuilder; |
23 | use ra_syntax::{ | 23 | use ra_syntax::{ |
24 | algo::find_leaf_at_offset, | 24 | algo::find_leaf_at_offset, |
25 | ast::{self, AstNode}, | 25 | ast::{self, AstNode}, |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 3e948800e..e968c9728 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -30,6 +30,12 @@ pub trait NameOwner<'a>: AstNode<'a> { | |||
30 | } | 30 | } |
31 | } | 31 | } |
32 | 32 | ||
33 | pub trait VisibilityOwner<'a>: AstNode<'a> { | ||
34 | fn visibility(self) -> Option<Visibility<'a>> { | ||
35 | child_opt(self) | ||
36 | } | ||
37 | } | ||
38 | |||
33 | pub trait LoopBodyOwner<'a>: AstNode<'a> { | 39 | pub trait LoopBodyOwner<'a>: AstNode<'a> { |
34 | fn loop_body(self) -> Option<Block<'a>> { | 40 | fn loop_body(self) -> Option<Block<'a>> { |
35 | child_opt(self) | 41 | child_opt(self) |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index c5ac90a62..c619fc130 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -695,6 +695,7 @@ impl<R: TreeRoot<RaTypes>> ConstDefNode<R> { | |||
695 | } | 695 | } |
696 | 696 | ||
697 | 697 | ||
698 | impl<'a> ast::VisibilityOwner<'a> for ConstDef<'a> {} | ||
698 | impl<'a> ast::NameOwner<'a> for ConstDef<'a> {} | 699 | impl<'a> ast::NameOwner<'a> for ConstDef<'a> {} |
699 | impl<'a> ast::TypeParamsOwner<'a> for ConstDef<'a> {} | 700 | impl<'a> ast::TypeParamsOwner<'a> for ConstDef<'a> {} |
700 | impl<'a> ast::AttrsOwner<'a> for ConstDef<'a> {} | 701 | impl<'a> ast::AttrsOwner<'a> for ConstDef<'a> {} |
@@ -810,6 +811,7 @@ impl<R: TreeRoot<RaTypes>> EnumDefNode<R> { | |||
810 | } | 811 | } |
811 | 812 | ||
812 | 813 | ||
814 | impl<'a> ast::VisibilityOwner<'a> for EnumDef<'a> {} | ||
813 | impl<'a> ast::NameOwner<'a> for EnumDef<'a> {} | 815 | impl<'a> ast::NameOwner<'a> for EnumDef<'a> {} |
814 | impl<'a> ast::TypeParamsOwner<'a> for EnumDef<'a> {} | 816 | impl<'a> ast::TypeParamsOwner<'a> for EnumDef<'a> {} |
815 | impl<'a> ast::AttrsOwner<'a> for EnumDef<'a> {} | 817 | impl<'a> ast::AttrsOwner<'a> for EnumDef<'a> {} |
@@ -1213,6 +1215,7 @@ impl<R: TreeRoot<RaTypes>> FnDefNode<R> { | |||
1213 | } | 1215 | } |
1214 | 1216 | ||
1215 | 1217 | ||
1218 | impl<'a> ast::VisibilityOwner<'a> for FnDef<'a> {} | ||
1216 | impl<'a> ast::NameOwner<'a> for FnDef<'a> {} | 1219 | impl<'a> ast::NameOwner<'a> for FnDef<'a> {} |
1217 | impl<'a> ast::TypeParamsOwner<'a> for FnDef<'a> {} | 1220 | impl<'a> ast::TypeParamsOwner<'a> for FnDef<'a> {} |
1218 | impl<'a> ast::AttrsOwner<'a> for FnDef<'a> {} | 1221 | impl<'a> ast::AttrsOwner<'a> for FnDef<'a> {} |
@@ -2136,6 +2139,7 @@ impl<R: TreeRoot<RaTypes>> ModuleNode<R> { | |||
2136 | } | 2139 | } |
2137 | 2140 | ||
2138 | 2141 | ||
2142 | impl<'a> ast::VisibilityOwner<'a> for Module<'a> {} | ||
2139 | impl<'a> ast::NameOwner<'a> for Module<'a> {} | 2143 | impl<'a> ast::NameOwner<'a> for Module<'a> {} |
2140 | impl<'a> ast::AttrsOwner<'a> for Module<'a> {} | 2144 | impl<'a> ast::AttrsOwner<'a> for Module<'a> {} |
2141 | impl<'a> ast::DocCommentsOwner<'a> for Module<'a> {} | 2145 | impl<'a> ast::DocCommentsOwner<'a> for Module<'a> {} |
@@ -2351,6 +2355,7 @@ impl<R: TreeRoot<RaTypes>> NamedFieldDefNode<R> { | |||
2351 | } | 2355 | } |
2352 | 2356 | ||
2353 | 2357 | ||
2358 | impl<'a> ast::VisibilityOwner<'a> for NamedFieldDef<'a> {} | ||
2354 | impl<'a> ast::NameOwner<'a> for NamedFieldDef<'a> {} | 2359 | impl<'a> ast::NameOwner<'a> for NamedFieldDef<'a> {} |
2355 | impl<'a> ast::AttrsOwner<'a> for NamedFieldDef<'a> {} | 2360 | impl<'a> ast::AttrsOwner<'a> for NamedFieldDef<'a> {} |
2356 | impl<'a> NamedFieldDef<'a> { | 2361 | impl<'a> NamedFieldDef<'a> { |
@@ -3082,6 +3087,7 @@ impl<R: TreeRoot<RaTypes>> PosFieldNode<R> { | |||
3082 | } | 3087 | } |
3083 | 3088 | ||
3084 | 3089 | ||
3090 | impl<'a> ast::VisibilityOwner<'a> for PosField<'a> {} | ||
3085 | impl<'a> ast::AttrsOwner<'a> for PosField<'a> {} | 3091 | impl<'a> ast::AttrsOwner<'a> for PosField<'a> {} |
3086 | impl<'a> PosField<'a> { | 3092 | impl<'a> PosField<'a> { |
3087 | pub fn type_ref(self) -> Option<TypeRef<'a>> { | 3093 | pub fn type_ref(self) -> Option<TypeRef<'a>> { |
@@ -3639,6 +3645,7 @@ impl<R: TreeRoot<RaTypes>> StaticDefNode<R> { | |||
3639 | } | 3645 | } |
3640 | 3646 | ||
3641 | 3647 | ||
3648 | impl<'a> ast::VisibilityOwner<'a> for StaticDef<'a> {} | ||
3642 | impl<'a> ast::NameOwner<'a> for StaticDef<'a> {} | 3649 | impl<'a> ast::NameOwner<'a> for StaticDef<'a> {} |
3643 | impl<'a> ast::TypeParamsOwner<'a> for StaticDef<'a> {} | 3650 | impl<'a> ast::TypeParamsOwner<'a> for StaticDef<'a> {} |
3644 | impl<'a> ast::AttrsOwner<'a> for StaticDef<'a> {} | 3651 | impl<'a> ast::AttrsOwner<'a> for StaticDef<'a> {} |
@@ -3742,6 +3749,7 @@ impl<R: TreeRoot<RaTypes>> StructDefNode<R> { | |||
3742 | } | 3749 | } |
3743 | 3750 | ||
3744 | 3751 | ||
3752 | impl<'a> ast::VisibilityOwner<'a> for StructDef<'a> {} | ||
3745 | impl<'a> ast::NameOwner<'a> for StructDef<'a> {} | 3753 | impl<'a> ast::NameOwner<'a> for StructDef<'a> {} |
3746 | impl<'a> ast::TypeParamsOwner<'a> for StructDef<'a> {} | 3754 | impl<'a> ast::TypeParamsOwner<'a> for StructDef<'a> {} |
3747 | impl<'a> ast::AttrsOwner<'a> for StructDef<'a> {} | 3755 | impl<'a> ast::AttrsOwner<'a> for StructDef<'a> {} |
@@ -3902,6 +3910,7 @@ impl<R: TreeRoot<RaTypes>> TraitDefNode<R> { | |||
3902 | } | 3910 | } |
3903 | 3911 | ||
3904 | 3912 | ||
3913 | impl<'a> ast::VisibilityOwner<'a> for TraitDef<'a> {} | ||
3905 | impl<'a> ast::NameOwner<'a> for TraitDef<'a> {} | 3914 | impl<'a> ast::NameOwner<'a> for TraitDef<'a> {} |
3906 | impl<'a> ast::AttrsOwner<'a> for TraitDef<'a> {} | 3915 | impl<'a> ast::AttrsOwner<'a> for TraitDef<'a> {} |
3907 | impl<'a> ast::DocCommentsOwner<'a> for TraitDef<'a> {} | 3916 | impl<'a> ast::DocCommentsOwner<'a> for TraitDef<'a> {} |
@@ -4135,6 +4144,7 @@ impl<R: TreeRoot<RaTypes>> TypeDefNode<R> { | |||
4135 | } | 4144 | } |
4136 | 4145 | ||
4137 | 4146 | ||
4147 | impl<'a> ast::VisibilityOwner<'a> for TypeDef<'a> {} | ||
4138 | impl<'a> ast::NameOwner<'a> for TypeDef<'a> {} | 4148 | impl<'a> ast::NameOwner<'a> for TypeDef<'a> {} |
4139 | impl<'a> ast::TypeParamsOwner<'a> for TypeDef<'a> {} | 4149 | impl<'a> ast::TypeParamsOwner<'a> for TypeDef<'a> {} |
4140 | impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {} | 4150 | impl<'a> ast::AttrsOwner<'a> for TypeDef<'a> {} |
@@ -4409,6 +4419,43 @@ impl<'a> UseTreeList<'a> { | |||
4409 | } | 4419 | } |
4410 | } | 4420 | } |
4411 | 4421 | ||
4422 | // Visibility | ||
4423 | #[derive(Debug, Clone, Copy,)] | ||
4424 | pub struct VisibilityNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
4425 | pub(crate) syntax: SyntaxNode<R>, | ||
4426 | } | ||
4427 | pub type Visibility<'a> = VisibilityNode<RefRoot<'a>>; | ||
4428 | |||
4429 | impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<VisibilityNode<R1>> for VisibilityNode<R2> { | ||
4430 | fn eq(&self, other: &VisibilityNode<R1>) -> bool { self.syntax == other.syntax } | ||
4431 | } | ||
4432 | impl<R: TreeRoot<RaTypes>> Eq for VisibilityNode<R> {} | ||
4433 | impl<R: TreeRoot<RaTypes>> Hash for VisibilityNode<R> { | ||
4434 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
4435 | } | ||
4436 | |||
4437 | impl<'a> AstNode<'a> for Visibility<'a> { | ||
4438 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
4439 | match syntax.kind() { | ||
4440 | VISIBILITY => Some(Visibility { syntax }), | ||
4441 | _ => None, | ||
4442 | } | ||
4443 | } | ||
4444 | fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } | ||
4445 | } | ||
4446 | |||
4447 | impl<R: TreeRoot<RaTypes>> VisibilityNode<R> { | ||
4448 | pub fn borrowed(&self) -> Visibility { | ||
4449 | VisibilityNode { syntax: self.syntax.borrowed() } | ||
4450 | } | ||
4451 | pub fn owned(&self) -> VisibilityNode { | ||
4452 | VisibilityNode { syntax: self.syntax.owned() } | ||
4453 | } | ||
4454 | } | ||
4455 | |||
4456 | |||
4457 | impl<'a> Visibility<'a> {} | ||
4458 | |||
4412 | // WhereClause | 4459 | // WhereClause |
4413 | #[derive(Debug, Clone, Copy,)] | 4460 | #[derive(Debug, Clone, Copy,)] |
4414 | pub struct WhereClauseNode<R: TreeRoot<RaTypes> = OwnedRoot> { | 4461 | pub struct WhereClauseNode<R: TreeRoot<RaTypes> = OwnedRoot> { |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index aab4839a9..2abb9da61 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -247,6 +247,7 @@ Grammar( | |||
247 | ), | 247 | ), |
248 | "FnDef": ( | 248 | "FnDef": ( |
249 | traits: [ | 249 | traits: [ |
250 | "VisibilityOwner", | ||
250 | "NameOwner", | 251 | "NameOwner", |
251 | "TypeParamsOwner", | 252 | "TypeParamsOwner", |
252 | "AttrsOwner", | 253 | "AttrsOwner", |
@@ -257,6 +258,7 @@ Grammar( | |||
257 | "RetType": (options: ["TypeRef"]), | 258 | "RetType": (options: ["TypeRef"]), |
258 | "StructDef": ( | 259 | "StructDef": ( |
259 | traits: [ | 260 | traits: [ |
261 | "VisibilityOwner", | ||
260 | "NameOwner", | 262 | "NameOwner", |
261 | "TypeParamsOwner", | 263 | "TypeParamsOwner", |
262 | "AttrsOwner", | 264 | "AttrsOwner", |
@@ -264,10 +266,11 @@ Grammar( | |||
264 | ] | 266 | ] |
265 | ), | 267 | ), |
266 | "NamedFieldDefList": (collections: [["fields", "NamedFieldDef"]]), | 268 | "NamedFieldDefList": (collections: [["fields", "NamedFieldDef"]]), |
267 | "NamedFieldDef": ( traits: ["NameOwner", "AttrsOwner"], options: ["TypeRef"] ), | 269 | "NamedFieldDef": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner"], options: ["TypeRef"] ), |
268 | "PosFieldList": (collections: [["fields", "PosField"]]), | 270 | "PosFieldList": (collections: [["fields", "PosField"]]), |
269 | "PosField": ( traits: ["AttrsOwner"], options: ["TypeRef"]), | 271 | "PosField": ( traits: ["VisibilityOwner", "AttrsOwner"], options: ["TypeRef"]), |
270 | "EnumDef": ( traits: [ | 272 | "EnumDef": ( traits: [ |
273 | "VisibilityOwner", | ||
271 | "NameOwner", | 274 | "NameOwner", |
272 | "TypeParamsOwner", | 275 | "TypeParamsOwner", |
273 | "AttrsOwner", | 276 | "AttrsOwner", |
@@ -275,27 +278,30 @@ Grammar( | |||
275 | ], options: [["variant_list", "EnumVariantList"]] ), | 278 | ], options: [["variant_list", "EnumVariantList"]] ), |
276 | "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), | 279 | "EnumVariantList": ( collections: [["variants", "EnumVariant"]] ), |
277 | "EnumVariant": ( traits: ["NameOwner"], options: ["Expr"] ), | 280 | "EnumVariant": ( traits: ["NameOwner"], options: ["Expr"] ), |
278 | "TraitDef": ( traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner"] ), | 281 | "TraitDef": ( traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner"] ), |
279 | "Module": ( | 282 | "Module": ( |
280 | traits: ["NameOwner", "AttrsOwner", "DocCommentsOwner" ], | 283 | traits: ["VisibilityOwner", "NameOwner", "AttrsOwner", "DocCommentsOwner" ], |
281 | options: [ "ItemList" ] | 284 | options: [ "ItemList" ] |
282 | ), | 285 | ), |
283 | "ItemList": ( | 286 | "ItemList": ( |
284 | traits: [ "FnDefOwner", "ModuleItemOwner" ], | 287 | traits: [ "FnDefOwner", "ModuleItemOwner" ], |
285 | ), | 288 | ), |
286 | "ConstDef": ( traits: [ | 289 | "ConstDef": ( traits: [ |
290 | "VisibilityOwner", | ||
287 | "NameOwner", | 291 | "NameOwner", |
288 | "TypeParamsOwner", | 292 | "TypeParamsOwner", |
289 | "AttrsOwner", | 293 | "AttrsOwner", |
290 | "DocCommentsOwner" | 294 | "DocCommentsOwner" |
291 | ] ), | 295 | ] ), |
292 | "StaticDef": ( traits: [ | 296 | "StaticDef": ( traits: [ |
297 | "VisibilityOwner", | ||
293 | "NameOwner", | 298 | "NameOwner", |
294 | "TypeParamsOwner", | 299 | "TypeParamsOwner", |
295 | "AttrsOwner", | 300 | "AttrsOwner", |
296 | "DocCommentsOwner" | 301 | "DocCommentsOwner" |
297 | ] ), | 302 | ] ), |
298 | "TypeDef": ( traits: [ | 303 | "TypeDef": ( traits: [ |
304 | "VisibilityOwner", | ||
299 | "NameOwner", | 305 | "NameOwner", |
300 | "TypeParamsOwner", | 306 | "TypeParamsOwner", |
301 | "AttrsOwner", | 307 | "AttrsOwner", |
@@ -482,6 +488,7 @@ Grammar( | |||
482 | ], | 488 | ], |
483 | ), | 489 | ), |
484 | 490 | ||
491 | "Visibility": (), | ||
485 | "Name": (), | 492 | "Name": (), |
486 | "NameRef": (), | 493 | "NameRef": (), |
487 | "MacroCall": ( options: [ "TokenTree", "Path" ] ), | 494 | "MacroCall": ( options: [ "TokenTree", "Path" ] ), |