aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-11-27 18:32:33 +0000
committerAleksey Kladov <[email protected]>2019-11-27 18:35:06 +0000
commit757e593b253b4df7e6fc8bf15a4d4f34c9d484c5 (patch)
treed972d3a7e6457efdb5e0c558a8350db1818d07ae /crates/ra_ide
parentd9a36a736bfb91578a36505e7237212959bb55fe (diff)
rename ra_ide_api -> ra_ide
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/Cargo.toml46
-rw-r--r--crates/ra_ide/src/assists.rs28
-rw-r--r--crates/ra_ide/src/call_info.rs592
-rw-r--r--crates/ra_ide/src/change.rs354
-rw-r--r--crates/ra_ide/src/completion.rs77
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs456
-rw-r--r--crates/ra_ide/src/completion/complete_fn_param.rs136
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs781
-rw-r--r--crates/ra_ide/src/completion/complete_macro_in_item_position.rs143
-rw-r--r--crates/ra_ide/src/completion/complete_path.rs785
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs89
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs282
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs159
-rw-r--r--crates/ra_ide/src/completion/complete_record_pattern.rs93
-rw-r--r--crates/ra_ide/src/completion/complete_scope.rs876
-rw-r--r--crates/ra_ide/src/completion/complete_snippet.rs120
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs274
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs322
-rw-r--r--crates/ra_ide/src/completion/presentation.rs676
-rw-r--r--crates/ra_ide/src/db.rs144
-rw-r--r--crates/ra_ide/src/diagnostics.rs652
-rw-r--r--crates/ra_ide/src/display.rs84
-rw-r--r--crates/ra_ide/src/display/function_signature.rs215
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs411
-rw-r--r--crates/ra_ide/src/display/short_label.rs97
-rw-r--r--crates/ra_ide/src/display/structure.rs401
-rw-r--r--crates/ra_ide/src/expand.rs63
-rw-r--r--crates/ra_ide/src/expand_macro.rs295
-rw-r--r--crates/ra_ide/src/extend_selection.rs452
-rw-r--r--crates/ra_ide/src/feature_flags.rs70
-rw-r--r--crates/ra_ide/src/folding_ranges.rs378
-rw-r--r--crates/ra_ide/src/goto_definition.rs696
-rw-r--r--crates/ra_ide/src/goto_type_definition.rs105
-rw-r--r--crates/ra_ide/src/hover.rs730
-rw-r--r--crates/ra_ide/src/impls.rs206
-rw-r--r--crates/ra_ide/src/inlay_hints.rs543
-rw-r--r--crates/ra_ide/src/join_lines.rs611
-rw-r--r--crates/ra_ide/src/lib.rs489
-rw-r--r--crates/ra_ide/src/line_index.rs283
-rw-r--r--crates/ra_ide/src/line_index_utils.rs331
-rw-r--r--crates/ra_ide/src/marks.rs13
-rw-r--r--crates/ra_ide/src/matching_brace.rs43
-rw-r--r--crates/ra_ide/src/mock_analysis.rs149
-rw-r--r--crates/ra_ide/src/parent_module.rs104
-rw-r--r--crates/ra_ide/src/references.rs389
-rw-r--r--crates/ra_ide/src/references/classify.rs186
-rw-r--r--crates/ra_ide/src/references/name_definition.rs83
-rw-r--r--crates/ra_ide/src/references/rename.rs328
-rw-r--r--crates/ra_ide/src/references/search_scope.rs145
-rw-r--r--crates/ra_ide/src/runnables.rs242
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html48
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html33
-rw-r--r--crates/ra_ide/src/source_change.rs119
-rw-r--r--crates/ra_ide/src/status.rs136
-rw-r--r--crates/ra_ide/src/symbol_index.rs405
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs342
-rw-r--r--crates/ra_ide/src/syntax_tree.rs359
-rw-r--r--crates/ra_ide/src/test_utils.rs21
-rw-r--r--crates/ra_ide/src/typing.rs490
-rw-r--r--crates/ra_ide/src/wasm_shims.rs19
60 files changed, 17199 insertions, 0 deletions
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
new file mode 100644
index 000000000..e6383dd35
--- /dev/null
+++ b/crates/ra_ide/Cargo.toml
@@ -0,0 +1,46 @@
1[package]
2edition = "2018"
3name = "ra_ide"
4version = "0.1.0"
5authors = ["rust-analyzer developers"]
6
7[lib]
8doctest = false
9
10[features]
11wasm = []
12
13[dependencies]
14format-buf = "1.0.0"
15itertools = "0.8.0"
16join_to_string = "0.1.3"
17log = "0.4.5"
18rayon = "1.0.2"
19fst = { version = "0.3.1", default-features = false }
20rustc-hash = "1.0"
21unicase = "2.2.0"
22superslice = "1.0.0"
23rand = { version = "0.7.0", features = ["small_rng"] }
24once_cell = "1.2.0"
25
26ra_syntax = { path = "../ra_syntax" }
27ra_text_edit = { path = "../ra_text_edit" }
28ra_db = { path = "../ra_db" }
29ra_cfg = { path = "../ra_cfg" }
30ra_fmt = { path = "../ra_fmt" }
31ra_prof = { path = "../ra_prof" }
32test_utils = { path = "../test_utils" }
33ra_assists = { path = "../ra_assists" }
34
35# ra_ide should depend only on the top-level `hir` package. if you need
36# something from some `hir_xxx` subpackage, reexport the API via `hir`.
37hir = { path = "../ra_hir", package = "ra_hir" }
38
39[dev-dependencies]
40insta = "0.12.0"
41
42[dev-dependencies.proptest]
43version = "0.9.0"
44# Disable `fork` feature to allow compiling on webassembly
45default-features = false
46features = ["std", "bit-set", "break-dead-code"]
diff --git a/crates/ra_ide/src/assists.rs b/crates/ra_ide/src/assists.rs
new file mode 100644
index 000000000..e00589733
--- /dev/null
+++ b/crates/ra_ide/src/assists.rs
@@ -0,0 +1,28 @@
1//! FIXME: write short doc here
2
3use ra_db::{FilePosition, FileRange};
4
5use crate::{db::RootDatabase, SourceChange, SourceFileEdit};
6
7pub use ra_assists::AssistId;
8
9#[derive(Debug)]
10pub struct Assist {
11 pub id: AssistId,
12 pub change: SourceChange,
13}
14
15pub(crate) fn assists(db: &RootDatabase, frange: FileRange) -> Vec<Assist> {
16 ra_assists::assists(db, frange)
17 .into_iter()
18 .map(|(label, action)| {
19 let file_id = frange.file_id;
20 let file_edit = SourceFileEdit { file_id, edit: action.edit };
21 let id = label.id;
22 let change = SourceChange::source_file_edit(label.label, file_edit).with_cursor_opt(
23 action.cursor_position.map(|offset| FilePosition { offset, file_id }),
24 );
25 Assist { id, change }
26 })
27 .collect()
28}
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
new file mode 100644
index 000000000..d559dc4d0
--- /dev/null
+++ b/crates/ra_ide/src/call_info.rs
@@ -0,0 +1,592 @@
1//! FIXME: write short doc here
2
3use ra_db::SourceDatabase;
4use ra_syntax::{
5 algo::ancestors_at_offset,
6 ast::{self, ArgListOwner},
7 match_ast, AstNode, SyntaxNode, TextUnit,
8};
9use test_utils::tested_by;
10
11use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature};
12
13/// Computes parameter information for the given call expression.
14pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
15 let parse = db.parse(position.file_id);
16 let syntax = parse.tree().syntax().clone();
17
18 // Find the calling expression and it's NameRef
19 let calling_node = FnCallNode::with_node(&syntax, position.offset)?;
20 let name_ref = calling_node.name_ref()?;
21 let name_ref = hir::Source::new(position.file_id.into(), name_ref.syntax());
22
23 let analyzer = hir::SourceAnalyzer::new(db, name_ref, None);
24 let (mut call_info, has_self) = match &calling_node {
25 FnCallNode::CallExpr(expr) => {
26 //FIXME: Type::as_callable is broken
27 let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
28 match callable_def {
29 hir::CallableDef::FunctionId(it) => {
30 let fn_def = it.into();
31 (CallInfo::with_fn(db, fn_def), fn_def.has_self_param(db))
32 }
33 hir::CallableDef::StructId(it) => (CallInfo::with_struct(db, it.into())?, false),
34 hir::CallableDef::EnumVariantId(it) => {
35 (CallInfo::with_enum_variant(db, it.into())?, false)
36 }
37 }
38 }
39 FnCallNode::MethodCallExpr(expr) => {
40 let function = analyzer.resolve_method_call(&expr)?;
41 (CallInfo::with_fn(db, function), function.has_self_param(db))
42 }
43 FnCallNode::MacroCallExpr(expr) => {
44 let macro_def = analyzer.resolve_macro_call(db, name_ref.with_value(&expr))?;
45 (CallInfo::with_macro(db, macro_def)?, false)
46 }
47 };
48
49 // If we have a calling expression let's find which argument we are on
50 let num_params = call_info.parameters().len();
51
52 if num_params == 1 {
53 if !has_self {
54 call_info.active_parameter = Some(0);
55 }
56 } else if num_params > 1 {
57 // Count how many parameters into the call we are.
58 if let Some(arg_list) = calling_node.arg_list() {
59 // Number of arguments specified at the call site
60 let num_args_at_callsite = arg_list.args().count();
61
62 let arg_list_range = arg_list.syntax().text_range();
63 if !arg_list_range.contains_inclusive(position.offset) {
64 tested_by!(call_info_bad_offset);
65 return None;
66 }
67
68 let mut param = std::cmp::min(
69 num_args_at_callsite,
70 arg_list
71 .args()
72 .take_while(|arg| arg.syntax().text_range().end() < position.offset)
73 .count(),
74 );
75
76 // If we are in a method account for `self`
77 if has_self {
78 param += 1;
79 }
80
81 call_info.active_parameter = Some(param);
82 }
83 }
84
85 Some(call_info)
86}
87
88#[derive(Debug)]
89enum FnCallNode {
90 CallExpr(ast::CallExpr),
91 MethodCallExpr(ast::MethodCallExpr),
92 MacroCallExpr(ast::MacroCall),
93}
94
95impl FnCallNode {
96 fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> {
97 ancestors_at_offset(syntax, offset).find_map(|node| {
98 match_ast! {
99 match node {
100 ast::CallExpr(it) => { Some(FnCallNode::CallExpr(it)) },
101 ast::MethodCallExpr(it) => { Some(FnCallNode::MethodCallExpr(it)) },
102 ast::MacroCall(it) => { Some(FnCallNode::MacroCallExpr(it)) },
103 _ => { None },
104 }
105 }
106 })
107 }
108
109 fn name_ref(&self) -> Option<ast::NameRef> {
110 match self {
111 FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
112 ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
113 _ => return None,
114 }),
115
116 FnCallNode::MethodCallExpr(call_expr) => {
117 call_expr.syntax().children().filter_map(ast::NameRef::cast).nth(0)
118 }
119
120 FnCallNode::MacroCallExpr(call_expr) => call_expr.path()?.segment()?.name_ref(),
121 }
122 }
123
124 fn arg_list(&self) -> Option<ast::ArgList> {
125 match self {
126 FnCallNode::CallExpr(expr) => expr.arg_list(),
127 FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
128 FnCallNode::MacroCallExpr(_) => None,
129 }
130 }
131}
132
133impl CallInfo {
134 fn with_fn(db: &RootDatabase, function: hir::Function) -> Self {
135 let signature = FunctionSignature::from_hir(db, function);
136
137 CallInfo { signature, active_parameter: None }
138 }
139
140 fn with_struct(db: &RootDatabase, st: hir::Struct) -> Option<Self> {
141 let signature = FunctionSignature::from_struct(db, st)?;
142
143 Some(CallInfo { signature, active_parameter: None })
144 }
145
146 fn with_enum_variant(db: &RootDatabase, variant: hir::EnumVariant) -> Option<Self> {
147 let signature = FunctionSignature::from_enum_variant(db, variant)?;
148
149 Some(CallInfo { signature, active_parameter: None })
150 }
151
152 fn with_macro(db: &RootDatabase, macro_def: hir::MacroDef) -> Option<Self> {
153 let signature = FunctionSignature::from_macro(db, macro_def)?;
154
155 Some(CallInfo { signature, active_parameter: None })
156 }
157
158 fn parameters(&self) -> &[String] {
159 &self.signature.parameters
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use test_utils::covers;
166
167 use crate::mock_analysis::single_file_with_position;
168
169 use super::*;
170
171 // These are only used when testing
172 impl CallInfo {
173 fn doc(&self) -> Option<hir::Documentation> {
174 self.signature.doc.clone()
175 }
176
177 fn label(&self) -> String {
178 self.signature.to_string()
179 }
180 }
181
182 fn call_info(text: &str) -> CallInfo {
183 let (analysis, position) = single_file_with_position(text);
184 analysis.call_info(position).unwrap().unwrap()
185 }
186
187 #[test]
188 fn test_fn_signature_two_args_firstx() {
189 let info = call_info(
190 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
191fn bar() { foo(<|>3, ); }"#,
192 );
193
194 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
195 assert_eq!(info.active_parameter, Some(0));
196 }
197
198 #[test]
199 fn test_fn_signature_two_args_second() {
200 let info = call_info(
201 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
202fn bar() { foo(3, <|>); }"#,
203 );
204
205 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
206 assert_eq!(info.active_parameter, Some(1));
207 }
208
209 #[test]
210 fn test_fn_signature_two_args_empty() {
211 let info = call_info(
212 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
213fn bar() { foo(<|>); }"#,
214 );
215
216 assert_eq!(info.parameters(), ["x: u32", "y: u32"]);
217 assert_eq!(info.active_parameter, Some(0));
218 }
219
220 #[test]
221 fn test_fn_signature_two_args_first_generics() {
222 let info = call_info(
223 r#"fn foo<T, U: Copy + Display>(x: T, y: U) -> u32 where T: Copy + Display, U: Debug {x + y}
224fn bar() { foo(<|>3, ); }"#,
225 );
226
227 assert_eq!(info.parameters(), ["x: T", "y: U"]);
228 assert_eq!(
229 info.label(),
230 r#"
231fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
232where T: Copy + Display,
233 U: Debug
234 "#
235 .trim()
236 );
237 assert_eq!(info.active_parameter, Some(0));
238 }
239
240 #[test]
241 fn test_fn_signature_no_params() {
242 let info = call_info(
243 r#"fn foo<T>() -> T where T: Copy + Display {}
244fn bar() { foo(<|>); }"#,
245 );
246
247 assert!(info.parameters().is_empty());
248 assert_eq!(
249 info.label(),
250 r#"
251fn foo<T>() -> T
252where T: Copy + Display
253 "#
254 .trim()
255 );
256 assert!(info.active_parameter.is_none());
257 }
258
259 #[test]
260 fn test_fn_signature_for_impl() {
261 let info = call_info(
262 r#"struct F; impl F { pub fn new() { F{}} }
263fn bar() {let _ : F = F::new(<|>);}"#,
264 );
265
266 assert!(info.parameters().is_empty());
267 assert_eq!(info.active_parameter, None);
268 }
269
270 #[test]
271 fn test_fn_signature_for_method_self() {
272 let info = call_info(
273 r#"struct F;
274impl F {
275 pub fn new() -> F{
276 F{}
277 }
278
279 pub fn do_it(&self) {}
280}
281
282fn bar() {
283 let f : F = F::new();
284 f.do_it(<|>);
285}"#,
286 );
287
288 assert_eq!(info.parameters(), ["&self"]);
289 assert_eq!(info.active_parameter, None);
290 }
291
292 #[test]
293 fn test_fn_signature_for_method_with_arg() {
294 let info = call_info(
295 r#"struct F;
296impl F {
297 pub fn new() -> F{
298 F{}
299 }
300
301 pub fn do_it(&self, x: i32) {}
302}
303
304fn bar() {
305 let f : F = F::new();
306 f.do_it(<|>);
307}"#,
308 );
309
310 assert_eq!(info.parameters(), ["&self", "x: i32"]);
311 assert_eq!(info.active_parameter, Some(1));
312 }
313
314 #[test]
315 fn test_fn_signature_with_docs_simple() {
316 let info = call_info(
317 r#"
318/// test
319// non-doc-comment
320fn foo(j: u32) -> u32 {
321 j
322}
323
324fn bar() {
325 let _ = foo(<|>);
326}
327"#,
328 );
329
330 assert_eq!(info.parameters(), ["j: u32"]);
331 assert_eq!(info.active_parameter, Some(0));
332 assert_eq!(info.label(), "fn foo(j: u32) -> u32");
333 assert_eq!(info.doc().map(|it| it.into()), Some("test".to_string()));
334 }
335
336 #[test]
337 fn test_fn_signature_with_docs() {
338 let info = call_info(
339 r#"
340/// Adds one to the number given.
341///
342/// # Examples
343///
344/// ```
345/// let five = 5;
346///
347/// assert_eq!(6, my_crate::add_one(5));
348/// ```
349pub fn add_one(x: i32) -> i32 {
350 x + 1
351}
352
353pub fn do() {
354 add_one(<|>
355}"#,
356 );
357
358 assert_eq!(info.parameters(), ["x: i32"]);
359 assert_eq!(info.active_parameter, Some(0));
360 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
361 assert_eq!(
362 info.doc().map(|it| it.into()),
363 Some(
364 r#"Adds one to the number given.
365
366# Examples
367
368```
369let five = 5;
370
371assert_eq!(6, my_crate::add_one(5));
372```"#
373 .to_string()
374 )
375 );
376 }
377
378 #[test]
379 fn test_fn_signature_with_docs_impl() {
380 let info = call_info(
381 r#"
382struct addr;
383impl addr {
384 /// Adds one to the number given.
385 ///
386 /// # Examples
387 ///
388 /// ```
389 /// let five = 5;
390 ///
391 /// assert_eq!(6, my_crate::add_one(5));
392 /// ```
393 pub fn add_one(x: i32) -> i32 {
394 x + 1
395 }
396}
397
398pub fn do_it() {
399 addr {};
400 addr::add_one(<|>);
401}"#,
402 );
403
404 assert_eq!(info.parameters(), ["x: i32"]);
405 assert_eq!(info.active_parameter, Some(0));
406 assert_eq!(info.label(), "pub fn add_one(x: i32) -> i32");
407 assert_eq!(
408 info.doc().map(|it| it.into()),
409 Some(
410 r#"Adds one to the number given.
411
412# Examples
413
414```
415let five = 5;
416
417assert_eq!(6, my_crate::add_one(5));
418```"#
419 .to_string()
420 )
421 );
422 }
423
424 #[test]
425 fn test_fn_signature_with_docs_from_actix() {
426 let info = call_info(
427 r#"
428struct WriteHandler<E>;
429
430impl<E> WriteHandler<E> {
431 /// Method is called when writer emits error.
432 ///
433 /// If this method returns `ErrorAction::Continue` writer processing
434 /// continues otherwise stream processing stops.
435 fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
436 Running::Stop
437 }
438
439 /// Method is called when writer finishes.
440 ///
441 /// By default this method stops actor's `Context`.
442 fn finished(&mut self, ctx: &mut Self::Context) {
443 ctx.stop()
444 }
445}
446
447pub fn foo(mut r: WriteHandler<()>) {
448 r.finished(<|>);
449}
450
451"#,
452 );
453
454 assert_eq!(info.label(), "fn finished(&mut self, ctx: &mut Self::Context)".to_string());
455 assert_eq!(info.parameters(), ["&mut self", "ctx: &mut Self::Context"]);
456 assert_eq!(info.active_parameter, Some(1));
457 assert_eq!(
458 info.doc().map(|it| it.into()),
459 Some(
460 r#"Method is called when writer finishes.
461
462By default this method stops actor's `Context`."#
463 .to_string()
464 )
465 );
466 }
467
468 #[test]
469 fn call_info_bad_offset() {
470 covers!(call_info_bad_offset);
471 let (analysis, position) = single_file_with_position(
472 r#"fn foo(x: u32, y: u32) -> u32 {x + y}
473 fn bar() { foo <|> (3, ); }"#,
474 );
475 let call_info = analysis.call_info(position).unwrap();
476 assert!(call_info.is_none());
477 }
478
479 #[test]
480 fn test_nested_method_in_lamba() {
481 let info = call_info(
482 r#"struct Foo;
483
484impl Foo {
485 fn bar(&self, _: u32) { }
486}
487
488fn bar(_: u32) { }
489
490fn main() {
491 let foo = Foo;
492 std::thread::spawn(move || foo.bar(<|>));
493}"#,
494 );
495
496 assert_eq!(info.parameters(), ["&self", "_: u32"]);
497 assert_eq!(info.active_parameter, Some(1));
498 assert_eq!(info.label(), "fn bar(&self, _: u32)");
499 }
500
501 #[test]
502 fn works_for_tuple_structs() {
503 let info = call_info(
504 r#"
505/// A cool tuple struct
506struct TS(u32, i32);
507fn main() {
508 let s = TS(0, <|>);
509}"#,
510 );
511
512 assert_eq!(info.label(), "struct TS(u32, i32) -> TS");
513 assert_eq!(info.doc().map(|it| it.into()), Some("A cool tuple struct".to_string()));
514 assert_eq!(info.active_parameter, Some(1));
515 }
516
517 #[test]
518 #[should_panic]
519 fn cant_call_named_structs() {
520 let _ = call_info(
521 r#"
522struct TS { x: u32, y: i32 }
523fn main() {
524 let s = TS(<|>);
525}"#,
526 );
527 }
528
529 #[test]
530 fn works_for_enum_variants() {
531 let info = call_info(
532 r#"
533enum E {
534 /// A Variant
535 A(i32),
536 /// Another
537 B,
538 /// And C
539 C { a: i32, b: i32 }
540}
541
542fn main() {
543 let a = E::A(<|>);
544}
545 "#,
546 );
547
548 assert_eq!(info.label(), "E::A(0: i32)");
549 assert_eq!(info.doc().map(|it| it.into()), Some("A Variant".to_string()));
550 assert_eq!(info.active_parameter, Some(0));
551 }
552
553 #[test]
554 #[should_panic]
555 fn cant_call_enum_records() {
556 let _ = call_info(
557 r#"
558enum E {
559 /// A Variant
560 A(i32),
561 /// Another
562 B,
563 /// And C
564 C { a: i32, b: i32 }
565}
566
567fn main() {
568 let a = E::C(<|>);
569}
570 "#,
571 );
572 }
573
574 #[test]
575 fn fn_signature_for_macro() {
576 let info = call_info(
577 r#"
578/// empty macro
579macro_rules! foo {
580 () => {}
581}
582
583fn f() {
584 foo!(<|>);
585}
586 "#,
587 );
588
589 assert_eq!(info.label(), "foo!()");
590 assert_eq!(info.doc().map(|it| it.into()), Some("empty macro".to_string()));
591 }
592}
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs
new file mode 100644
index 000000000..4a76d1dd8
--- /dev/null
+++ b/crates/ra_ide/src/change.rs
@@ -0,0 +1,354 @@
1//! FIXME: write short doc here
2
3use std::{fmt, sync::Arc, time};
4
5use ra_db::{
6 salsa::{Database, Durability, SweepStrategy},
7 CrateGraph, CrateId, FileId, RelativePathBuf, SourceDatabase, SourceDatabaseExt, SourceRoot,
8 SourceRootId,
9};
10use ra_prof::{memory_usage, profile, Bytes};
11use ra_syntax::SourceFile;
12#[cfg(not(feature = "wasm"))]
13use rayon::prelude::*;
14use rustc_hash::FxHashMap;
15
16use crate::{
17 db::{DebugData, RootDatabase},
18 symbol_index::{SymbolIndex, SymbolsDatabase},
19};
20
21#[derive(Default)]
22pub struct AnalysisChange {
23 new_roots: Vec<(SourceRootId, bool)>,
24 roots_changed: FxHashMap<SourceRootId, RootChange>,
25 files_changed: Vec<(FileId, Arc<String>)>,
26 libraries_added: Vec<LibraryData>,
27 crate_graph: Option<CrateGraph>,
28 debug_data: DebugData,
29}
30
31impl fmt::Debug for AnalysisChange {
32 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
33 let mut d = fmt.debug_struct("AnalysisChange");
34 if !self.new_roots.is_empty() {
35 d.field("new_roots", &self.new_roots);
36 }
37 if !self.roots_changed.is_empty() {
38 d.field("roots_changed", &self.roots_changed);
39 }
40 if !self.files_changed.is_empty() {
41 d.field("files_changed", &self.files_changed.len());
42 }
43 if !self.libraries_added.is_empty() {
44 d.field("libraries_added", &self.libraries_added.len());
45 }
46 if !self.crate_graph.is_none() {
47 d.field("crate_graph", &self.crate_graph);
48 }
49 d.finish()
50 }
51}
52
53impl AnalysisChange {
54 pub fn new() -> AnalysisChange {
55 AnalysisChange::default()
56 }
57
58 pub fn add_root(&mut self, root_id: SourceRootId, is_local: bool) {
59 self.new_roots.push((root_id, is_local));
60 }
61
62 pub fn add_file(
63 &mut self,
64 root_id: SourceRootId,
65 file_id: FileId,
66 path: RelativePathBuf,
67 text: Arc<String>,
68 ) {
69 let file = AddFile { file_id, path, text };
70 self.roots_changed.entry(root_id).or_default().added.push(file);
71 }
72
73 pub fn change_file(&mut self, file_id: FileId, new_text: Arc<String>) {
74 self.files_changed.push((file_id, new_text))
75 }
76
77 pub fn remove_file(&mut self, root_id: SourceRootId, file_id: FileId, path: RelativePathBuf) {
78 let file = RemoveFile { file_id, path };
79 self.roots_changed.entry(root_id).or_default().removed.push(file);
80 }
81
82 pub fn add_library(&mut self, data: LibraryData) {
83 self.libraries_added.push(data)
84 }
85
86 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
87 self.crate_graph = Some(graph);
88 }
89
90 pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) {
91 self.debug_data.crate_names.insert(crate_id, name);
92 }
93
94 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
95 self.debug_data.root_paths.insert(source_root_id, path);
96 }
97}
98
99#[derive(Debug)]
100struct AddFile {
101 file_id: FileId,
102 path: RelativePathBuf,
103 text: Arc<String>,
104}
105
106#[derive(Debug)]
107struct RemoveFile {
108 file_id: FileId,
109 path: RelativePathBuf,
110}
111
112#[derive(Default)]
113struct RootChange {
114 added: Vec<AddFile>,
115 removed: Vec<RemoveFile>,
116}
117
118impl fmt::Debug for RootChange {
119 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
120 fmt.debug_struct("AnalysisChange")
121 .field("added", &self.added.len())
122 .field("removed", &self.removed.len())
123 .finish()
124 }
125}
126
127pub struct LibraryData {
128 root_id: SourceRootId,
129 root_change: RootChange,
130 symbol_index: SymbolIndex,
131}
132
133impl fmt::Debug for LibraryData {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 f.debug_struct("LibraryData")
136 .field("root_id", &self.root_id)
137 .field("root_change", &self.root_change)
138 .field("n_symbols", &self.symbol_index.len())
139 .finish()
140 }
141}
142
143impl LibraryData {
144 pub fn prepare(
145 root_id: SourceRootId,
146 files: Vec<(FileId, RelativePathBuf, Arc<String>)>,
147 ) -> LibraryData {
148 #[cfg(not(feature = "wasm"))]
149 let iter = files.par_iter();
150 #[cfg(feature = "wasm")]
151 let iter = files.iter();
152
153 let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| {
154 let parse = SourceFile::parse(text);
155 (*file_id, parse)
156 }));
157 let mut root_change = RootChange::default();
158 root_change.added = files
159 .into_iter()
160 .map(|(file_id, path, text)| AddFile { file_id, path, text })
161 .collect();
162 LibraryData { root_id, root_change, symbol_index }
163 }
164}
165
166const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
167
168impl RootDatabase {
169 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
170 let _p = profile("RootDatabase::apply_change");
171 log::info!("apply_change {:?}", change);
172 {
173 let _p = profile("RootDatabase::apply_change/cancellation");
174 self.salsa_runtime_mut().synthetic_write(Durability::LOW);
175 }
176 if !change.new_roots.is_empty() {
177 let mut local_roots = Vec::clone(&self.local_roots());
178 for (root_id, is_local) in change.new_roots {
179 let root = if is_local { SourceRoot::new() } else { SourceRoot::new_library() };
180 let durability = durability(&root);
181 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
182 if is_local {
183 local_roots.push(root_id);
184 }
185 }
186 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
187 }
188
189 for (root_id, root_change) in change.roots_changed {
190 self.apply_root_change(root_id, root_change);
191 }
192 for (file_id, text) in change.files_changed {
193 let source_root_id = self.file_source_root(file_id);
194 let source_root = self.source_root(source_root_id);
195 let durability = durability(&source_root);
196 self.set_file_text_with_durability(file_id, text, durability)
197 }
198 if !change.libraries_added.is_empty() {
199 let mut libraries = Vec::clone(&self.library_roots());
200 for library in change.libraries_added {
201 libraries.push(library.root_id);
202 self.set_source_root_with_durability(
203 library.root_id,
204 Default::default(),
205 Durability::HIGH,
206 );
207 self.set_library_symbols_with_durability(
208 library.root_id,
209 Arc::new(library.symbol_index),
210 Durability::HIGH,
211 );
212 self.apply_root_change(library.root_id, library.root_change);
213 }
214 self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH);
215 }
216 if let Some(crate_graph) = change.crate_graph {
217 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
218 }
219
220 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
221 }
222
223 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
224 let mut source_root = SourceRoot::clone(&self.source_root(root_id));
225 let durability = durability(&source_root);
226 for add_file in root_change.added {
227 self.set_file_text_with_durability(add_file.file_id, add_file.text, durability);
228 self.set_file_relative_path_with_durability(
229 add_file.file_id,
230 add_file.path.clone(),
231 durability,
232 );
233 self.set_file_source_root_with_durability(add_file.file_id, root_id, durability);
234 source_root.insert_file(add_file.path, add_file.file_id);
235 }
236 for remove_file in root_change.removed {
237 self.set_file_text_with_durability(remove_file.file_id, Default::default(), durability);
238 source_root.remove_file(&remove_file.path);
239 }
240 self.set_source_root_with_durability(root_id, Arc::new(source_root), durability);
241 }
242
243 pub(crate) fn maybe_collect_garbage(&mut self) {
244 if cfg!(feature = "wasm") {
245 return;
246 }
247
248 if self.last_gc_check.elapsed() > GC_COOLDOWN {
249 self.last_gc_check = crate::wasm_shims::Instant::now();
250 }
251 }
252
253 pub(crate) fn collect_garbage(&mut self) {
254 if cfg!(feature = "wasm") {
255 return;
256 }
257
258 let _p = profile("RootDatabase::collect_garbage");
259 self.last_gc = crate::wasm_shims::Instant::now();
260
261 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
262
263 self.query(ra_db::ParseQuery).sweep(sweep);
264 self.query(hir::db::ParseMacroQuery).sweep(sweep);
265
266 // Macros do take significant space, but less then the syntax trees
267 // self.query(hir::db::MacroDefQuery).sweep(sweep);
268 // self.query(hir::db::MacroArgQuery).sweep(sweep);
269 // self.query(hir::db::MacroExpandQuery).sweep(sweep);
270
271 self.query(hir::db::AstIdMapQuery).sweep(sweep);
272
273 self.query(hir::db::RawItemsWithSourceMapQuery).sweep(sweep);
274 self.query(hir::db::BodyWithSourceMapQuery).sweep(sweep);
275
276 self.query(hir::db::ExprScopesQuery).sweep(sweep);
277 self.query(hir::db::InferQuery).sweep(sweep);
278 self.query(hir::db::BodyQuery).sweep(sweep);
279 }
280
281 pub(crate) fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
282 let mut acc: Vec<(String, Bytes)> = vec![];
283 let sweep = SweepStrategy::default().discard_values().sweep_all_revisions();
284 macro_rules! sweep_each_query {
285 ($($q:path)*) => {$(
286 let before = memory_usage().allocated;
287 self.query($q).sweep(sweep);
288 let after = memory_usage().allocated;
289 let q: $q = Default::default();
290 let name = format!("{:?}", q);
291 acc.push((name, before - after));
292
293 let before = memory_usage().allocated;
294 self.query($q).sweep(sweep.discard_everything());
295 let after = memory_usage().allocated;
296 let q: $q = Default::default();
297 let name = format!("{:?} (deps)", q);
298 acc.push((name, before - after));
299 )*}
300 }
301 sweep_each_query![
302 ra_db::ParseQuery
303 ra_db::SourceRootCratesQuery
304 hir::db::AstIdMapQuery
305 hir::db::ParseMacroQuery
306 hir::db::MacroDefQuery
307 hir::db::MacroArgQuery
308 hir::db::MacroExpandQuery
309 hir::db::StructDataQuery
310 hir::db::EnumDataQuery
311 hir::db::TraitDataQuery
312 hir::db::RawItemsWithSourceMapQuery
313 hir::db::RawItemsQuery
314 hir::db::CrateDefMapQuery
315 hir::db::GenericParamsQuery
316 hir::db::FunctionDataQuery
317 hir::db::TypeAliasDataQuery
318 hir::db::ConstDataQuery
319 hir::db::StaticDataQuery
320 hir::db::ModuleLangItemsQuery
321 hir::db::CrateLangItemsQuery
322 hir::db::LangItemQuery
323 hir::db::DocumentationQuery
324 hir::db::ExprScopesQuery
325 hir::db::InferQuery
326 hir::db::TyQuery
327 hir::db::ValueTyQuery
328 hir::db::FieldTypesQuery
329 hir::db::CallableItemSignatureQuery
330 hir::db::GenericPredicatesQuery
331 hir::db::GenericDefaultsQuery
332 hir::db::BodyWithSourceMapQuery
333 hir::db::BodyQuery
334 hir::db::ImplsInCrateQuery
335 hir::db::ImplsForTraitQuery
336 hir::db::AssociatedTyDataQuery
337 hir::db::TraitDatumQuery
338 hir::db::StructDatumQuery
339 hir::db::ImplDatumQuery
340 hir::db::ImplDataQuery
341 hir::db::TraitSolveQuery
342 ];
343 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
344 acc
345 }
346}
347
348fn durability(source_root: &SourceRoot) -> Durability {
349 if source_root.is_library {
350 Durability::HIGH
351 } else {
352 Durability::LOW
353 }
354}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
new file mode 100644
index 000000000..abe1f36ce
--- /dev/null
+++ b/crates/ra_ide/src/completion.rs
@@ -0,0 +1,77 @@
1//! FIXME: write short doc here
2
3mod completion_item;
4mod completion_context;
5mod presentation;
6
7mod complete_dot;
8mod complete_record_literal;
9mod complete_record_pattern;
10mod complete_pattern;
11mod complete_fn_param;
12mod complete_keyword;
13mod complete_snippet;
14mod complete_path;
15mod complete_scope;
16mod complete_postfix;
17mod complete_macro_in_item_position;
18
19use ra_db::SourceDatabase;
20
21#[cfg(test)]
22use crate::completion::completion_item::do_completion;
23use crate::{
24 completion::{
25 completion_context::CompletionContext,
26 completion_item::{CompletionKind, Completions},
27 },
28 db, FilePosition,
29};
30
31pub use crate::completion::completion_item::{
32 CompletionItem, CompletionItemKind, InsertTextFormat,
33};
34
35/// Main entry point for completion. We run completion as a two-phase process.
36///
37/// First, we look at the position and collect a so-called `CompletionContext.
38/// This is a somewhat messy process, because, during completion, syntax tree is
39/// incomplete and can look really weird.
40///
41/// Once the context is collected, we run a series of completion routines which
42/// look at the context and produce completion items. One subtlety about this
43/// phase is that completion engine should not filter by the substring which is
44/// already present, it should give all possible variants for the identifier at
45/// the caret. In other words, for
46///
47/// ```no-run
48/// fn f() {
49/// let foo = 92;
50/// let _ = bar<|>
51/// }
52/// ```
53///
54/// `foo` *should* be present among the completion variants. Filtering by
55/// identifier prefix/fuzzy match should be done higher in the stack, together
56/// with ordering of completions (currently this is done by the client).
57pub(crate) fn completions(db: &db::RootDatabase, position: FilePosition) -> Option<Completions> {
58 let original_parse = db.parse(position.file_id);
59 let ctx = CompletionContext::new(db, &original_parse, position)?;
60
61 let mut acc = Completions::default();
62
63 complete_fn_param::complete_fn_param(&mut acc, &ctx);
64 complete_keyword::complete_expr_keyword(&mut acc, &ctx);
65 complete_keyword::complete_use_tree_keyword(&mut acc, &ctx);
66 complete_snippet::complete_expr_snippet(&mut acc, &ctx);
67 complete_snippet::complete_item_snippet(&mut acc, &ctx);
68 complete_path::complete_path(&mut acc, &ctx);
69 complete_scope::complete_scope(&mut acc, &ctx);
70 complete_dot::complete_dot(&mut acc, &ctx);
71 complete_record_literal::complete_record_literal(&mut acc, &ctx);
72 complete_record_pattern::complete_record_pattern(&mut acc, &ctx);
73 complete_pattern::complete_pattern(&mut acc, &ctx);
74 complete_postfix::complete_postfix(&mut acc, &ctx);
75 complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx);
76 Some(acc)
77}
diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs
new file mode 100644
index 000000000..b6fe48627
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_dot.rs
@@ -0,0 +1,456 @@
1//! FIXME: write short doc here
2
3use hir::Type;
4
5use crate::completion::completion_item::CompletionKind;
6use crate::{
7 completion::{completion_context::CompletionContext, completion_item::Completions},
8 CompletionItem,
9};
10use rustc_hash::FxHashSet;
11
12/// Complete dot accesses, i.e. fields or methods (and .await syntax).
13pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
14 let dot_receiver = match &ctx.dot_receiver {
15 Some(expr) => expr,
16 _ => return,
17 };
18
19 let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
20 Some(ty) => ty,
21 _ => return,
22 };
23
24 if !ctx.is_call {
25 complete_fields(acc, ctx, &receiver_ty);
26 }
27 complete_methods(acc, ctx, &receiver_ty);
28
29 // Suggest .await syntax for types that implement Future trait
30 if ctx.analyzer.impls_future(ctx.db, receiver_ty.into_ty()) {
31 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
32 .detail("expr.await")
33 .insert_text("await")
34 .add_to(acc);
35 }
36}
37
38fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
39 for receiver in receiver.autoderef(ctx.db) {
40 for (field, ty) in receiver.fields(ctx.db) {
41 acc.add_field(ctx, field, &ty);
42 }
43 for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() {
44 acc.add_tuple_field(ctx, i, &ty);
45 }
46 }
47}
48
49fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) {
50 let mut seen_methods = FxHashSet::default();
51 ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| {
52 if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) {
53 acc.add_function(ctx, func);
54 }
55 None::<()>
56 });
57}
58
59#[cfg(test)]
60mod tests {
61 use crate::completion::{do_completion, CompletionItem, CompletionKind};
62 use insta::assert_debug_snapshot;
63
64 fn do_ref_completion(code: &str) -> Vec<CompletionItem> {
65 do_completion(code, CompletionKind::Reference)
66 }
67
68 #[test]
69 fn test_struct_field_completion() {
70 assert_debug_snapshot!(
71 do_ref_completion(
72 r"
73 struct A { the_field: u32 }
74 fn foo(a: A) {
75 a.<|>
76 }
77 ",
78 ),
79 @r###"
80 [
81 CompletionItem {
82 label: "the_field",
83 source_range: [94; 94),
84 delete: [94; 94),
85 insert: "the_field",
86 kind: Field,
87 detail: "u32",
88 },
89 ]
90 "###
91 );
92 }
93
94 #[test]
95 fn test_struct_field_completion_self() {
96 assert_debug_snapshot!(
97 do_ref_completion(
98 r"
99 struct A {
100 /// This is the_field
101 the_field: (u32,)
102 }
103 impl A {
104 fn foo(self) {
105 self.<|>
106 }
107 }
108 ",
109 ),
110 @r###"
111 [
112 CompletionItem {
113 label: "foo()",
114 source_range: [187; 187),
115 delete: [187; 187),
116 insert: "foo()$0",
117 kind: Method,
118 lookup: "foo",
119 detail: "fn foo(self)",
120 },
121 CompletionItem {
122 label: "the_field",
123 source_range: [187; 187),
124 delete: [187; 187),
125 insert: "the_field",
126 kind: Field,
127 detail: "(u32,)",
128 documentation: Documentation(
129 "This is the_field",
130 ),
131 },
132 ]
133 "###
134 );
135 }
136
137 #[test]
138 fn test_struct_field_completion_autoderef() {
139 assert_debug_snapshot!(
140 do_ref_completion(
141 r"
142 struct A { the_field: (u32, i32) }
143 impl A {
144 fn foo(&self) {
145 self.<|>
146 }
147 }
148 ",
149 ),
150 @r###"
151 [
152 CompletionItem {
153 label: "foo()",
154 source_range: [126; 126),
155 delete: [126; 126),
156 insert: "foo()$0",
157 kind: Method,
158 lookup: "foo",
159 detail: "fn foo(&self)",
160 },
161 CompletionItem {
162 label: "the_field",
163 source_range: [126; 126),
164 delete: [126; 126),
165 insert: "the_field",
166 kind: Field,
167 detail: "(u32, i32)",
168 },
169 ]
170 "###
171 );
172 }
173
174 #[test]
175 fn test_no_struct_field_completion_for_method_call() {
176 assert_debug_snapshot!(
177 do_ref_completion(
178 r"
179 struct A { the_field: u32 }
180 fn foo(a: A) {
181 a.<|>()
182 }
183 ",
184 ),
185 @"[]"
186 );
187 }
188
189 #[test]
190 fn test_method_completion() {
191 assert_debug_snapshot!(
192 do_ref_completion(
193 r"
194 struct A {}
195 impl A {
196 fn the_method(&self) {}
197 }
198 fn foo(a: A) {
199 a.<|>
200 }
201 ",
202 ),
203 @r###"
204 [
205 CompletionItem {
206 label: "the_method()",
207 source_range: [144; 144),
208 delete: [144; 144),
209 insert: "the_method()$0",
210 kind: Method,
211 lookup: "the_method",
212 detail: "fn the_method(&self)",
213 },
214 ]
215 "###
216 );
217 }
218
219 #[test]
220 fn test_trait_method_completion() {
221 assert_debug_snapshot!(
222 do_ref_completion(
223 r"
224 struct A {}
225 trait Trait { fn the_method(&self); }
226 impl Trait for A {}
227 fn foo(a: A) {
228 a.<|>
229 }
230 ",
231 ),
232 @r###"
233 [
234 CompletionItem {
235 label: "the_method()",
236 source_range: [151; 151),
237 delete: [151; 151),
238 insert: "the_method()$0",
239 kind: Method,
240 lookup: "the_method",
241 detail: "fn the_method(&self)",
242 },
243 ]
244 "###
245 );
246 }
247
248 #[test]
249 fn test_trait_method_completion_deduplicated() {
250 assert_debug_snapshot!(
251 do_ref_completion(
252 r"
253 struct A {}
254 trait Trait { fn the_method(&self); }
255 impl<T> Trait for T {}
256 fn foo(a: &A) {
257 a.<|>
258 }
259 ",
260 ),
261 @r###"
262 [
263 CompletionItem {
264 label: "the_method()",
265 source_range: [155; 155),
266 delete: [155; 155),
267 insert: "the_method()$0",
268 kind: Method,
269 lookup: "the_method",
270 detail: "fn the_method(&self)",
271 },
272 ]
273 "###
274 );
275 }
276
277 #[test]
278 fn test_no_non_self_method() {
279 assert_debug_snapshot!(
280 do_ref_completion(
281 r"
282 struct A {}
283 impl A {
284 fn the_method() {}
285 }
286 fn foo(a: A) {
287 a.<|>
288 }
289 ",
290 ),
291 @"[]"
292 );
293 }
294
295 #[test]
296 fn test_method_attr_filtering() {
297 assert_debug_snapshot!(
298 do_ref_completion(
299 r"
300 struct A {}
301 impl A {
302 #[inline]
303 fn the_method(&self) {
304 let x = 1;
305 let y = 2;
306 }
307 }
308 fn foo(a: A) {
309 a.<|>
310 }
311 ",
312 ),
313 @r###"
314 [
315 CompletionItem {
316 label: "the_method()",
317 source_range: [249; 249),
318 delete: [249; 249),
319 insert: "the_method()$0",
320 kind: Method,
321 lookup: "the_method",
322 detail: "fn the_method(&self)",
323 },
324 ]
325 "###
326 );
327 }
328
329 #[test]
330 fn test_tuple_field_completion() {
331 assert_debug_snapshot!(
332 do_ref_completion(
333 r"
334 fn foo() {
335 let b = (0, 3.14);
336 b.<|>
337 }
338 ",
339 ),
340 @r###"
341 [
342 CompletionItem {
343 label: "0",
344 source_range: [75; 75),
345 delete: [75; 75),
346 insert: "0",
347 kind: Field,
348 detail: "i32",
349 },
350 CompletionItem {
351 label: "1",
352 source_range: [75; 75),
353 delete: [75; 75),
354 insert: "1",
355 kind: Field,
356 detail: "f64",
357 },
358 ]
359 "###
360 );
361 }
362
363 #[test]
364 fn test_tuple_field_inference() {
365 assert_debug_snapshot!(
366 do_ref_completion(
367 r"
368 pub struct S;
369 impl S {
370 pub fn blah(&self) {}
371 }
372
373 struct T(S);
374
375 impl T {
376 fn foo(&self) {
377 // FIXME: This doesn't work without the trailing `a` as `0.` is a float
378 self.0.a<|>
379 }
380 }
381 ",
382 ),
383 @r###"
384 [
385 CompletionItem {
386 label: "blah()",
387 source_range: [299; 300),
388 delete: [299; 300),
389 insert: "blah()$0",
390 kind: Method,
391 lookup: "blah",
392 detail: "pub fn blah(&self)",
393 },
394 ]
395 "###
396 );
397 }
398
399 #[test]
400 fn test_completion_works_in_consts() {
401 assert_debug_snapshot!(
402 do_ref_completion(
403 r"
404 struct A { the_field: u32 }
405 const X: u32 = {
406 A { the_field: 92 }.<|>
407 };
408 ",
409 ),
410 @r###"
411 [
412 CompletionItem {
413 label: "the_field",
414 source_range: [106; 106),
415 delete: [106; 106),
416 insert: "the_field",
417 kind: Field,
418 detail: "u32",
419 },
420 ]
421 "###
422 );
423 }
424
425 #[test]
426 fn test_completion_await_impls_future() {
427 assert_debug_snapshot!(
428 do_completion(
429 r###"
430 //- /main.rs
431 use std::future::*;
432 struct A {}
433 impl Future for A {}
434 fn foo(a: A) {
435 a.<|>
436 }
437
438 //- /std/lib.rs
439 pub mod future {
440 pub trait Future {}
441 }
442 "###, CompletionKind::Keyword),
443 @r###"
444 [
445 CompletionItem {
446 label: "await",
447 source_range: [74; 74),
448 delete: [74; 74),
449 insert: "await",
450 detail: "expr.await",
451 },
452 ]
453 "###
454 )
455 }
456}
diff --git a/crates/ra_ide/src/completion/complete_fn_param.rs b/crates/ra_ide/src/completion/complete_fn_param.rs
new file mode 100644
index 000000000..502458706
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_fn_param.rs
@@ -0,0 +1,136 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{ast, match_ast, AstNode};
4use rustc_hash::FxHashMap;
5
6use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
7
8/// Complete repeated parameters, both name and type. For example, if all
9/// functions in a file have a `spam: &mut Spam` parameter, a completion with
10/// `spam: &mut Spam` insert text/label and `spam` lookup string will be
11/// suggested.
12pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext) {
13 if !ctx.is_param {
14 return;
15 }
16
17 let mut params = FxHashMap::default();
18 for node in ctx.token.parent().ancestors() {
19 match_ast! {
20 match node {
21 ast::SourceFile(it) => { process(it, &mut params) },
22 ast::ItemList(it) => { process(it, &mut params) },
23 _ => (),
24 }
25 }
26 }
27 params
28 .into_iter()
29 .filter_map(|(label, (count, param))| {
30 let lookup = param.pat()?.syntax().text().to_string();
31 if count < 2 {
32 None
33 } else {
34 Some((label, lookup))
35 }
36 })
37 .for_each(|(label, lookup)| {
38 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label)
39 .lookup_by(lookup)
40 .add_to(acc)
41 });
42
43 fn process<N: ast::FnDefOwner>(node: N, params: &mut FxHashMap<String, (u32, ast::Param)>) {
44 node.functions().filter_map(|it| it.param_list()).flat_map(|it| it.params()).for_each(
45 |param| {
46 let text = param.syntax().text().to_string();
47 params.entry(text).or_insert((0, param)).0 += 1;
48 },
49 )
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use crate::completion::{do_completion, CompletionItem, CompletionKind};
56 use insta::assert_debug_snapshot;
57
58 fn do_magic_completion(code: &str) -> Vec<CompletionItem> {
59 do_completion(code, CompletionKind::Magic)
60 }
61
62 #[test]
63 fn test_param_completion_last_param() {
64 assert_debug_snapshot!(
65 do_magic_completion(
66 r"
67 fn foo(file_id: FileId) {}
68 fn bar(file_id: FileId) {}
69 fn baz(file<|>) {}
70 ",
71 ),
72 @r###"
73 [
74 CompletionItem {
75 label: "file_id: FileId",
76 source_range: [110; 114),
77 delete: [110; 114),
78 insert: "file_id: FileId",
79 lookup: "file_id",
80 },
81 ]
82 "###
83 );
84 }
85
86 #[test]
87 fn test_param_completion_nth_param() {
88 assert_debug_snapshot!(
89 do_magic_completion(
90 r"
91 fn foo(file_id: FileId) {}
92 fn bar(file_id: FileId) {}
93 fn baz(file<|>, x: i32) {}
94 ",
95 ),
96 @r###"
97 [
98 CompletionItem {
99 label: "file_id: FileId",
100 source_range: [110; 114),
101 delete: [110; 114),
102 insert: "file_id: FileId",
103 lookup: "file_id",
104 },
105 ]
106 "###
107 );
108 }
109
110 #[test]
111 fn test_param_completion_trait_param() {
112 assert_debug_snapshot!(
113 do_magic_completion(
114 r"
115 pub(crate) trait SourceRoot {
116 pub fn contains(&self, file_id: FileId) -> bool;
117 pub fn module_map(&self) -> &ModuleMap;
118 pub fn lines(&self, file_id: FileId) -> &LineIndex;
119 pub fn syntax(&self, file<|>)
120 }
121 ",
122 ),
123 @r###"
124 [
125 CompletionItem {
126 label: "file_id: FileId",
127 source_range: [289; 293),
128 delete: [289; 293),
129 insert: "file_id: FileId",
130 lookup: "file_id",
131 },
132 ]
133 "###
134 );
135 }
136}
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
new file mode 100644
index 000000000..eb7cd9ac2
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -0,0 +1,781 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{
4 ast::{self, LoopBodyOwner},
5 match_ast, AstNode,
6 SyntaxKind::*,
7 SyntaxToken,
8};
9
10use crate::completion::{
11 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
12};
13
14pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
15 // complete keyword "crate" in use stmt
16 let source_range = ctx.source_range();
17 match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
18 (Some(_), None) => {
19 CompletionItem::new(CompletionKind::Keyword, source_range, "crate")
20 .kind(CompletionItemKind::Keyword)
21 .insert_text("crate::")
22 .add_to(acc);
23 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
24 .kind(CompletionItemKind::Keyword)
25 .add_to(acc);
26 CompletionItem::new(CompletionKind::Keyword, source_range, "super")
27 .kind(CompletionItemKind::Keyword)
28 .insert_text("super::")
29 .add_to(acc);
30 }
31 (Some(_), Some(_)) => {
32 CompletionItem::new(CompletionKind::Keyword, source_range, "self")
33 .kind(CompletionItemKind::Keyword)
34 .add_to(acc);
35 CompletionItem::new(CompletionKind::Keyword, source_range, "super")
36 .kind(CompletionItemKind::Keyword)
37 .insert_text("super::")
38 .add_to(acc);
39 }
40 _ => {}
41 }
42}
43
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
45 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
46 .kind(CompletionItemKind::Keyword)
47 .insert_snippet(snippet)
48 .build()
49}
50
51pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
52 if !ctx.is_trivial_path {
53 return;
54 }
55
56 let fn_def = match &ctx.function_syntax {
57 Some(it) => it,
58 None => return,
59 };
60 acc.add(keyword(ctx, "if", "if $0 {}"));
61 acc.add(keyword(ctx, "match", "match $0 {}"));
62 acc.add(keyword(ctx, "while", "while $0 {}"));
63 acc.add(keyword(ctx, "loop", "loop {$0}"));
64
65 if ctx.after_if {
66 acc.add(keyword(ctx, "else", "else {$0}"));
67 acc.add(keyword(ctx, "else if", "else if $0 {}"));
68 }
69 if is_in_loop_body(&ctx.token) {
70 if ctx.can_be_stmt {
71 acc.add(keyword(ctx, "continue", "continue;"));
72 acc.add(keyword(ctx, "break", "break;"));
73 } else {
74 acc.add(keyword(ctx, "continue", "continue"));
75 acc.add(keyword(ctx, "break", "break"));
76 }
77 }
78 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
79}
80
81fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
82 for node in leaf.parent().ancestors() {
83 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
84 break;
85 }
86 let loop_body = match_ast! {
87 match node {
88 ast::ForExpr(it) => { it.loop_body() },
89 ast::WhileExpr(it) => { it.loop_body() },
90 ast::LoopExpr(it) => { it.loop_body() },
91 _ => None,
92 }
93 };
94 if let Some(body) = loop_body {
95 if leaf.text_range().is_subrange(&body.syntax().text_range()) {
96 return true;
97 }
98 }
99 }
100 false
101}
102
103fn complete_return(
104 ctx: &CompletionContext,
105 fn_def: &ast::FnDef,
106 can_be_stmt: bool,
107) -> Option<CompletionItem> {
108 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
109 (true, true) => "return $0;",
110 (true, false) => "return;",
111 (false, true) => "return $0",
112 (false, false) => "return",
113 };
114 Some(keyword(ctx, "return", snip))
115}
116
117#[cfg(test)]
118mod tests {
119 use crate::completion::{do_completion, CompletionItem, CompletionKind};
120 use insta::assert_debug_snapshot;
121
122 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> {
123 do_completion(code, CompletionKind::Keyword)
124 }
125
126 #[test]
127 fn completes_keywords_in_use_stmt() {
128 assert_debug_snapshot!(
129 do_keyword_completion(
130 r"
131 use <|>
132 ",
133 ),
134 @r###"
135 [
136 CompletionItem {
137 label: "crate",
138 source_range: [21; 21),
139 delete: [21; 21),
140 insert: "crate::",
141 kind: Keyword,
142 },
143 CompletionItem {
144 label: "self",
145 source_range: [21; 21),
146 delete: [21; 21),
147 insert: "self",
148 kind: Keyword,
149 },
150 CompletionItem {
151 label: "super",
152 source_range: [21; 21),
153 delete: [21; 21),
154 insert: "super::",
155 kind: Keyword,
156 },
157 ]
158 "###
159 );
160
161 assert_debug_snapshot!(
162 do_keyword_completion(
163 r"
164 use a::<|>
165 ",
166 ),
167 @r###"
168 [
169 CompletionItem {
170 label: "self",
171 source_range: [24; 24),
172 delete: [24; 24),
173 insert: "self",
174 kind: Keyword,
175 },
176 CompletionItem {
177 label: "super",
178 source_range: [24; 24),
179 delete: [24; 24),
180 insert: "super::",
181 kind: Keyword,
182 },
183 ]
184 "###
185 );
186
187 assert_debug_snapshot!(
188 do_keyword_completion(
189 r"
190 use a::{b, <|>}
191 ",
192 ),
193 @r###"
194 [
195 CompletionItem {
196 label: "self",
197 source_range: [28; 28),
198 delete: [28; 28),
199 insert: "self",
200 kind: Keyword,
201 },
202 CompletionItem {
203 label: "super",
204 source_range: [28; 28),
205 delete: [28; 28),
206 insert: "super::",
207 kind: Keyword,
208 },
209 ]
210 "###
211 );
212 }
213
214 #[test]
215 fn completes_various_keywords_in_function() {
216 assert_debug_snapshot!(
217 do_keyword_completion(
218 r"
219 fn quux() {
220 <|>
221 }
222 ",
223 ),
224 @r###"
225 [
226 CompletionItem {
227 label: "if",
228 source_range: [49; 49),
229 delete: [49; 49),
230 insert: "if $0 {}",
231 kind: Keyword,
232 },
233 CompletionItem {
234 label: "loop",
235 source_range: [49; 49),
236 delete: [49; 49),
237 insert: "loop {$0}",
238 kind: Keyword,
239 },
240 CompletionItem {
241 label: "match",
242 source_range: [49; 49),
243 delete: [49; 49),
244 insert: "match $0 {}",
245 kind: Keyword,
246 },
247 CompletionItem {
248 label: "return",
249 source_range: [49; 49),
250 delete: [49; 49),
251 insert: "return;",
252 kind: Keyword,
253 },
254 CompletionItem {
255 label: "while",
256 source_range: [49; 49),
257 delete: [49; 49),
258 insert: "while $0 {}",
259 kind: Keyword,
260 },
261 ]
262 "###
263 );
264 }
265
266 #[test]
267 fn completes_else_after_if() {
268 assert_debug_snapshot!(
269 do_keyword_completion(
270 r"
271 fn quux() {
272 if true {
273 ()
274 } <|>
275 }
276 ",
277 ),
278 @r###"
279 [
280 CompletionItem {
281 label: "else",
282 source_range: [108; 108),
283 delete: [108; 108),
284 insert: "else {$0}",
285 kind: Keyword,
286 },
287 CompletionItem {
288 label: "else if",
289 source_range: [108; 108),
290 delete: [108; 108),
291 insert: "else if $0 {}",
292 kind: Keyword,
293 },
294 CompletionItem {
295 label: "if",
296 source_range: [108; 108),
297 delete: [108; 108),
298 insert: "if $0 {}",
299 kind: Keyword,
300 },
301 CompletionItem {
302 label: "loop",
303 source_range: [108; 108),
304 delete: [108; 108),
305 insert: "loop {$0}",
306 kind: Keyword,
307 },
308 CompletionItem {
309 label: "match",
310 source_range: [108; 108),
311 delete: [108; 108),
312 insert: "match $0 {}",
313 kind: Keyword,
314 },
315 CompletionItem {
316 label: "return",
317 source_range: [108; 108),
318 delete: [108; 108),
319 insert: "return;",
320 kind: Keyword,
321 },
322 CompletionItem {
323 label: "while",
324 source_range: [108; 108),
325 delete: [108; 108),
326 insert: "while $0 {}",
327 kind: Keyword,
328 },
329 ]
330 "###
331 );
332 }
333
334 #[test]
335 fn test_completion_return_value() {
336 assert_debug_snapshot!(
337 do_keyword_completion(
338 r"
339 fn quux() -> i32 {
340 <|>
341 92
342 }
343 ",
344 ),
345 @r###"
346 [
347 CompletionItem {
348 label: "if",
349 source_range: [56; 56),
350 delete: [56; 56),
351 insert: "if $0 {}",
352 kind: Keyword,
353 },
354 CompletionItem {
355 label: "loop",
356 source_range: [56; 56),
357 delete: [56; 56),
358 insert: "loop {$0}",
359 kind: Keyword,
360 },
361 CompletionItem {
362 label: "match",
363 source_range: [56; 56),
364 delete: [56; 56),
365 insert: "match $0 {}",
366 kind: Keyword,
367 },
368 CompletionItem {
369 label: "return",
370 source_range: [56; 56),
371 delete: [56; 56),
372 insert: "return $0;",
373 kind: Keyword,
374 },
375 CompletionItem {
376 label: "while",
377 source_range: [56; 56),
378 delete: [56; 56),
379 insert: "while $0 {}",
380 kind: Keyword,
381 },
382 ]
383 "###
384 );
385 assert_debug_snapshot!(
386 do_keyword_completion(
387 r"
388 fn quux() {
389 <|>
390 92
391 }
392 ",
393 ),
394 @r###"
395 [
396 CompletionItem {
397 label: "if",
398 source_range: [49; 49),
399 delete: [49; 49),
400 insert: "if $0 {}",
401 kind: Keyword,
402 },
403 CompletionItem {
404 label: "loop",
405 source_range: [49; 49),
406 delete: [49; 49),
407 insert: "loop {$0}",
408 kind: Keyword,
409 },
410 CompletionItem {
411 label: "match",
412 source_range: [49; 49),
413 delete: [49; 49),
414 insert: "match $0 {}",
415 kind: Keyword,
416 },
417 CompletionItem {
418 label: "return",
419 source_range: [49; 49),
420 delete: [49; 49),
421 insert: "return;",
422 kind: Keyword,
423 },
424 CompletionItem {
425 label: "while",
426 source_range: [49; 49),
427 delete: [49; 49),
428 insert: "while $0 {}",
429 kind: Keyword,
430 },
431 ]
432 "###
433 );
434 }
435
436 #[test]
437 fn dont_add_semi_after_return_if_not_a_statement() {
438 assert_debug_snapshot!(
439 do_keyword_completion(
440 r"
441 fn quux() -> i32 {
442 match () {
443 () => <|>
444 }
445 }
446 ",
447 ),
448 @r###"
449 [
450 CompletionItem {
451 label: "if",
452 source_range: [97; 97),
453 delete: [97; 97),
454 insert: "if $0 {}",
455 kind: Keyword,
456 },
457 CompletionItem {
458 label: "loop",
459 source_range: [97; 97),
460 delete: [97; 97),
461 insert: "loop {$0}",
462 kind: Keyword,
463 },
464 CompletionItem {
465 label: "match",
466 source_range: [97; 97),
467 delete: [97; 97),
468 insert: "match $0 {}",
469 kind: Keyword,
470 },
471 CompletionItem {
472 label: "return",
473 source_range: [97; 97),
474 delete: [97; 97),
475 insert: "return $0",
476 kind: Keyword,
477 },
478 CompletionItem {
479 label: "while",
480 source_range: [97; 97),
481 delete: [97; 97),
482 insert: "while $0 {}",
483 kind: Keyword,
484 },
485 ]
486 "###
487 );
488 }
489
490 #[test]
491 fn last_return_in_block_has_semi() {
492 assert_debug_snapshot!(
493 do_keyword_completion(
494 r"
495 fn quux() -> i32 {
496 if condition {
497 <|>
498 }
499 }
500 ",
501 ),
502 @r###"
503 [
504 CompletionItem {
505 label: "if",
506 source_range: [95; 95),
507 delete: [95; 95),
508 insert: "if $0 {}",
509 kind: Keyword,
510 },
511 CompletionItem {
512 label: "loop",
513 source_range: [95; 95),
514 delete: [95; 95),
515 insert: "loop {$0}",
516 kind: Keyword,
517 },
518 CompletionItem {
519 label: "match",
520 source_range: [95; 95),
521 delete: [95; 95),
522 insert: "match $0 {}",
523 kind: Keyword,
524 },
525 CompletionItem {
526 label: "return",
527 source_range: [95; 95),
528 delete: [95; 95),
529 insert: "return $0;",
530 kind: Keyword,
531 },
532 CompletionItem {
533 label: "while",
534 source_range: [95; 95),
535 delete: [95; 95),
536 insert: "while $0 {}",
537 kind: Keyword,
538 },
539 ]
540 "###
541 );
542 assert_debug_snapshot!(
543 do_keyword_completion(
544 r"
545 fn quux() -> i32 {
546 if condition {
547 <|>
548 }
549 let x = 92;
550 x
551 }
552 ",
553 ),
554 @r###"
555 [
556 CompletionItem {
557 label: "if",
558 source_range: [95; 95),
559 delete: [95; 95),
560 insert: "if $0 {}",
561 kind: Keyword,
562 },
563 CompletionItem {
564 label: "loop",
565 source_range: [95; 95),
566 delete: [95; 95),
567 insert: "loop {$0}",
568 kind: Keyword,
569 },
570 CompletionItem {
571 label: "match",
572 source_range: [95; 95),
573 delete: [95; 95),
574 insert: "match $0 {}",
575 kind: Keyword,
576 },
577 CompletionItem {
578 label: "return",
579 source_range: [95; 95),
580 delete: [95; 95),
581 insert: "return $0;",
582 kind: Keyword,
583 },
584 CompletionItem {
585 label: "while",
586 source_range: [95; 95),
587 delete: [95; 95),
588 insert: "while $0 {}",
589 kind: Keyword,
590 },
591 ]
592 "###
593 );
594 }
595
596 #[test]
597 fn completes_break_and_continue_in_loops() {
598 assert_debug_snapshot!(
599 do_keyword_completion(
600 r"
601 fn quux() -> i32 {
602 loop { <|> }
603 }
604 ",
605 ),
606 @r###"
607 [
608 CompletionItem {
609 label: "break",
610 source_range: [63; 63),
611 delete: [63; 63),
612 insert: "break;",
613 kind: Keyword,
614 },
615 CompletionItem {
616 label: "continue",
617 source_range: [63; 63),
618 delete: [63; 63),
619 insert: "continue;",
620 kind: Keyword,
621 },
622 CompletionItem {
623 label: "if",
624 source_range: [63; 63),
625 delete: [63; 63),
626 insert: "if $0 {}",
627 kind: Keyword,
628 },
629 CompletionItem {
630 label: "loop",
631 source_range: [63; 63),
632 delete: [63; 63),
633 insert: "loop {$0}",
634 kind: Keyword,
635 },
636 CompletionItem {
637 label: "match",
638 source_range: [63; 63),
639 delete: [63; 63),
640 insert: "match $0 {}",
641 kind: Keyword,
642 },
643 CompletionItem {
644 label: "return",
645 source_range: [63; 63),
646 delete: [63; 63),
647 insert: "return $0;",
648 kind: Keyword,
649 },
650 CompletionItem {
651 label: "while",
652 source_range: [63; 63),
653 delete: [63; 63),
654 insert: "while $0 {}",
655 kind: Keyword,
656 },
657 ]
658 "###
659 );
660
661 // No completion: lambda isolates control flow
662 assert_debug_snapshot!(
663 do_keyword_completion(
664 r"
665 fn quux() -> i32 {
666 loop { || { <|> } }
667 }
668 ",
669 ),
670 @r###"
671 [
672 CompletionItem {
673 label: "if",
674 source_range: [68; 68),
675 delete: [68; 68),
676 insert: "if $0 {}",
677 kind: Keyword,
678 },
679 CompletionItem {
680 label: "loop",
681 source_range: [68; 68),
682 delete: [68; 68),
683 insert: "loop {$0}",
684 kind: Keyword,
685 },
686 CompletionItem {
687 label: "match",
688 source_range: [68; 68),
689 delete: [68; 68),
690 insert: "match $0 {}",
691 kind: Keyword,
692 },
693 CompletionItem {
694 label: "return",
695 source_range: [68; 68),
696 delete: [68; 68),
697 insert: "return $0;",
698 kind: Keyword,
699 },
700 CompletionItem {
701 label: "while",
702 source_range: [68; 68),
703 delete: [68; 68),
704 insert: "while $0 {}",
705 kind: Keyword,
706 },
707 ]
708 "###
709 );
710 }
711
712 #[test]
713 fn no_semi_after_break_continue_in_expr() {
714 assert_debug_snapshot!(
715 do_keyword_completion(
716 r"
717 fn f() {
718 loop {
719 match () {
720 () => br<|>
721 }
722 }
723 }
724 ",
725 ),
726 @r###"
727 [
728 CompletionItem {
729 label: "break",
730 source_range: [122; 124),
731 delete: [122; 124),
732 insert: "break",
733 kind: Keyword,
734 },
735 CompletionItem {
736 label: "continue",
737 source_range: [122; 124),
738 delete: [122; 124),
739 insert: "continue",
740 kind: Keyword,
741 },
742 CompletionItem {
743 label: "if",
744 source_range: [122; 124),
745 delete: [122; 124),
746 insert: "if $0 {}",
747 kind: Keyword,
748 },
749 CompletionItem {
750 label: "loop",
751 source_range: [122; 124),
752 delete: [122; 124),
753 insert: "loop {$0}",
754 kind: Keyword,
755 },
756 CompletionItem {
757 label: "match",
758 source_range: [122; 124),
759 delete: [122; 124),
760 insert: "match $0 {}",
761 kind: Keyword,
762 },
763 CompletionItem {
764 label: "return",
765 source_range: [122; 124),
766 delete: [122; 124),
767 insert: "return",
768 kind: Keyword,
769 },
770 CompletionItem {
771 label: "while",
772 source_range: [122; 124),
773 delete: [122; 124),
774 insert: "while $0 {}",
775 kind: Keyword,
776 },
777 ]
778 "###
779 )
780 }
781}
diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
new file mode 100644
index 000000000..faadd1e3f
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs
@@ -0,0 +1,143 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) {
6 // Show only macros in top level.
7 if ctx.is_new_item {
8 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
9 if let hir::ScopeDef::MacroDef(mac) = res {
10 acc.add_macro(ctx, Some(name.to_string()), mac);
11 }
12 })
13 }
14}
15
16#[cfg(test)]
17mod tests {
18 use crate::completion::{do_completion, CompletionItem, CompletionKind};
19 use insta::assert_debug_snapshot;
20
21 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
22 do_completion(code, CompletionKind::Reference)
23 }
24
25 #[test]
26 fn completes_macros_as_item() {
27 assert_debug_snapshot!(
28 do_reference_completion(
29 "
30 //- /main.rs
31 macro_rules! foo {
32 () => {}
33 }
34
35 fn foo() {}
36
37 <|>
38 "
39 ),
40 @r###"
41 [
42 CompletionItem {
43 label: "foo!",
44 source_range: [46; 46),
45 delete: [46; 46),
46 insert: "foo!($0)",
47 kind: Macro,
48 detail: "macro_rules! foo",
49 },
50 ]
51 "###
52 );
53 }
54
55 #[test]
56 fn completes_vec_macros_with_square_brackets() {
57 assert_debug_snapshot!(
58 do_reference_completion(
59 "
60 //- /main.rs
61 /// Creates a [`Vec`] containing the arguments.
62 ///
63 /// - Create a [`Vec`] containing a given list of elements:
64 ///
65 /// ```
66 /// let v = vec![1, 2, 3];
67 /// assert_eq!(v[0], 1);
68 /// assert_eq!(v[1], 2);
69 /// assert_eq!(v[2], 3);
70 /// ```
71 macro_rules! vec {
72 () => {}
73 }
74
75 fn foo() {}
76
77 <|>
78 "
79 ),
80 @r###"
81 [
82 CompletionItem {
83 label: "vec!",
84 source_range: [280; 280),
85 delete: [280; 280),
86 insert: "vec![$0]",
87 kind: Macro,
88 detail: "macro_rules! vec",
89 documentation: Documentation(
90 "Creates a [`Vec`] containing the arguments.\n\n- Create a [`Vec`] containing a given list of elements:\n\n```\nlet v = vec![1, 2, 3];\nassert_eq!(v[0], 1);\nassert_eq!(v[1], 2);\nassert_eq!(v[2], 3);\n```",
91 ),
92 },
93 ]
94 "###
95 );
96 }
97
98 #[test]
99 fn completes_macros_braces_guessing() {
100 assert_debug_snapshot!(
101 do_reference_completion(
102 "
103 //- /main.rs
104 /// Foo
105 ///
106 /// Not call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.
107 /// Call as `let _=foo! { hello world };`
108 macro_rules! foo {
109 () => {}
110 }
111
112 fn main() {
113 <|>
114 }
115 "
116 ),
117 @r###"
118 [
119 CompletionItem {
120 label: "foo!",
121 source_range: [163; 163),
122 delete: [163; 163),
123 insert: "foo! {$0}",
124 kind: Macro,
125 detail: "macro_rules! foo",
126 documentation: Documentation(
127 "Foo\n\nNot call `fooo!()` `fooo!()`, or `_foo![]` `_foo![]`.\nCall as `let _=foo! { hello world };`",
128 ),
129 },
130 CompletionItem {
131 label: "main()",
132 source_range: [163; 163),
133 delete: [163; 163),
134 insert: "main()$0",
135 kind: Function,
136 lookup: "main",
137 detail: "fn main()",
138 },
139 ]
140 "###
141 );
142 }
143}
diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs
new file mode 100644
index 000000000..89e0009a1
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_path.rs
@@ -0,0 +1,785 @@
1//! FIXME: write short doc here
2
3use hir::{Adt, Either, HasSource, PathResolution};
4use ra_syntax::AstNode;
5use test_utils::tested_by;
6
7use crate::completion::{CompletionContext, Completions};
8
9pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
10 let path = match &ctx.path_prefix {
11 Some(path) => path.clone(),
12 _ => return,
13 };
14 let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path) {
15 Some(PathResolution::Def(def)) => def,
16 _ => return,
17 };
18 match def {
19 hir::ModuleDef::Module(module) => {
20 let module_scope = module.scope(ctx.db);
21 for (name, def, import) in module_scope {
22 if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def {
23 if ctx.use_item_syntax.is_some() {
24 tested_by!(dont_complete_primitive_in_use);
25 continue;
26 }
27 }
28 if Some(module) == ctx.module {
29 if let Some(import) = import {
30 if let Either::A(use_tree) = import.source(ctx.db).value {
31 if use_tree.syntax().text_range().contains_inclusive(ctx.offset) {
32 // for `use self::foo<|>`, don't suggest `foo` as a completion
33 tested_by!(dont_complete_current_use);
34 continue;
35 }
36 }
37 }
38 }
39 acc.add_resolution(ctx, name.to_string(), &def);
40 }
41 }
42 hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => {
43 if let hir::ModuleDef::Adt(Adt::Enum(e)) = def {
44 for variant in e.variants(ctx.db) {
45 acc.add_enum_variant(ctx, variant);
46 }
47 }
48 let ty = match def {
49 hir::ModuleDef::Adt(adt) => adt.ty(ctx.db),
50 hir::ModuleDef::TypeAlias(a) => a.ty(ctx.db),
51 _ => unreachable!(),
52 };
53 ctx.analyzer.iterate_path_candidates(ctx.db, &ty, None, |_ty, item| {
54 match item {
55 hir::AssocItem::Function(func) => {
56 if !func.has_self_param(ctx.db) {
57 acc.add_function(ctx, func);
58 }
59 }
60 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
61 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
62 }
63 None::<()>
64 });
65 // Iterate assoc types separately
66 // FIXME: complete T::AssocType
67 let krate = ctx.module.map(|m| m.krate());
68 if let Some(krate) = krate {
69 ty.iterate_impl_items(ctx.db, krate, |item| {
70 match item {
71 hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => {}
72 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
73 }
74 None::<()>
75 });
76 }
77 }
78 hir::ModuleDef::Trait(t) => {
79 for item in t.items(ctx.db) {
80 match item {
81 hir::AssocItem::Function(func) => {
82 if !func.has_self_param(ctx.db) {
83 acc.add_function(ctx, func);
84 }
85 }
86 hir::AssocItem::Const(ct) => acc.add_const(ctx, ct),
87 hir::AssocItem::TypeAlias(ty) => acc.add_type_alias(ctx, ty),
88 }
89 }
90 }
91 _ => {}
92 };
93}
94
95#[cfg(test)]
96mod tests {
97 use test_utils::covers;
98
99 use crate::completion::{do_completion, CompletionItem, CompletionKind};
100 use insta::assert_debug_snapshot;
101
102 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
103 do_completion(code, CompletionKind::Reference)
104 }
105
106 #[test]
107 fn dont_complete_current_use() {
108 covers!(dont_complete_current_use);
109 let completions = do_completion(r"use self::foo<|>;", CompletionKind::Reference);
110 assert!(completions.is_empty());
111 }
112
113 #[test]
114 fn dont_complete_current_use_in_braces_with_glob() {
115 let completions = do_completion(
116 r"
117 mod foo { pub struct S; }
118 use self::{foo::*, bar<|>};
119 ",
120 CompletionKind::Reference,
121 );
122 assert_eq!(completions.len(), 2);
123 }
124
125 #[test]
126 fn dont_complete_primitive_in_use() {
127 covers!(dont_complete_primitive_in_use);
128 let completions = do_completion(r"use self::<|>;", CompletionKind::BuiltinType);
129 assert!(completions.is_empty());
130 }
131
132 #[test]
133 fn completes_primitives() {
134 let completions =
135 do_completion(r"fn main() { let _: <|> = 92; }", CompletionKind::BuiltinType);
136 assert_eq!(completions.len(), 17);
137 }
138
139 #[test]
140 fn completes_mod_with_docs() {
141 assert_debug_snapshot!(
142 do_reference_completion(
143 r"
144 use self::my<|>;
145
146 /// Some simple
147 /// docs describing `mod my`.
148 mod my {
149 struct Bar;
150 }
151 "
152 ),
153 @r###"
154 [
155 CompletionItem {
156 label: "my",
157 source_range: [27; 29),
158 delete: [27; 29),
159 insert: "my",
160 kind: Module,
161 documentation: Documentation(
162 "Some simple\ndocs describing `mod my`.",
163 ),
164 },
165 ]
166 "###
167 );
168 }
169
170 #[test]
171 fn completes_use_item_starting_with_self() {
172 assert_debug_snapshot!(
173 do_reference_completion(
174 r"
175 use self::m::<|>;
176
177 mod m {
178 struct Bar;
179 }
180 "
181 ),
182 @r###"
183 [
184 CompletionItem {
185 label: "Bar",
186 source_range: [30; 30),
187 delete: [30; 30),
188 insert: "Bar",
189 kind: Struct,
190 },
191 ]
192 "###
193 );
194 }
195
196 #[test]
197 fn completes_use_item_starting_with_crate() {
198 assert_debug_snapshot!(
199 do_reference_completion(
200 "
201 //- /lib.rs
202 mod foo;
203 struct Spam;
204 //- /foo.rs
205 use crate::Sp<|>
206 "
207 ),
208 @r###"
209 [
210 CompletionItem {
211 label: "Spam",
212 source_range: [11; 13),
213 delete: [11; 13),
214 insert: "Spam",
215 kind: Struct,
216 },
217 CompletionItem {
218 label: "foo",
219 source_range: [11; 13),
220 delete: [11; 13),
221 insert: "foo",
222 kind: Module,
223 },
224 ]
225 "###
226 );
227 }
228
229 #[test]
230 fn completes_nested_use_tree() {
231 assert_debug_snapshot!(
232 do_reference_completion(
233 "
234 //- /lib.rs
235 mod foo;
236 struct Spam;
237 //- /foo.rs
238 use crate::{Sp<|>};
239 "
240 ),
241 @r###"
242 [
243 CompletionItem {
244 label: "Spam",
245 source_range: [12; 14),
246 delete: [12; 14),
247 insert: "Spam",
248 kind: Struct,
249 },
250 CompletionItem {
251 label: "foo",
252 source_range: [12; 14),
253 delete: [12; 14),
254 insert: "foo",
255 kind: Module,
256 },
257 ]
258 "###
259 );
260 }
261
262 #[test]
263 fn completes_deeply_nested_use_tree() {
264 assert_debug_snapshot!(
265 do_reference_completion(
266 "
267 //- /lib.rs
268 mod foo;
269 pub mod bar {
270 pub mod baz {
271 pub struct Spam;
272 }
273 }
274 //- /foo.rs
275 use crate::{bar::{baz::Sp<|>}};
276 "
277 ),
278 @r###"
279 [
280 CompletionItem {
281 label: "Spam",
282 source_range: [23; 25),
283 delete: [23; 25),
284 insert: "Spam",
285 kind: Struct,
286 },
287 ]
288 "###
289 );
290 }
291
292 #[test]
293 fn completes_enum_variant() {
294 assert_debug_snapshot!(
295 do_reference_completion(
296 "
297 //- /lib.rs
298 /// An enum
299 enum E {
300 /// Foo Variant
301 Foo,
302 /// Bar Variant with i32
303 Bar(i32)
304 }
305 fn foo() { let _ = E::<|> }
306 "
307 ),
308 @r###"
309 [
310 CompletionItem {
311 label: "Bar",
312 source_range: [116; 116),
313 delete: [116; 116),
314 insert: "Bar",
315 kind: EnumVariant,
316 detail: "(i32)",
317 documentation: Documentation(
318 "Bar Variant with i32",
319 ),
320 },
321 CompletionItem {
322 label: "Foo",
323 source_range: [116; 116),
324 delete: [116; 116),
325 insert: "Foo",
326 kind: EnumVariant,
327 detail: "()",
328 documentation: Documentation(
329 "Foo Variant",
330 ),
331 },
332 ]
333 "###
334 );
335 }
336
337 #[test]
338 fn completes_enum_variant_with_details() {
339 assert_debug_snapshot!(
340 do_reference_completion(
341 "
342 //- /lib.rs
343 struct S { field: u32 }
344 /// An enum
345 enum E {
346 /// Foo Variant (empty)
347 Foo,
348 /// Bar Variant with i32 and u32
349 Bar(i32, u32),
350 ///
351 S(S),
352 }
353 fn foo() { let _ = E::<|> }
354 "
355 ),
356 @r###"
357 [
358 CompletionItem {
359 label: "Bar",
360 source_range: [180; 180),
361 delete: [180; 180),
362 insert: "Bar",
363 kind: EnumVariant,
364 detail: "(i32, u32)",
365 documentation: Documentation(
366 "Bar Variant with i32 and u32",
367 ),
368 },
369 CompletionItem {
370 label: "Foo",
371 source_range: [180; 180),
372 delete: [180; 180),
373 insert: "Foo",
374 kind: EnumVariant,
375 detail: "()",
376 documentation: Documentation(
377 "Foo Variant (empty)",
378 ),
379 },
380 CompletionItem {
381 label: "S",
382 source_range: [180; 180),
383 delete: [180; 180),
384 insert: "S",
385 kind: EnumVariant,
386 detail: "(S)",
387 documentation: Documentation(
388 "",
389 ),
390 },
391 ]
392 "###
393 );
394 }
395
396 #[test]
397 fn completes_struct_associated_method() {
398 assert_debug_snapshot!(
399 do_reference_completion(
400 "
401 //- /lib.rs
402 /// A Struct
403 struct S;
404
405 impl S {
406 /// An associated method
407 fn m() { }
408 }
409
410 fn foo() { let _ = S::<|> }
411 "
412 ),
413 @r###"
414 [
415 CompletionItem {
416 label: "m()",
417 source_range: [100; 100),
418 delete: [100; 100),
419 insert: "m()$0",
420 kind: Function,
421 lookup: "m",
422 detail: "fn m()",
423 documentation: Documentation(
424 "An associated method",
425 ),
426 },
427 ]
428 "###
429 );
430 }
431
432 #[test]
433 fn completes_struct_associated_const() {
434 assert_debug_snapshot!(
435 do_reference_completion(
436 "
437 //- /lib.rs
438 /// A Struct
439 struct S;
440
441 impl S {
442 /// An associated const
443 const C: i32 = 42;
444 }
445
446 fn foo() { let _ = S::<|> }
447 "
448 ),
449 @r###"
450 [
451 CompletionItem {
452 label: "C",
453 source_range: [107; 107),
454 delete: [107; 107),
455 insert: "C",
456 kind: Const,
457 detail: "const C: i32 = 42;",
458 documentation: Documentation(
459 "An associated const",
460 ),
461 },
462 ]
463 "###
464 );
465 }
466
467 #[test]
468 fn completes_struct_associated_type() {
469 assert_debug_snapshot!(
470 do_reference_completion(
471 "
472 //- /lib.rs
473 /// A Struct
474 struct S;
475
476 impl S {
477 /// An associated type
478 type T = i32;
479 }
480
481 fn foo() { let _ = S::<|> }
482 "
483 ),
484 @r###"
485 [
486 CompletionItem {
487 label: "T",
488 source_range: [101; 101),
489 delete: [101; 101),
490 insert: "T",
491 kind: TypeAlias,
492 detail: "type T = i32;",
493 documentation: Documentation(
494 "An associated type",
495 ),
496 },
497 ]
498 "###
499 );
500 }
501
502 #[test]
503 fn completes_enum_associated_method() {
504 assert_debug_snapshot!(
505 do_reference_completion(
506 "
507 //- /lib.rs
508 /// An enum
509 enum S {};
510
511 impl S {
512 /// An associated method
513 fn m() { }
514 }
515
516 fn foo() { let _ = S::<|> }
517 "
518 ),
519 @r###"
520 [
521 CompletionItem {
522 label: "m()",
523 source_range: [100; 100),
524 delete: [100; 100),
525 insert: "m()$0",
526 kind: Function,
527 lookup: "m",
528 detail: "fn m()",
529 documentation: Documentation(
530 "An associated method",
531 ),
532 },
533 ]
534 "###
535 );
536 }
537
538 #[test]
539 fn completes_union_associated_method() {
540 assert_debug_snapshot!(
541 do_reference_completion(
542 "
543 //- /lib.rs
544 /// A union
545 union U {};
546
547 impl U {
548 /// An associated method
549 fn m() { }
550 }
551
552 fn foo() { let _ = U::<|> }
553 "
554 ),
555 @r###"
556 [
557 CompletionItem {
558 label: "m()",
559 source_range: [101; 101),
560 delete: [101; 101),
561 insert: "m()$0",
562 kind: Function,
563 lookup: "m",
564 detail: "fn m()",
565 documentation: Documentation(
566 "An associated method",
567 ),
568 },
569 ]
570 "###
571 );
572 }
573
574 #[test]
575 fn completes_use_paths_across_crates() {
576 assert_debug_snapshot!(
577 do_reference_completion(
578 "
579 //- /main.rs
580 use foo::<|>;
581
582 //- /foo/lib.rs
583 pub mod bar {
584 pub struct S;
585 }
586 "
587 ),
588 @r###"
589 [
590 CompletionItem {
591 label: "bar",
592 source_range: [9; 9),
593 delete: [9; 9),
594 insert: "bar",
595 kind: Module,
596 },
597 ]
598 "###
599 );
600 }
601
602 #[test]
603 fn completes_trait_associated_method_1() {
604 assert_debug_snapshot!(
605 do_reference_completion(
606 "
607 //- /lib.rs
608 trait Trait {
609 /// A trait method
610 fn m();
611 }
612
613 fn foo() { let _ = Trait::<|> }
614 "
615 ),
616 @r###"
617 [
618 CompletionItem {
619 label: "m()",
620 source_range: [73; 73),
621 delete: [73; 73),
622 insert: "m()$0",
623 kind: Function,
624 lookup: "m",
625 detail: "fn m()",
626 documentation: Documentation(
627 "A trait method",
628 ),
629 },
630 ]
631 "###
632 );
633 }
634
635 #[test]
636 fn completes_trait_associated_method_2() {
637 assert_debug_snapshot!(
638 do_reference_completion(
639 "
640 //- /lib.rs
641 trait Trait {
642 /// A trait method
643 fn m();
644 }
645
646 struct S;
647 impl Trait for S {}
648
649 fn foo() { let _ = S::<|> }
650 "
651 ),
652 @r###"
653 [
654 CompletionItem {
655 label: "m()",
656 source_range: [99; 99),
657 delete: [99; 99),
658 insert: "m()$0",
659 kind: Function,
660 lookup: "m",
661 detail: "fn m()",
662 documentation: Documentation(
663 "A trait method",
664 ),
665 },
666 ]
667 "###
668 );
669 }
670
671 #[test]
672 fn completes_trait_associated_method_3() {
673 assert_debug_snapshot!(
674 do_reference_completion(
675 "
676 //- /lib.rs
677 trait Trait {
678 /// A trait method
679 fn m();
680 }
681
682 struct S;
683 impl Trait for S {}
684
685 fn foo() { let _ = <S as Trait>::<|> }
686 "
687 ),
688 @r###"
689 [
690 CompletionItem {
691 label: "m()",
692 source_range: [110; 110),
693 delete: [110; 110),
694 insert: "m()$0",
695 kind: Function,
696 lookup: "m",
697 detail: "fn m()",
698 documentation: Documentation(
699 "A trait method",
700 ),
701 },
702 ]
703 "###
704 );
705 }
706
707 #[test]
708 fn completes_type_alias() {
709 assert_debug_snapshot!(
710 do_reference_completion(
711 "
712 struct S;
713 impl S { fn foo() {} }
714 type T = S;
715 impl T { fn bar() {} }
716
717 fn main() {
718 T::<|>;
719 }
720 "
721 ),
722 @r###"
723 [
724 CompletionItem {
725 label: "bar()",
726 source_range: [185; 185),
727 delete: [185; 185),
728 insert: "bar()$0",
729 kind: Function,
730 lookup: "bar",
731 detail: "fn bar()",
732 },
733 CompletionItem {
734 label: "foo()",
735 source_range: [185; 185),
736 delete: [185; 185),
737 insert: "foo()$0",
738 kind: Function,
739 lookup: "foo",
740 detail: "fn foo()",
741 },
742 ]
743 "###
744 );
745 }
746
747 #[test]
748 fn completes_qualified_macros() {
749 assert_debug_snapshot!(
750 do_reference_completion(
751 "
752 #[macro_export]
753 macro_rules! foo {
754 () => {}
755 }
756
757 fn main() {
758 let _ = crate::<|>
759 }
760 "
761 ),
762 @r###"
763 [
764 CompletionItem {
765 label: "foo!",
766 source_range: [179; 179),
767 delete: [179; 179),
768 insert: "foo!($0)",
769 kind: Macro,
770 detail: "#[macro_export]\nmacro_rules! foo",
771 },
772 CompletionItem {
773 label: "main()",
774 source_range: [179; 179),
775 delete: [179; 179),
776 insert: "main()$0",
777 kind: Function,
778 lookup: "main",
779 detail: "fn main()",
780 },
781 ]
782 "###
783 );
784 }
785}
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
new file mode 100644
index 000000000..fd03b1c40
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -0,0 +1,89 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5/// Completes constats and paths in patterns.
6pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding {
8 return;
9 }
10 // FIXME: ideally, we should look at the type we are matching against and
11 // suggest variants + auto-imports
12 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
13 let def = match &res {
14 hir::ScopeDef::ModuleDef(def) => def,
15 _ => return,
16 };
17 match def {
18 hir::ModuleDef::Adt(hir::Adt::Enum(..))
19 | hir::ModuleDef::EnumVariant(..)
20 | hir::ModuleDef::Const(..)
21 | hir::ModuleDef::Module(..) => (),
22 _ => return,
23 }
24 acc.add_resolution(ctx, name.to_string(), &res)
25 });
26}
27
28#[cfg(test)]
29mod tests {
30 use crate::completion::{do_completion, CompletionItem, CompletionKind};
31 use insta::assert_debug_snapshot;
32
33 fn complete(code: &str) -> Vec<CompletionItem> {
34 do_completion(code, CompletionKind::Reference)
35 }
36
37 #[test]
38 fn completes_enum_variants_and_modules() {
39 let completions = complete(
40 r"
41 enum E { X }
42 use self::E::X;
43 const Z: E = E::X;
44 mod m {}
45
46 static FOO: E = E::X;
47 struct Bar { f: u32 }
48
49 fn foo() {
50 match E::X {
51 <|>
52 }
53 }
54 ",
55 );
56 assert_debug_snapshot!(completions, @r###"
57 [
58 CompletionItem {
59 label: "E",
60 source_range: [246; 246),
61 delete: [246; 246),
62 insert: "E",
63 kind: Enum,
64 },
65 CompletionItem {
66 label: "X",
67 source_range: [246; 246),
68 delete: [246; 246),
69 insert: "X",
70 kind: EnumVariant,
71 },
72 CompletionItem {
73 label: "Z",
74 source_range: [246; 246),
75 delete: [246; 246),
76 insert: "Z",
77 kind: Const,
78 },
79 CompletionItem {
80 label: "m",
81 source_range: [246; 246),
82 delete: [246; 246),
83 insert: "m",
84 kind: Module,
85 },
86 ]
87 "###);
88 }
89}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
new file mode 100644
index 000000000..646a30c76
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -0,0 +1,282 @@
1//! FIXME: write short doc here
2
3use ra_syntax::{ast::AstNode, TextRange, TextUnit};
4use ra_text_edit::TextEdit;
5
6use crate::{
7 completion::{
8 completion_context::CompletionContext,
9 completion_item::{Builder, CompletionKind, Completions},
10 },
11 CompletionItem,
12};
13
14pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
15 if ctx.db.feature_flags.get("completion.enable-postfix") == false {
16 return;
17 }
18
19 let dot_receiver = match &ctx.dot_receiver {
20 Some(it) => it,
21 None => return,
22 };
23
24 let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal {
25 let text = dot_receiver.syntax().text();
26 let without_dot = ..text.len() - TextUnit::of_char('.');
27 text.slice(without_dot).to_string()
28 } else {
29 dot_receiver.syntax().text().to_string()
30 };
31
32 let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) {
33 Some(it) => it,
34 None => return,
35 };
36
37 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
38 postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
39 .add_to(acc);
40 postfix_snippet(
41 ctx,
42 "while",
43 "while expr {}",
44 &format!("while {} {{\n$0\n}}", receiver_text),
45 )
46 .add_to(acc);
47 }
48
49 postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
50
51 postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
52 postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc);
53
54 postfix_snippet(
55 ctx,
56 "match",
57 "match expr {}",
58 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
59 )
60 .add_to(acc);
61
62 postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc);
63
64 postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text))
65 .add_to(acc);
66}
67
68fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
69 let edit = {
70 let receiver_range =
71 ctx.dot_receiver.as_ref().expect("no receiver available").syntax().text_range();
72 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
73 TextEdit::replace(delete_range, snippet.to_string())
74 };
75 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label)
76 .detail(detail)
77 .snippet_edit(edit)
78}
79
80#[cfg(test)]
81mod tests {
82 use insta::assert_debug_snapshot;
83
84 use crate::completion::{do_completion, CompletionItem, CompletionKind};
85
86 fn do_postfix_completion(code: &str) -> Vec<CompletionItem> {
87 do_completion(code, CompletionKind::Postfix)
88 }
89
90 #[test]
91 fn postfix_completion_works_for_trivial_path_expression() {
92 assert_debug_snapshot!(
93 do_postfix_completion(
94 r#"
95 fn main() {
96 let bar = true;
97 bar.<|>
98 }
99 "#,
100 ),
101 @r###"
102 [
103 CompletionItem {
104 label: "box",
105 source_range: [89; 89),
106 delete: [85; 89),
107 insert: "Box::new(bar)",
108 detail: "Box::new(expr)",
109 },
110 CompletionItem {
111 label: "dbg",
112 source_range: [89; 89),
113 delete: [85; 89),
114 insert: "dbg!(bar)",
115 detail: "dbg!(expr)",
116 },
117 CompletionItem {
118 label: "if",
119 source_range: [89; 89),
120 delete: [85; 89),
121 insert: "if bar {$0}",
122 detail: "if expr {}",
123 },
124 CompletionItem {
125 label: "match",
126 source_range: [89; 89),
127 delete: [85; 89),
128 insert: "match bar {\n ${1:_} => {$0\\},\n}",
129 detail: "match expr {}",
130 },
131 CompletionItem {
132 label: "not",
133 source_range: [89; 89),
134 delete: [85; 89),
135 insert: "!bar",
136 detail: "!expr",
137 },
138 CompletionItem {
139 label: "ref",
140 source_range: [89; 89),
141 delete: [85; 89),
142 insert: "&bar",
143 detail: "&expr",
144 },
145 CompletionItem {
146 label: "refm",
147 source_range: [89; 89),
148 delete: [85; 89),
149 insert: "&mut bar",
150 detail: "&mut expr",
151 },
152 CompletionItem {
153 label: "while",
154 source_range: [89; 89),
155 delete: [85; 89),
156 insert: "while bar {\n$0\n}",
157 detail: "while expr {}",
158 },
159 ]
160 "###
161 );
162 }
163
164 #[test]
165 fn some_postfix_completions_ignored() {
166 assert_debug_snapshot!(
167 do_postfix_completion(
168 r#"
169 fn main() {
170 let bar: u8 = 12;
171 bar.<|>
172 }
173 "#,
174 ),
175 @r###"
176 [
177 CompletionItem {
178 label: "box",
179 source_range: [91; 91),
180 delete: [87; 91),
181 insert: "Box::new(bar)",
182 detail: "Box::new(expr)",
183 },
184 CompletionItem {
185 label: "dbg",
186 source_range: [91; 91),
187 delete: [87; 91),
188 insert: "dbg!(bar)",
189 detail: "dbg!(expr)",
190 },
191 CompletionItem {
192 label: "match",
193 source_range: [91; 91),
194 delete: [87; 91),
195 insert: "match bar {\n ${1:_} => {$0\\},\n}",
196 detail: "match expr {}",
197 },
198 CompletionItem {
199 label: "not",
200 source_range: [91; 91),
201 delete: [87; 91),
202 insert: "!bar",
203 detail: "!expr",
204 },
205 CompletionItem {
206 label: "ref",
207 source_range: [91; 91),
208 delete: [87; 91),
209 insert: "&bar",
210 detail: "&expr",
211 },
212 CompletionItem {
213 label: "refm",
214 source_range: [91; 91),
215 delete: [87; 91),
216 insert: "&mut bar",
217 detail: "&mut expr",
218 },
219 ]
220 "###
221 );
222 }
223
224 #[test]
225 fn postfix_completion_works_for_ambiguous_float_literal() {
226 assert_debug_snapshot!(
227 do_postfix_completion(
228 r#"
229 fn main() {
230 42.<|>
231 }
232 "#,
233 ),
234 @r###"
235 [
236 CompletionItem {
237 label: "box",
238 source_range: [52; 52),
239 delete: [49; 52),
240 insert: "Box::new(42)",
241 detail: "Box::new(expr)",
242 },
243 CompletionItem {
244 label: "dbg",
245 source_range: [52; 52),
246 delete: [49; 52),
247 insert: "dbg!(42)",
248 detail: "dbg!(expr)",
249 },
250 CompletionItem {
251 label: "match",
252 source_range: [52; 52),
253 delete: [49; 52),
254 insert: "match 42 {\n ${1:_} => {$0\\},\n}",
255 detail: "match expr {}",
256 },
257 CompletionItem {
258 label: "not",
259 source_range: [52; 52),
260 delete: [49; 52),
261 insert: "!42",
262 detail: "!expr",
263 },
264 CompletionItem {
265 label: "ref",
266 source_range: [52; 52),
267 delete: [49; 52),
268 insert: "&42",
269 detail: "&expr",
270 },
271 CompletionItem {
272 label: "refm",
273 source_range: [52; 52),
274 delete: [49; 52),
275 insert: "&mut 42",
276 detail: "&mut expr",
277 },
278 ]
279 "###
280 );
281 }
282}
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs
new file mode 100644
index 000000000..577c394d2
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_record_literal.rs
@@ -0,0 +1,159 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5/// Complete fields in fields literals.
6pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) {
7 let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| {
8 Some((
9 ctx.analyzer.type_of(ctx.db, &it.clone().into())?,
10 ctx.analyzer.resolve_record_literal(it)?,
11 ))
12 }) {
13 Some(it) => it,
14 _ => return,
15 };
16
17 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
18 acc.add_field(ctx, field, &field_ty);
19 }
20}
21
22#[cfg(test)]
23mod tests {
24 use crate::completion::{do_completion, CompletionItem, CompletionKind};
25 use insta::assert_debug_snapshot;
26
27 fn complete(code: &str) -> Vec<CompletionItem> {
28 do_completion(code, CompletionKind::Reference)
29 }
30
31 #[test]
32 fn test_record_literal_deprecated_field() {
33 let completions = complete(
34 r"
35 struct A {
36 #[deprecated]
37 the_field: u32,
38 }
39 fn foo() {
40 A { the<|> }
41 }
42 ",
43 );
44 assert_debug_snapshot!(completions, @r###"
45 [
46 CompletionItem {
47 label: "the_field",
48 source_range: [142; 145),
49 delete: [142; 145),
50 insert: "the_field",
51 kind: Field,
52 detail: "u32",
53 deprecated: true,
54 },
55 ]
56 "###);
57 }
58
59 #[test]
60 fn test_record_literal_field() {
61 let completions = complete(
62 r"
63 struct A { the_field: u32 }
64 fn foo() {
65 A { the<|> }
66 }
67 ",
68 );
69 assert_debug_snapshot!(completions, @r###"
70 [
71 CompletionItem {
72 label: "the_field",
73 source_range: [83; 86),
74 delete: [83; 86),
75 insert: "the_field",
76 kind: Field,
77 detail: "u32",
78 },
79 ]
80 "###);
81 }
82
83 #[test]
84 fn test_record_literal_enum_variant() {
85 let completions = complete(
86 r"
87 enum E {
88 A { a: u32 }
89 }
90 fn foo() {
91 let _ = E::A { <|> }
92 }
93 ",
94 );
95 assert_debug_snapshot!(completions, @r###"
96 [
97 CompletionItem {
98 label: "a",
99 source_range: [119; 119),
100 delete: [119; 119),
101 insert: "a",
102 kind: Field,
103 detail: "u32",
104 },
105 ]
106 "###);
107 }
108
109 #[test]
110 fn test_record_literal_two_structs() {
111 let completions = complete(
112 r"
113 struct A { a: u32 }
114 struct B { b: u32 }
115
116 fn foo() {
117 let _: A = B { <|> }
118 }
119 ",
120 );
121 assert_debug_snapshot!(completions, @r###"
122 [
123 CompletionItem {
124 label: "b",
125 source_range: [119; 119),
126 delete: [119; 119),
127 insert: "b",
128 kind: Field,
129 detail: "u32",
130 },
131 ]
132 "###);
133 }
134
135 #[test]
136 fn test_record_literal_generic_struct() {
137 let completions = complete(
138 r"
139 struct A<T> { a: T }
140
141 fn foo() {
142 let _: A<u32> = A { <|> }
143 }
144 ",
145 );
146 assert_debug_snapshot!(completions, @r###"
147 [
148 CompletionItem {
149 label: "a",
150 source_range: [93; 93),
151 delete: [93; 93),
152 insert: "a",
153 kind: Field,
154 detail: "u32",
155 },
156 ]
157 "###);
158 }
159}
diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs
new file mode 100644
index 000000000..a56c7e3a1
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_record_pattern.rs
@@ -0,0 +1,93 @@
1//! FIXME: write short doc here
2
3use crate::completion::{CompletionContext, Completions};
4
5pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) {
6 let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| {
7 Some((
8 ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?,
9 ctx.analyzer.resolve_record_pattern(it)?,
10 ))
11 }) {
12 Some(it) => it,
13 _ => return,
14 };
15
16 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
17 acc.add_field(ctx, field, &field_ty);
18 }
19}
20
21#[cfg(test)]
22mod tests {
23 use crate::completion::{do_completion, CompletionItem, CompletionKind};
24 use insta::assert_debug_snapshot;
25
26 fn complete(code: &str) -> Vec<CompletionItem> {
27 do_completion(code, CompletionKind::Reference)
28 }
29
30 #[test]
31 fn test_record_pattern_field() {
32 let completions = complete(
33 r"
34 struct S { foo: u32 }
35
36 fn process(f: S) {
37 match f {
38 S { f<|>: 92 } => (),
39 }
40 }
41 ",
42 );
43 assert_debug_snapshot!(completions, @r###"
44 [
45 CompletionItem {
46 label: "foo",
47 source_range: [117; 118),
48 delete: [117; 118),
49 insert: "foo",
50 kind: Field,
51 detail: "u32",
52 },
53 ]
54 "###);
55 }
56
57 #[test]
58 fn test_record_pattern_enum_variant() {
59 let completions = complete(
60 r"
61 enum E {
62 S { foo: u32, bar: () }
63 }
64
65 fn process(e: E) {
66 match e {
67 E::S { <|> } => (),
68 }
69 }
70 ",
71 );
72 assert_debug_snapshot!(completions, @r###"
73 [
74 CompletionItem {
75 label: "bar",
76 source_range: [161; 161),
77 delete: [161; 161),
78 insert: "bar",
79 kind: Field,
80 detail: "()",
81 },
82 CompletionItem {
83 label: "foo",
84 source_range: [161; 161),
85 delete: [161; 161),
86 insert: "foo",
87 kind: Field,
88 detail: "u32",
89 },
90 ]
91 "###);
92 }
93}
diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs
new file mode 100644
index 000000000..d5739b58a
--- /dev/null
+++ b/crates/ra_ide/src/completion/complete_scope.rs
@@ -0,0 +1,876 @@
1//! FIXME: write short doc here
2
3use ra_assists::auto_import_text_edit;
4use ra_syntax::{ast, AstNode, SmolStr};
5use ra_text_edit::TextEditBuilder;
6use rustc_hash::FxHashMap;
7
8use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
9
10pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
11 if !ctx.is_trivial_path {
12 return;
13 }
14
15 ctx.analyzer.process_all_names(ctx.db, &mut |name, res| {
16 acc.add_resolution(ctx, name.to_string(), &res)
17 });
18
19 // auto-import
20 // We fetch ident from the original file, because we need to pre-filter auto-imports
21 if ast::NameRef::cast(ctx.token.parent()).is_some() {
22 let import_resolver = ImportResolver::new();
23 let import_names = import_resolver.all_names(ctx.token.text());
24 import_names.into_iter().for_each(|(name, path)| {
25 let edit = {
26 let mut builder = TextEditBuilder::default();
27 builder.replace(ctx.source_range(), name.to_string());
28 auto_import_text_edit(
29 &ctx.token.parent(),
30 &ctx.token.parent(),
31 &path,
32 &mut builder,
33 );
34 builder.finish()
35 };
36
37 // Hack: copied this check form conv.rs beacause auto import can produce edits
38 // that invalidate assert in conv_with.
39 if edit
40 .as_atoms()
41 .iter()
42 .filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
43 .all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
44 {
45 CompletionItem::new(
46 CompletionKind::Reference,
47 ctx.source_range(),
48 build_import_label(&name, &path),
49 )
50 .text_edit(edit)
51 .add_to(acc);
52 }
53 });
54 }
55}
56
57fn build_import_label(name: &str, path: &[SmolStr]) -> String {
58 let mut buf = String::with_capacity(64);
59 buf.push_str(name);
60 buf.push_str(" (");
61 fmt_import_path(path, &mut buf);
62 buf.push_str(")");
63 buf
64}
65
66fn fmt_import_path(path: &[SmolStr], buf: &mut String) {
67 let mut segments = path.iter();
68 if let Some(s) = segments.next() {
69 buf.push_str(&s);
70 }
71 for s in segments {
72 buf.push_str("::");
73 buf.push_str(&s);
74 }
75}
76
77#[derive(Debug, Clone, Default)]
78pub(crate) struct ImportResolver {
79 // todo: use fst crate or something like that
80 dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
81}
82
83impl ImportResolver {
84 pub(crate) fn new() -> Self {
85 let dummy_names = vec![
86 (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
87 (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
88 (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
89 (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
90 (
91 SmolStr::new("Debug"),
92 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
93 ),
94 (
95 SmolStr::new("Display"),
96 vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
97 ),
98 (
99 SmolStr::new("Hash"),
100 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
101 ),
102 (
103 SmolStr::new("Hasher"),
104 vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
105 ),
106 (
107 SmolStr::new("Iterator"),
108 vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
109 ),
110 ];
111
112 ImportResolver { dummy_names }
113 }
114
115 // Returns a map of importable items filtered by name.
116 // The map associates item name with its full path.
117 // todo: should return Resolutions
118 pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
119 if name.len() > 1 {
120 self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
121 } else {
122 FxHashMap::default()
123 }
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use crate::completion::{do_completion, CompletionItem, CompletionKind};
130 use insta::assert_debug_snapshot;
131
132 fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
133 do_completion(code, CompletionKind::Reference)
134 }
135
136 #[test]
137 fn completes_bindings_from_let() {
138 assert_debug_snapshot!(
139 do_reference_completion(
140 r"
141 fn quux(x: i32) {
142 let y = 92;
143 1 + <|>;
144 let z = ();
145 }
146 "
147 ),
148 @r###"
149 [
150 CompletionItem {
151 label: "quux(…)",
152 source_range: [91; 91),
153 delete: [91; 91),
154 insert: "quux($0)",
155 kind: Function,
156 lookup: "quux",
157 detail: "fn quux(x: i32)",
158 },
159 CompletionItem {
160 label: "x",
161 source_range: [91; 91),
162 delete: [91; 91),
163 insert: "x",
164 kind: Binding,
165 detail: "i32",
166 },
167 CompletionItem {
168 label: "y",
169 source_range: [91; 91),
170 delete: [91; 91),
171 insert: "y",
172 kind: Binding,
173 detail: "i32",
174 },
175 ]
176 "###
177 );
178 }
179
180 #[test]
181 fn completes_bindings_from_if_let() {
182 assert_debug_snapshot!(
183 do_reference_completion(
184 r"
185 fn quux() {
186 if let Some(x) = foo() {
187 let y = 92;
188 };
189 if let Some(a) = bar() {
190 let b = 62;
191 1 + <|>
192 }
193 }
194 "
195 ),
196 @r###"
197 [
198 CompletionItem {
199 label: "a",
200 source_range: [242; 242),
201 delete: [242; 242),
202 insert: "a",
203 kind: Binding,
204 },
205 CompletionItem {
206 label: "b",
207 source_range: [242; 242),
208 delete: [242; 242),
209 insert: "b",
210 kind: Binding,
211 detail: "i32",
212 },
213 CompletionItem {
214 label: "quux()",
215 source_range: [242; 242),
216 delete: [242; 242),
217 insert: "quux()$0",
218 kind: Function,
219 lookup: "quux",
220 detail: "fn quux()",
221 },
222 ]
223 "###
224 );
225 }
226
227 #[test]
228 fn completes_bindings_from_for() {
229 assert_debug_snapshot!(
230 do_reference_completion(
231 r"
232 fn quux() {
233 for x in &[1, 2, 3] {
234 <|>
235 }
236 }
237 "
238 ),
239 @r###"
240 [
241 CompletionItem {
242 label: "quux()",
243 source_range: [95; 95),
244 delete: [95; 95),
245 insert: "quux()$0",
246 kind: Function,
247 lookup: "quux",
248 detail: "fn quux()",
249 },
250 CompletionItem {
251 label: "x",
252 source_range: [95; 95),
253 delete: [95; 95),
254 insert: "x",
255 kind: Binding,
256 },
257 ]
258 "###
259 );
260 }
261
262 #[test]
263 fn completes_generic_params() {
264 assert_debug_snapshot!(
265 do_reference_completion(
266 r"
267 fn quux<T>() {
268 <|>
269 }
270 "
271 ),
272 @r###"
273 [
274 CompletionItem {
275 label: "T",
276 source_range: [52; 52),
277 delete: [52; 52),
278 insert: "T",
279 kind: TypeParam,
280 },
281 CompletionItem {
282 label: "quux()",
283 source_range: [52; 52),
284 delete: [52; 52),
285 insert: "quux()$0",
286 kind: Function,
287 lookup: "quux",
288 detail: "fn quux<T>()",
289 },
290 ]
291 "###
292 );
293 }
294
295 #[test]
296 fn completes_generic_params_in_struct() {
297 assert_debug_snapshot!(
298 do_reference_completion(
299 r"
300 struct X<T> {
301 x: <|>
302 }
303 "
304 ),
305 @r###"
306 [
307 CompletionItem {
308 label: "Self",
309 source_range: [54; 54),
310 delete: [54; 54),
311 insert: "Self",
312 kind: TypeParam,
313 },
314 CompletionItem {
315 label: "T",
316 source_range: [54; 54),
317 delete: [54; 54),
318 insert: "T",
319 kind: TypeParam,
320 },
321 CompletionItem {
322 label: "X<…>",
323 source_range: [54; 54),
324 delete: [54; 54),
325 insert: "X<$0>",
326 kind: Struct,
327 lookup: "X",
328 },
329 ]
330 "###
331 );
332 }
333
334 #[test]
335 fn completes_self_in_enum() {
336 assert_debug_snapshot!(
337 do_reference_completion(
338 r"
339 enum X {
340 Y(<|>)
341 }
342 "
343 ),
344 @r###"
345 [
346 CompletionItem {
347 label: "Self",
348 source_range: [48; 48),
349 delete: [48; 48),
350 insert: "Self",
351 kind: TypeParam,
352 },
353 CompletionItem {
354 label: "X",
355 source_range: [48; 48),
356 delete: [48; 48),
357 insert: "X",
358 kind: Enum,
359 },
360 ]
361 "###
362 );
363 }
364
365 #[test]
366 fn completes_module_items() {
367 assert_debug_snapshot!(
368 do_reference_completion(
369 r"
370 struct Foo;
371 enum Baz {}
372 fn quux() {
373 <|>
374 }
375 "
376 ),
377 @r###"
378 [
379 CompletionItem {
380 label: "Baz",
381 source_range: [105; 105),
382 delete: [105; 105),
383 insert: "Baz",
384 kind: Enum,
385 },
386 CompletionItem {
387 label: "Foo",
388 source_range: [105; 105),
389 delete: [105; 105),
390 insert: "Foo",
391 kind: Struct,
392 },
393 CompletionItem {
394 label: "quux()",
395 source_range: [105; 105),
396 delete: [105; 105),
397 insert: "quux()$0",
398 kind: Function,
399 lookup: "quux",
400 detail: "fn quux()",
401 },
402 ]
403 "###
404 );
405 }
406
407 #[test]
408 fn completes_extern_prelude() {
409 assert_debug_snapshot!(
410 do_reference_completion(
411 r"
412 //- /lib.rs
413 use <|>;
414
415 //- /other_crate/lib.rs
416 // nothing here
417 "
418 ),
419 @r###"
420 [
421 CompletionItem {
422 label: "other_crate",
423 source_range: [4; 4),
424 delete: [4; 4),
425 insert: "other_crate",
426 kind: Module,
427 },
428 ]
429 "###
430 );
431 }
432
433 #[test]
434 fn completes_module_items_in_nested_modules() {
435 assert_debug_snapshot!(
436 do_reference_completion(
437 r"
438 struct Foo;
439 mod m {
440 struct Bar;
441 fn quux() { <|> }
442 }
443 "
444 ),
445 @r###"
446 [
447 CompletionItem {
448 label: "Bar",
449 source_range: [117; 117),
450 delete: [117; 117),
451 insert: "Bar",
452 kind: Struct,
453 },
454 CompletionItem {
455 label: "quux()",
456 source_range: [117; 117),
457 delete: [117; 117),
458 insert: "quux()$0",
459 kind: Function,
460 lookup: "quux",
461 detail: "fn quux()",
462 },
463 ]
464 "###
465 );
466 }
467
468 #[test]
469 fn completes_return_type() {
470 assert_debug_snapshot!(
471 do_reference_completion(
472 r"
473 struct Foo;
474 fn x() -> <|>
475 "
476 ),
477 @r###"
478 [
479 CompletionItem {
480 label: "Foo",
481 source_range: [55; 55),
482 delete: [55; 55),
483 insert: "Foo",
484 kind: Struct,
485 },
486 CompletionItem {
487 label: "x()",
488 source_range: [55; 55),
489 delete: [55; 55),
490 insert: "x()$0",
491 kind: Function,
492 lookup: "x",
493 detail: "fn x()",
494 },
495 ]
496 "###
497 );
498 }
499
500 #[test]
501 fn dont_show_both_completions_for_shadowing() {
502 assert_debug_snapshot!(
503 do_reference_completion(
504 r"
505 fn foo() {
506 let bar = 92;
507 {
508 let bar = 62;
509 <|>
510 }
511 }
512 "
513 ),
514 @r###"
515 [
516 CompletionItem {
517 label: "bar",
518 source_range: [146; 146),
519 delete: [146; 146),
520 insert: "bar",
521 kind: Binding,
522 detail: "i32",
523 },
524 CompletionItem {
525 label: "foo()",
526 source_range: [146; 146),
527 delete: [146; 146),
528 insert: "foo()$0",
529 kind: Function,
530 lookup: "foo",
531 detail: "fn foo()",
532 },
533 ]
534 "###
535 );
536 }
537
538 #[test]
539 fn completes_self_in_methods() {
540 assert_debug_snapshot!(
541 do_reference_completion(r"impl S { fn foo(&self) { <|> } }"),
542 @r###"
543 [
544 CompletionItem {
545 label: "Self",
546 source_range: [25; 25),
547 delete: [25; 25),
548 insert: "Self",
549 kind: TypeParam,
550 },
551 CompletionItem {
552 label: "self",
553 source_range: [25; 25),
554 delete: [25; 25),
555 insert: "self",
556 kind: Binding,
557 detail: "&{unknown}",
558 },
559 ]
560 "###
561 );
562 }
563
564 #[test]
565 fn completes_prelude() {
566 assert_debug_snapshot!(
567 do_reference_completion(
568 "
569 //- /main.rs
570 fn foo() { let x: <|> }
571
572 //- /std/lib.rs
573 #[prelude_import]
574 use prelude::*;
575
576 mod prelude {
577 struct Option;
578 }
579 "
580 ),
581 @r###"
582 [
583 CompletionItem {
584 label: "Option",
585 source_range: [18; 18),
586 delete: [18; 18),
587 insert: "Option",
588 kind: Struct,
589 },
590 CompletionItem {
591 label: "foo()",
592 source_range: [18; 18),
593 delete: [18; 18),
594 insert: "foo()$0",
595 kind: Function,
596 lookup: "foo",
597 detail: "fn foo()",
598 },
599 CompletionItem {
600 label: "std",
601 source_range: [18; 18),
602 delete: [18; 18),
603 insert: "std",
604 kind: Module,
605 },
606 ]
607 "###
608 );
609 }
610
611 #[test]
612 fn completes_std_prelude_if_core_is_defined() {
613 assert_debug_snapshot!(
614 do_reference_completion(
615 "
616 //- /main.rs
617 fn foo() { let x: <|> }
618
619 //- /core/lib.rs
620 #[prelude_import]
621 use prelude::*;
622
623 mod prelude {
624 struct Option;
625 }
626
627 //- /std/lib.rs
628 #[prelude_import]
629 use prelude::*;
630
631 mod prelude {
632 struct String;
633 }
634 "
635 ),
636 @r###"
637 [
638 CompletionItem {
639 label: "String",
640 source_range: [18; 18),
641 delete: [18; 18),
642 insert: "String",
643 kind: Struct,
644 },
645 CompletionItem {
646 label: "core",
647 source_range: [18; 18),
648 delete: [18; 18),
649 insert: "core",
650 kind: Module,
651 },
652 CompletionItem {
653 label: "foo()",
654 source_range: [18; 18),
655 delete: [18; 18),
656 insert: "foo()$0",
657 kind: Function,
658 lookup: "foo",
659 detail: "fn foo()",
660 },
661 CompletionItem {
662 label: "std",
663 source_range: [18; 18),
664 delete: [18; 18),
665 insert: "std",
666 kind: Module,
667 },
668 ]
669 "###
670 );
671 }
672
673 #[test]
674 fn completes_macros_as_value() {
675 assert_debug_snapshot!(
676 do_reference_completion(
677 "
678 //- /main.rs
679 macro_rules! foo {
680 () => {}
681 }
682
683 #[macro_use]
684 mod m1 {
685 macro_rules! bar {
686 () => {}
687 }
688 }
689
690 mod m2 {
691 macro_rules! nope {
692 () => {}
693 }
694
695 #[macro_export]
696 macro_rules! baz {
697 () => {}
698 }
699 }
700
701 fn main() {
702 let v = <|>
703 }
704 "
705 ),
706 @r###"
707 [
708 CompletionItem {
709 label: "bar!",
710 source_range: [252; 252),
711 delete: [252; 252),
712 insert: "bar!($0)",
713 kind: Macro,
714 detail: "macro_rules! bar",
715 },
716 CompletionItem {
717 label: "baz!",
718 source_range: [252; 252),
719 delete: [252; 252),
720 insert: "baz!($0)",
721 kind: Macro,
722 detail: "#[macro_export]\nmacro_rules! baz",
723 },
724 CompletionItem {
725 label: "foo!",
726 source_range: [252; 252),
727 delete: [252; 252),
728 insert: "foo!($0)",
729 kind: Macro,
730 detail: "macro_rules! foo",
731 },
732 CompletionItem {
733 label: "m1",
734 source_range: [252; 252),
735 delete: [252; 252),
736 insert: "m1",
737 kind: Module,
738 },
739 CompletionItem {
740 label: "m2",
741 source_range: [252; 252),
742 delete: [252; 252),
743 insert: "m2",
744 kind: Module,
745 },
746 CompletionItem {
747 label: "main()",
748 source_range: [252; 252),
749 delete: [252; 252),
750 insert: "main()$0",
751 kind: Function,
752 lookup: "main",
753 detail: "fn main()",
754 },
755 ]
756 "###
757 );
758 }
759
760 #[test]
761 fn completes_both_macro_and_value() {
762 assert_debug_snapshot!(
763 do_reference_completion(
764 "
765 //- /main.rs
766 macro_rules! foo {
767 () => {}
768 }
769
770 fn foo() {
771 <|>
772 }
773 "
774 ),
775 @r###"
776 [
777 CompletionItem {
778 label: "foo!",
779 source_range: [49; 49),
780 delete: [49; 49),
781 insert: "foo!($0)",
782 kind: Macro,
783 detail: "macro_rules! foo",
784 },
785 CompletionItem {
786 label: "foo()",
787 source_range: [49; 49),
788 delete: [49; 49),
789 insert: "foo()$0",
790 kind: Function,
791 lookup: "foo",
792 detail: "fn foo()",
793 },
794 ]
795 "###
796 );
797 }
798
799 #[test]
800 fn completes_macros_as_type() {
801 assert_debug_snapshot!(
802 do_reference_completion(
803 "
804 //- /main.rs
805 macro_rules! foo {
806 () => {}
807 }
808
809 fn main() {
810 let x: <|>
811 }
812 "
813 ),
814 @r###"
815 [
816 CompletionItem {
817 label: "foo!",
818 source_range: [57; 57),
819 delete: [57; 57),
820 insert: "foo!($0)",
821 kind: Macro,
822 detail: "macro_rules! foo",
823 },
824 CompletionItem {
825 label: "main()",
826 source_range: [57; 57),
827 delete: [57; 57),
828 insert: "main()$0",
829 kind: Function,
830 lookup: "main",
831 detail: "fn main()",
832 },
833 ]
834 "###
835 );
836 }
837
838 #[test]
839 fn completes_macros_as_stmt() {
840 assert_debug_snapshot!(
841 do_reference_completion(
842 "
843 //- /main.rs
844 macro_rules! foo {
845 () => {}
846 }
847
848 fn main() {
849 <|>
850 }
851 "
852 ),
853 @r###"
854 [
855 CompletionItem {
856 label: "foo!",
857 source_range: [50; 50),
858 delete: [50; 50),
859 insert: "foo!($0)",
860 kind: Macro,
861 detail: "macro_rules! foo",
862 },
863 CompletionItem {
864 label: "main()",
865 source_range: [50; 50),
866 delete: [50; 50),
867 insert: "main()$0",
868 kind: Function,
869 lookup: "main",