aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/completion/complete_scope.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/completion/complete_scope.rs')
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs173
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 @@
1use rustc_hash::FxHashSet;
2use ra_syntax::TextUnit;
3
4use crate::{
5 Cancelable,
6 completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
7};
8
9pub(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
44fn 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)]
61mod 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}