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.rs171
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 @@
1use rustc_hash::FxHashSet;
2use ra_syntax::TextUnit;
3
4use crate::{
5 Cancelable,
6 completion::{CompletionItem, 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 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
42fn 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)]
59mod 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}