aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_editor/src/completion.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_editor/src/completion.rs')
-rw-r--r--crates/ra_editor/src/completion.rs178
1 files changed, 114 insertions, 64 deletions
diff --git a/crates/ra_editor/src/completion.rs b/crates/ra_editor/src/completion.rs
index b6095dca9..86ef46ebd 100644
--- a/crates/ra_editor/src/completion.rs
+++ b/crates/ra_editor/src/completion.rs
@@ -1,17 +1,18 @@
1use rustc_hash::{FxHashMap, FxHashSet}; 1use rustc_hash::{FxHashMap, FxHashSet};
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 File, TextUnit, AstNode, SyntaxNodeRef, SyntaxKind::*, 4 algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx},
5 ast::{self, LoopBodyOwner, ModuleItemOwner}, 5 ast::{self, LoopBodyOwner, ModuleItemOwner},
6 algo::{
7 visit::{visitor, Visitor, visitor_ctx, VisitorCtx},
8 },
9 text_utils::is_subrange, 6 text_utils::is_subrange,
7 AstNode, File,
8 SyntaxKind::*,
9 SyntaxNodeRef, TextUnit,
10}; 10};
11 11
12use crate::{ 12use crate::{
13 AtomEdit, find_node_at_offset, 13 find_node_at_offset,
14 scope::{FnScopes, ModuleScope}, 14 scope::{FnScopes, ModuleScope},
15 AtomEdit,
15}; 16};
16 17
17#[derive(Debug)] 18#[derive(Debug)]
@@ -21,7 +22,7 @@ pub struct CompletionItem {
21 /// What string is used for filtering, defaults to label 22 /// What string is used for filtering, defaults to label
22 pub lookup: Option<String>, 23 pub lookup: Option<String>,
23 /// What is inserted, defaults to label 24 /// What is inserted, defaults to label
24 pub snippet: Option<String> 25 pub snippet: Option<String>,
25} 26}
26 27
27pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> { 28pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionItem>> {
@@ -40,7 +41,12 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
40 param_completions(name_ref.syntax(), &mut res); 41 param_completions(name_ref.syntax(), &mut res);
41 } 42 }
42 let name_range = name_ref.syntax().range(); 43 let name_range = name_ref.syntax().range();
43 let top_node = name_ref.syntax().ancestors().take_while(|it| it.range() == name_range).last().unwrap(); 44 let top_node = name_ref
45 .syntax()
46 .ancestors()
47 .take_while(|it| it.range() == name_range)
48 .last()
49 .unwrap();
44 match top_node.parent().map(|it| it.kind()) { 50 match top_node.parent().map(|it| it.kind()) {
45 Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res), 51 Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res),
46 _ => (), 52 _ => (),
@@ -68,21 +74,23 @@ fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<Completi
68 if let Some(items) = visitor() 74 if let Some(items) = visitor()
69 .visit::<ast::Root, _>(|it| Some(it.items())) 75 .visit::<ast::Root, _>(|it| Some(it.items()))
70 .visit::<ast::Module, _>(|it| Some(it.item_list()?.items())) 76 .visit::<ast::Module, _>(|it| Some(it.item_list()?.items()))
71 .accept(node) { 77 .accept(node)
78 {
72 if let Some(items) = items { 79 if let Some(items) = items {
73 let scope = ModuleScope::new(items); 80 let scope = ModuleScope::new(items);
74 acc.extend( 81 acc.extend(
75 scope.entries().iter() 82 scope
83 .entries()
84 .iter()
76 .filter(|entry| entry.syntax() != name_ref.syntax()) 85 .filter(|entry| entry.syntax() != name_ref.syntax())
77 .map(|entry| CompletionItem { 86 .map(|entry| CompletionItem {
78 label: entry.name().to_string(), 87 label: entry.name().to_string(),
79 lookup: None, 88 lookup: None,
80 snippet: None, 89 snippet: None,
81 }) 90 }),
82 ); 91 );
83 } 92 }
84 break; 93 break;
85
86 } else if !visited_fn { 94 } else if !visited_fn {
87 if let Some(fn_def) = ast::FnDef::cast(node) { 95 if let Some(fn_def) = ast::FnDef::cast(node) {
88 visited_fn = true; 96 visited_fn = true;
@@ -103,26 +111,34 @@ fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) {
103 .visit::<ast::ItemList, _>(process) 111 .visit::<ast::ItemList, _>(process)
104 .accept(node); 112 .accept(node);
105 } 113 }
106 params.into_iter() 114 params
115 .into_iter()
107 .filter_map(|(label, (count, param))| { 116 .filter_map(|(label, (count, param))| {
108 let lookup = param.pat()?.syntax().text().to_string(); 117 let lookup = param.pat()?.syntax().text().to_string();
109 if count < 2 { None } else { Some((label, lookup)) } 118 if count < 2 {
119 None
120 } else {
121 Some((label, lookup))
122 }
110 }) 123 })
111 .for_each(|(label, lookup)| { 124 .for_each(|(label, lookup)| {
112 acc.push(CompletionItem { 125 acc.push(CompletionItem {
113 label, lookup: Some(lookup), snippet: None 126 label,
127 lookup: Some(lookup),
128 snippet: None,
114 }) 129 })
115 }); 130 });
116 131
117 fn process<'a, N: ast::FnDefOwner<'a>>(node: N, params: &mut FxHashMap<String, (u32, ast::Param<'a>)>) { 132 fn process<'a, N: ast::FnDefOwner<'a>>(
133 node: N,
134 params: &mut FxHashMap<String, (u32, ast::Param<'a>)>,
135 ) {
118 node.functions() 136 node.functions()
119 .filter_map(|it| it.param_list()) 137 .filter_map(|it| it.param_list())
120 .flat_map(|it| it.params()) 138 .flat_map(|it| it.params())
121 .for_each(|param| { 139 .for_each(|param| {
122 let text = param.syntax().text().to_string(); 140 let text = param.syntax().text().to_string();
123 params.entry(text) 141 params.entry(text).or_insert((0, param)).0 += 1;
124 .or_insert((0, param))
125 .0 += 1;
126 }) 142 })
127 } 143 }
128} 144}
@@ -134,8 +150,12 @@ fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
134 } 150 }
135} 151}
136 152
137 153fn complete_expr_keywords(
138fn complete_expr_keywords(file: &File, fn_def: ast::FnDef, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) { 154 file: &File,
155 fn_def: ast::FnDef,
156 name_ref: ast::NameRef,
157 acc: &mut Vec<CompletionItem>,
158) {
139 acc.push(keyword("if", "if $0 {}")); 159 acc.push(keyword("if", "if $0 {}"));
140 acc.push(keyword("match", "match $0 {}")); 160 acc.push(keyword("match", "match $0 {}"));
141 acc.push(keyword("while", "while $0 {}")); 161 acc.push(keyword("while", "while $0 {}"));
@@ -186,9 +206,14 @@ fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<Complet
186 // return None; 206 // return None;
187 // } 207 // }
188 208
189 let is_stmt = match name_ref.syntax().ancestors().filter_map(ast::ExprStmt::cast).next() { 209 let is_stmt = match name_ref
210 .syntax()
211 .ancestors()
212 .filter_map(ast::ExprStmt::cast)
213 .next()
214 {
190 None => false, 215 None => false,
191 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range() 216 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
192 }; 217 };
193 let snip = match (is_stmt, fn_def.ret_type().is_some()) { 218 let snip = match (is_stmt, fn_def.ret_type().is_some()) {
194 (true, true) => "return $0;", 219 (true, true) => "return $0;",
@@ -209,39 +234,37 @@ fn keyword(kw: &str, snip: &str) -> CompletionItem {
209 234
210fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) { 235fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
211 acc.push(CompletionItem { 236 acc.push(CompletionItem {
212 label: "pd".to_string(), 237 label: "pd".to_string(),
213 lookup: None, 238 lookup: None,
214 snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()), 239 snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
215 } 240 });
216 );
217 acc.push(CompletionItem { 241 acc.push(CompletionItem {
218 label: "ppd".to_string(), 242 label: "ppd".to_string(),
219 lookup: None, 243 lookup: None,
220 snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()), 244 snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
221 } 245 });
222 );
223} 246}
224 247
225fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) { 248fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
226 acc.push(CompletionItem { 249 acc.push(CompletionItem {
227 label: "tfn".to_string(), 250 label: "tfn".to_string(),
228 lookup: None, 251 lookup: None,
229 snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()), 252 snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()),
230 } 253 });
231 );
232} 254}
233 255
234fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { 256fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
235 let mut shadowed = FxHashSet::default(); 257 let mut shadowed = FxHashSet::default();
236 acc.extend( 258 acc.extend(
237 scopes.scope_chain(name_ref.syntax()) 259 scopes
260 .scope_chain(name_ref.syntax())
238 .flat_map(|scope| scopes.entries(scope).iter()) 261 .flat_map(|scope| scopes.entries(scope).iter())
239 .filter(|entry| shadowed.insert(entry.name())) 262 .filter(|entry| shadowed.insert(entry.name()))
240 .map(|entry| CompletionItem { 263 .map(|entry| CompletionItem {
241 label: entry.name().to_string(), 264 label: entry.name().to_string(),
242 lookup: None, 265 lookup: None,
243 snippet: None, 266 snippet: None,
244 }) 267 }),
245 ); 268 );
246 if scopes.self_param.is_some() { 269 if scopes.self_param.is_some() {
247 acc.push(CompletionItem { 270 acc.push(CompletionItem {
@@ -281,20 +304,24 @@ mod tests {
281 304
282 #[test] 305 #[test]
283 fn test_completion_let_scope() { 306 fn test_completion_let_scope() {
284 check_scope_completion(r" 307 check_scope_completion(
308 r"
285 fn quux(x: i32) { 309 fn quux(x: i32) {
286 let y = 92; 310 let y = 92;
287 1 + <|>; 311 1 + <|>;
288 let z = (); 312 let z = ();
289 } 313 }
290 ", r#"[CompletionItem { label: "y", lookup: None, snippet: None }, 314 ",
315 r#"[CompletionItem { label: "y", lookup: None, snippet: None },
291 CompletionItem { label: "x", lookup: None, snippet: None }, 316 CompletionItem { label: "x", lookup: None, snippet: None },
292 CompletionItem { label: "quux", lookup: None, snippet: None }]"#); 317 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
318 );
293 } 319 }
294 320
295 #[test] 321 #[test]
296 fn test_completion_if_let_scope() { 322 fn test_completion_if_let_scope() {
297 check_scope_completion(r" 323 check_scope_completion(
324 r"
298 fn quux() { 325 fn quux() {
299 if let Some(x) = foo() { 326 if let Some(x) = foo() {
300 let y = 92; 327 let y = 92;
@@ -304,67 +331,85 @@ mod tests {
304 1 + <|> 331 1 + <|>
305 } 332 }
306 } 333 }
307 ", r#"[CompletionItem { label: "b", lookup: None, snippet: None }, 334 ",
335 r#"[CompletionItem { label: "b", lookup: None, snippet: None },
308 CompletionItem { label: "a", lookup: None, snippet: None }, 336 CompletionItem { label: "a", lookup: None, snippet: None },
309 CompletionItem { label: "quux", lookup: None, snippet: None }]"#); 337 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
338 );
310 } 339 }
311 340
312 #[test] 341 #[test]
313 fn test_completion_for_scope() { 342 fn test_completion_for_scope() {
314 check_scope_completion(r" 343 check_scope_completion(
344 r"
315 fn quux() { 345 fn quux() {
316 for x in &[1, 2, 3] { 346 for x in &[1, 2, 3] {
317 <|> 347 <|>
318 } 348 }
319 } 349 }
320 ", r#"[CompletionItem { label: "x", lookup: None, snippet: None }, 350 ",
321 CompletionItem { label: "quux", lookup: None, snippet: None }]"#); 351 r#"[CompletionItem { label: "x", lookup: None, snippet: None },
352 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
353 );
322 } 354 }
323 355
324 #[test] 356 #[test]
325 fn test_completion_mod_scope() { 357 fn test_completion_mod_scope() {
326 check_scope_completion(r" 358 check_scope_completion(
359 r"
327 struct Foo; 360 struct Foo;
328 enum Baz {} 361 enum Baz {}
329 fn quux() { 362 fn quux() {
330 <|> 363 <|>
331 } 364 }
332 ", r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, 365 ",
366 r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
333 CompletionItem { label: "Baz", lookup: None, snippet: None }, 367 CompletionItem { label: "Baz", lookup: None, snippet: None },
334 CompletionItem { label: "quux", lookup: None, snippet: None }]"#); 368 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
369 );
335 } 370 }
336 371
337 #[test] 372 #[test]
338 fn test_completion_mod_scope_no_self_use() { 373 fn test_completion_mod_scope_no_self_use() {
339 check_scope_completion(r" 374 check_scope_completion(
375 r"
340 use foo<|>; 376 use foo<|>;
341 ", r#"[]"#); 377 ",
378 r#"[]"#,
379 );
342 } 380 }
343 381
344 #[test] 382 #[test]
345 fn test_completion_mod_scope_nested() { 383 fn test_completion_mod_scope_nested() {
346 check_scope_completion(r" 384 check_scope_completion(
385 r"
347 struct Foo; 386 struct Foo;
348 mod m { 387 mod m {
349 struct Bar; 388 struct Bar;
350 fn quux() { <|> } 389 fn quux() { <|> }
351 } 390 }
352 ", r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, 391 ",
353 CompletionItem { label: "quux", lookup: None, snippet: None }]"#); 392 r#"[CompletionItem { label: "Bar", lookup: None, snippet: None },
393 CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
394 );
354 } 395 }
355 396
356 #[test] 397 #[test]
357 fn test_complete_type() { 398 fn test_complete_type() {
358 check_scope_completion(r" 399 check_scope_completion(
400 r"
359 struct Foo; 401 struct Foo;
360 fn x() -> <|> 402 fn x() -> <|>
361 ", r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, 403 ",
362 CompletionItem { label: "x", lookup: None, snippet: None }]"#) 404 r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
405 CompletionItem { label: "x", lookup: None, snippet: None }]"#,
406 )
363 } 407 }
364 408
365 #[test] 409 #[test]
366 fn test_complete_shadowing() { 410 fn test_complete_shadowing() {
367 check_scope_completion(r" 411 check_scope_completion(
412 r"
368 fn foo() -> { 413 fn foo() -> {
369 let bar = 92; 414 let bar = 92;
370 { 415 {
@@ -372,15 +417,20 @@ mod tests {
372 <|> 417 <|>
373 } 418 }
374 } 419 }
375 ", r#"[CompletionItem { label: "bar", lookup: None, snippet: None }, 420 ",
376 CompletionItem { label: "foo", lookup: None, snippet: None }]"#) 421 r#"[CompletionItem { label: "bar", lookup: None, snippet: None },
422 CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
423 )
377 } 424 }
378 425
379 #[test] 426 #[test]
380 fn test_complete_self() { 427 fn test_complete_self() {
381 check_scope_completion(r" 428 check_scope_completion(
429 r"
382 impl S { fn foo(&self) { <|> } } 430 impl S { fn foo(&self) { <|> } }
383 ", r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#) 431 ",
432 r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#,
433 )
384 } 434 }
385 435
386 #[test] 436 #[test]