diff options
Diffstat (limited to 'crates/ra_analysis/src/completion/complete_scope.rs')
-rw-r--r-- | crates/ra_analysis/src/completion/complete_scope.rs | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs new file mode 100644 index 000000000..d07c0e46d --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_scope.rs | |||
@@ -0,0 +1,171 @@ | |||
1 | use rustc_hash::FxHashSet; | ||
2 | use ra_syntax::TextUnit; | ||
3 | |||
4 | use crate::{ | ||
5 | Cancelable, | ||
6 | completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext}, | ||
7 | }; | ||
8 | |||
9 | pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | ||
10 | if !ctx.is_trivial_path { | ||
11 | return Ok(()); | ||
12 | } | ||
13 | if let Some(fn_def) = ctx.enclosing_fn { | ||
14 | let scopes = hir::FnScopes::new(fn_def); | ||
15 | complete_fn(acc, &scopes, ctx.offset); | ||
16 | } | ||
17 | |||
18 | if let Some(module) = &ctx.module { | ||
19 | let module_scope = module.scope(ctx.db)?; | ||
20 | module_scope | ||
21 | .entries() | ||
22 | .filter(|(_name, res)| { | ||
23 | // Don't expose this item | ||
24 | match res.import { | ||
25 | None => true, | ||
26 | Some(import) => { | ||
27 | let range = import.range(ctx.db, module.source().file_id()); | ||
28 | !range.is_subrange(&ctx.leaf.range()) | ||
29 | } | ||
30 | } | ||
31 | }) | ||
32 | .for_each(|(name, _res)| { | ||
33 | CompletionItem::new(name.to_string()) | ||
34 | .kind(Reference) | ||
35 | .add_to(acc) | ||
36 | }); | ||
37 | } | ||
38 | |||
39 | Ok(()) | ||
40 | } | ||
41 | |||
42 | fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) { | ||
43 | let mut shadowed = FxHashSet::default(); | ||
44 | scopes | ||
45 | .scope_chain_for_offset(offset) | ||
46 | .flat_map(|scope| scopes.entries(scope).iter()) | ||
47 | .filter(|entry| shadowed.insert(entry.name())) | ||
48 | .for_each(|entry| { | ||
49 | CompletionItem::new(entry.name().to_string()) | ||
50 | .kind(Reference) | ||
51 | .add_to(acc) | ||
52 | }); | ||
53 | if scopes.self_param.is_some() { | ||
54 | CompletionItem::new("self").kind(Reference).add_to(acc); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | #[cfg(test)] | ||
59 | mod tests { | ||
60 | use crate::completion::{CompletionKind, check_completion}; | ||
61 | |||
62 | fn check_reference_completion(code: &str, expected_completions: &str) { | ||
63 | check_completion(code, expected_completions, CompletionKind::Reference); | ||
64 | } | ||
65 | |||
66 | #[test] | ||
67 | fn completes_bindings_from_let() { | ||
68 | check_reference_completion( | ||
69 | r" | ||
70 | fn quux(x: i32) { | ||
71 | let y = 92; | ||
72 | 1 + <|>; | ||
73 | let z = (); | ||
74 | } | ||
75 | ", | ||
76 | "y;x;quux", | ||
77 | ); | ||
78 | } | ||
79 | |||
80 | #[test] | ||
81 | fn completes_bindings_from_if_let() { | ||
82 | check_reference_completion( | ||
83 | r" | ||
84 | fn quux() { | ||
85 | if let Some(x) = foo() { | ||
86 | let y = 92; | ||
87 | }; | ||
88 | if let Some(a) = bar() { | ||
89 | let b = 62; | ||
90 | 1 + <|> | ||
91 | } | ||
92 | } | ||
93 | ", | ||
94 | "b;a;quux", | ||
95 | ); | ||
96 | } | ||
97 | |||
98 | #[test] | ||
99 | fn completes_bindings_from_for() { | ||
100 | check_reference_completion( | ||
101 | r" | ||
102 | fn quux() { | ||
103 | for x in &[1, 2, 3] { | ||
104 | <|> | ||
105 | } | ||
106 | } | ||
107 | ", | ||
108 | "x;quux", | ||
109 | ); | ||
110 | } | ||
111 | |||
112 | #[test] | ||
113 | fn completes_module_items() { | ||
114 | check_reference_completion( | ||
115 | r" | ||
116 | struct Foo; | ||
117 | enum Baz {} | ||
118 | fn quux() { | ||
119 | <|> | ||
120 | } | ||
121 | ", | ||
122 | "quux;Foo;Baz", | ||
123 | ); | ||
124 | } | ||
125 | |||
126 | #[test] | ||
127 | fn completes_module_items_in_nested_modules() { | ||
128 | check_reference_completion( | ||
129 | r" | ||
130 | struct Foo; | ||
131 | mod m { | ||
132 | struct Bar; | ||
133 | fn quux() { <|> } | ||
134 | } | ||
135 | ", | ||
136 | "quux;Bar", | ||
137 | ); | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn completes_return_type() { | ||
142 | check_reference_completion( | ||
143 | r" | ||
144 | struct Foo; | ||
145 | fn x() -> <|> | ||
146 | ", | ||
147 | "Foo;x", | ||
148 | ) | ||
149 | } | ||
150 | |||
151 | #[test] | ||
152 | fn dont_show_to_completions_for_shadowing() { | ||
153 | check_reference_completion( | ||
154 | r" | ||
155 | fn foo() -> { | ||
156 | let bar = 92; | ||
157 | { | ||
158 | let bar = 62; | ||
159 | <|> | ||
160 | } | ||
161 | } | ||
162 | ", | ||
163 | "bar;foo", | ||
164 | ) | ||
165 | } | ||
166 | |||
167 | #[test] | ||
168 | fn completes_self_in_methods() { | ||
169 | check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") | ||
170 | } | ||
171 | } | ||