aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src/code_actions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src/code_actions.rs')
-rw-r--r--crates/ra_editor/src/code_actions.rs90
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
67pub fn add_impl<'a>( 76pub 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.
226struct Foo { a: i32<|>, }
227 ",
228 "
229/// `Foo` is a pretty important struct.
230/// It does stuff.
231#[derive(<|>)]
232struct 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 "
225fn foo() { 261fn 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 "
241fn foo() { 277fn 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 "
292fn foo() {
293 <|>1<|> + 1;
294}",
295 "
296fn 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 "
256fn foo() { 308fn 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 "
324fn foo() {
325 <|>bar(1 + 1)<|>
326}",
327 "
328fn 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}