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