diff options
Diffstat (limited to 'crates/ra_editor/src/code_actions.rs')
-rw-r--r-- | crates/ra_editor/src/code_actions.rs | 90 |
1 files changed, 79 insertions, 11 deletions
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs index 0139b19d3..6979251d1 100644 --- a/crates/ra_editor/src/code_actions.rs +++ b/crates/ra_editor/src/code_actions.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | algo::{find_covering_node, find_leaf_at_offset}, | 4 | algo::{find_covering_node, find_leaf_at_offset}, |
5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, | 5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, |
6 | Direction, SourceFileNode, | 6 | Direction, SourceFileNode, |
7 | SyntaxKind::{COMMA, WHITESPACE}, | 7 | SyntaxKind::{COMMA, WHITESPACE, COMMENT}, |
8 | SyntaxNodeRef, TextRange, TextUnit, | 8 | SyntaxNodeRef, TextRange, TextUnit, |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -41,7 +41,8 @@ pub fn add_derive<'a>( | |||
41 | offset: TextUnit, | 41 | offset: TextUnit, |
42 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 42 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { |
43 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | 43 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; |
44 | Some(move || { | 44 | let node_start = derive_insertion_offset(nominal)?; |
45 | return Some(move || { | ||
45 | let derive_attr = nominal | 46 | let derive_attr = nominal |
46 | .attrs() | 47 | .attrs() |
47 | .filter_map(|x| x.as_call()) | 48 | .filter_map(|x| x.as_call()) |
@@ -51,7 +52,6 @@ pub fn add_derive<'a>( | |||
51 | let mut edit = EditBuilder::new(); | 52 | let mut edit = EditBuilder::new(); |
52 | let offset = match derive_attr { | 53 | let offset = match derive_attr { |
53 | None => { | 54 | None => { |
54 | let node_start = nominal.syntax().range().start(); | ||
55 | edit.insert(node_start, "#[derive()]\n".to_string()); | 55 | edit.insert(node_start, "#[derive()]\n".to_string()); |
56 | node_start + TextUnit::of_str("#[derive(") | 56 | node_start + TextUnit::of_str("#[derive(") |
57 | } | 57 | } |
@@ -61,7 +61,16 @@ pub fn add_derive<'a>( | |||
61 | edit: edit.finish(), | 61 | edit: edit.finish(), |
62 | cursor_position: Some(offset), | 62 | cursor_position: Some(offset), |
63 | } | 63 | } |
64 | }) | 64 | }); |
65 | |||
66 | // Insert `derive` after doc comments. | ||
67 | fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> { | ||
68 | let non_ws_child = nominal | ||
69 | .syntax() | ||
70 | .children() | ||
71 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
72 | Some(non_ws_child.range().start()) | ||
73 | } | ||
65 | } | 74 | } |
66 | 75 | ||
67 | pub fn add_impl<'a>( | 76 | pub fn add_impl<'a>( |
@@ -113,7 +122,7 @@ pub fn introduce_variable<'a>( | |||
113 | let node = find_covering_node(file.syntax(), range); | 122 | let node = find_covering_node(file.syntax(), range); |
114 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 123 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; |
115 | 124 | ||
116 | let anchor_stmt = ahchor_stmt(expr)?; | 125 | let anchor_stmt = anchor_stmt(expr)?; |
117 | let indent = anchor_stmt.prev_sibling()?; | 126 | let indent = anchor_stmt.prev_sibling()?; |
118 | if indent.kind() != WHITESPACE { | 127 | if indent.kind() != WHITESPACE { |
119 | return None; | 128 | return None; |
@@ -124,7 +133,12 @@ pub fn introduce_variable<'a>( | |||
124 | 133 | ||
125 | buf.push_str("let var_name = "); | 134 | buf.push_str("let var_name = "); |
126 | expr.syntax().text().push_to(&mut buf); | 135 | expr.syntax().text().push_to(&mut buf); |
127 | if expr.syntax().range().start() == anchor_stmt.range().start() { | 136 | let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) { |
137 | Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax()) | ||
138 | } else { | ||
139 | false | ||
140 | }; | ||
141 | if is_full_stmt { | ||
128 | edit.replace(expr.syntax().range(), buf); | 142 | edit.replace(expr.syntax().range(), buf); |
129 | } else { | 143 | } else { |
130 | buf.push_str(";"); | 144 | buf.push_str(";"); |
@@ -141,7 +155,7 @@ pub fn introduce_variable<'a>( | |||
141 | 155 | ||
142 | /// Statement or last in the block expression, which will follow | 156 | /// Statement or last in the block expression, which will follow |
143 | /// the freshly introduced var. | 157 | /// the freshly introduced var. |
144 | fn ahchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { | 158 | fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { |
145 | expr.syntax().ancestors().find(|&node| { | 159 | expr.syntax().ancestors().find(|&node| { |
146 | if ast::Stmt::cast(node).is_some() { | 160 | if ast::Stmt::cast(node).is_some() { |
147 | return true; | 161 | return true; |
@@ -181,7 +195,7 @@ mod tests { | |||
181 | } | 195 | } |
182 | 196 | ||
183 | #[test] | 197 | #[test] |
184 | fn test_add_derive() { | 198 | fn add_derive_new() { |
185 | check_action( | 199 | check_action( |
186 | "struct Foo { a: i32, <|>}", | 200 | "struct Foo { a: i32, <|>}", |
187 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 201 | "#[derive(<|>)]\nstruct Foo { a: i32, }", |
@@ -192,6 +206,10 @@ mod tests { | |||
192 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 206 | "#[derive(<|>)]\nstruct Foo { a: i32, }", |
193 | |file, off| add_derive(file, off).map(|f| f()), | 207 | |file, off| add_derive(file, off).map(|f| f()), |
194 | ); | 208 | ); |
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn add_derive_existing() { | ||
195 | check_action( | 213 | check_action( |
196 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | 214 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", |
197 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | 215 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", |
@@ -200,6 +218,24 @@ mod tests { | |||
200 | } | 218 | } |
201 | 219 | ||
202 | #[test] | 220 | #[test] |
221 | fn add_derive_new_with_doc_comment() { | ||
222 | check_action( | ||
223 | " | ||
224 | /// `Foo` is a pretty important struct. | ||
225 | /// It does stuff. | ||
226 | struct Foo { a: i32<|>, } | ||
227 | ", | ||
228 | " | ||
229 | /// `Foo` is a pretty important struct. | ||
230 | /// It does stuff. | ||
231 | #[derive(<|>)] | ||
232 | struct Foo { a: i32, } | ||
233 | ", | ||
234 | |file, off| add_derive(file, off).map(|f| f()), | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
203 | fn test_add_impl() { | 239 | fn test_add_impl() { |
204 | check_action( | 240 | check_action( |
205 | "struct Foo {<|>}\n", | 241 | "struct Foo {<|>}\n", |
@@ -219,7 +255,7 @@ mod tests { | |||
219 | } | 255 | } |
220 | 256 | ||
221 | #[test] | 257 | #[test] |
222 | fn test_intrdoduce_var_simple() { | 258 | fn test_introduce_var_simple() { |
223 | check_action_range( | 259 | check_action_range( |
224 | " | 260 | " |
225 | fn foo() { | 261 | fn foo() { |
@@ -235,7 +271,7 @@ fn foo() { | |||
235 | } | 271 | } |
236 | 272 | ||
237 | #[test] | 273 | #[test] |
238 | fn test_intrdoduce_var_expr_stmt() { | 274 | fn test_introduce_var_expr_stmt() { |
239 | check_action_range( | 275 | check_action_range( |
240 | " | 276 | " |
241 | fn foo() { | 277 | fn foo() { |
@@ -250,7 +286,23 @@ fn foo() { | |||
250 | } | 286 | } |
251 | 287 | ||
252 | #[test] | 288 | #[test] |
253 | fn test_intrdoduce_var_last_expr() { | 289 | fn test_introduce_var_part_of_expr_stmt() { |
290 | check_action_range( | ||
291 | " | ||
292 | fn foo() { | ||
293 | <|>1<|> + 1; | ||
294 | }", | ||
295 | " | ||
296 | fn foo() { | ||
297 | let <|>var_name = 1; | ||
298 | var_name + 1; | ||
299 | }", | ||
300 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
301 | ); | ||
302 | } | ||
303 | |||
304 | #[test] | ||
305 | fn test_introduce_var_last_expr() { | ||
254 | check_action_range( | 306 | check_action_range( |
255 | " | 307 | " |
256 | fn foo() { | 308 | fn foo() { |
@@ -265,4 +317,20 @@ fn foo() { | |||
265 | ); | 317 | ); |
266 | } | 318 | } |
267 | 319 | ||
320 | #[test] | ||
321 | fn test_introduce_var_last_full_expr() { | ||
322 | check_action_range( | ||
323 | " | ||
324 | fn foo() { | ||
325 | <|>bar(1 + 1)<|> | ||
326 | }", | ||
327 | " | ||
328 | fn foo() { | ||
329 | let <|>var_name = bar(1 + 1); | ||
330 | var_name | ||
331 | }", | ||
332 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
333 | ); | ||
334 | } | ||
335 | |||
268 | } | 336 | } |