aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/completion/reference_completion.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src/completion/reference_completion.rs')
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs294
1 files changed, 0 insertions, 294 deletions
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
deleted file mode 100644
index f483ed045..000000000
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ /dev/null
@@ -1,294 +0,0 @@
1use rustc_hash::{FxHashSet};
2use ra_editor::find_node_at_offset;
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 SourceFileNode, AstNode,
6 ast::{self, LoopBodyOwner},
7 SyntaxKind::*,
8};
9use hir::{
10 self,
11 FnScopes,
12 Def,
13 Path,
14};
15
16use crate::{
17 db::RootDatabase,
18 completion::CompletionItem,
19 Cancelable
20};
21
22pub(super) fn completions(
23 acc: &mut Vec<CompletionItem>,
24 db: &RootDatabase,
25 module: &hir::Module,
26 file: &SourceFileNode,
27 name_ref: ast::NameRef,
28) -> Cancelable<()> {
29 let kind = match classify_name_ref(name_ref) {
30 Some(it) => it,
31 None => return Ok(()),
32 };
33
34 match kind {
35 NameRefKind::LocalRef { enclosing_fn } => {
36 if let Some(fn_def) = enclosing_fn {
37 let scopes = FnScopes::new(fn_def);
38 complete_fn(name_ref, &scopes, acc);
39 complete_expr_keywords(&file, fn_def, name_ref, acc);
40 complete_expr_snippets(acc);
41 }
42
43 let module_scope = module.scope(db)?;
44 acc.extend(
45 module_scope
46 .entries()
47 .filter(|(_name, res)| {
48 // Don't expose this item
49 match res.import {
50 None => true,
51 Some(import) => {
52 let range = import.range(db, module.source().file_id());
53 !range.is_subrange(&name_ref.syntax().range())
54 }
55 }
56 })
57 .map(|(name, _res)| CompletionItem {
58 label: name.to_string(),
59 lookup: None,
60 snippet: None,
61 }),
62 );
63 }
64 NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
65 NameRefKind::BareIdentInMod => {
66 let name_range = name_ref.syntax().range();
67 let top_node = name_ref
68 .syntax()
69 .ancestors()
70 .take_while(|it| it.range() == name_range)
71 .last()
72 .unwrap();
73 match top_node.parent().map(|it| it.kind()) {
74 Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
75 _ => (),
76 }
77 }
78 }
79 Ok(())
80}
81
82enum NameRefKind<'a> {
83 /// NameRef is a part of single-segment path, for example, a refernece to a
84 /// local variable.
85 LocalRef {
86 enclosing_fn: Option<ast::FnDef<'a>>,
87 },
88 /// NameRef is the last segment in some path
89 Path(Path),
90 /// NameRef is bare identifier at the module's root.
91 /// Used for keyword completion
92 BareIdentInMod,
93}
94
95fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
96 let name_range = name_ref.syntax().range();
97 let top_node = name_ref
98 .syntax()
99 .ancestors()
100 .take_while(|it| it.range() == name_range)
101 .last()
102 .unwrap();
103 match top_node.parent().map(|it| it.kind()) {
104 Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
105 _ => (),
106 }
107
108 let parent = name_ref.syntax().parent()?;
109 if let Some(segment) = ast::PathSegment::cast(parent) {
110 let path = segment.parent_path();
111 if let Some(path) = Path::from_ast(path) {
112 if !path.is_ident() {
113 return Some(NameRefKind::Path(path));
114 }
115 }
116 if path.qualifier().is_none() {
117 let enclosing_fn = name_ref
118 .syntax()
119 .ancestors()
120 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
121 .find_map(ast::FnDef::cast);
122 return Some(NameRefKind::LocalRef { enclosing_fn });
123 }
124 }
125 None
126}
127
128fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
129 let mut shadowed = FxHashSet::default();
130 acc.extend(
131 scopes
132 .scope_chain(name_ref.syntax())
133 .flat_map(|scope| scopes.entries(scope).iter())
134 .filter(|entry| shadowed.insert(entry.name()))
135 .map(|entry| CompletionItem {
136 label: entry.name().to_string(),
137 lookup: None,
138 snippet: None,
139 }),
140 );
141 if scopes.self_param.is_some() {
142 acc.push(CompletionItem {
143 label: "self".to_string(),
144 lookup: None,
145 snippet: None,
146 })
147 }
148}
149
150fn complete_path(
151 acc: &mut Vec<CompletionItem>,
152 db: &RootDatabase,
153 module: &hir::Module,
154 mut path: Path,
155) -> Cancelable<()> {
156 if path.segments.is_empty() {
157 return Ok(());
158 }
159 path.segments.pop();
160 let def_id = match module.resolve_path(db, path)? {
161 None => return Ok(()),
162 Some(it) => it,
163 };
164 let target_module = match def_id.resolve(db)? {
165 Def::Module(it) => it,
166 _ => return Ok(()),
167 };
168 let module_scope = target_module.scope(db)?;
169 let completions = module_scope.entries().map(|(name, _res)| CompletionItem {
170 label: name.to_string(),
171 lookup: None,
172 snippet: None,
173 });
174 acc.extend(completions);
175 Ok(())
176}
177
178fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
179 acc.push(CompletionItem {
180 label: "Test function".to_string(),
181 lookup: Some("tfn".to_string()),
182 snippet: Some(
183 "#[test]\n\
184 fn ${1:feature}() {\n\
185 $0\n\
186 }"
187 .to_string(),
188 ),
189 });
190 acc.push(CompletionItem {
191 label: "pub(crate)".to_string(),
192 lookup: None,
193 snippet: Some("pub(crate) $0".to_string()),
194 })
195}
196
197fn complete_expr_keywords(
198 file: &SourceFileNode,
199 fn_def: ast::FnDef,
200 name_ref: ast::NameRef,
201 acc: &mut Vec<CompletionItem>,
202) {
203 acc.push(keyword("if", "if $0 {}"));
204 acc.push(keyword("match", "match $0 {}"));
205 acc.push(keyword("while", "while $0 {}"));
206 acc.push(keyword("loop", "loop {$0}"));
207
208 if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
209 if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
210 if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
211 acc.push(keyword("else", "else {$0}"));
212 acc.push(keyword("else if", "else if $0 {}"));
213 }
214 }
215 }
216 if is_in_loop_body(name_ref) {
217 acc.push(keyword("continue", "continue"));
218 acc.push(keyword("break", "break"));
219 }
220 acc.extend(complete_return(fn_def, name_ref));
221}
222
223fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
224 for node in name_ref.syntax().ancestors() {
225 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
226 break;
227 }
228 let loop_body = visitor()
229 .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
230 .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
231 .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
232 .accept(node);
233 if let Some(Some(body)) = loop_body {
234 if name_ref
235 .syntax()
236 .range()
237 .is_subrange(&body.syntax().range())
238 {
239 return true;
240 }
241 }
242 }
243 false
244}
245
246fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
247 // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
248 // .next()
249 // .and_then(|it| it.syntax().parent())
250 // .and_then(ast::Block::cast)
251 // .is_some();
252
253 // if is_last_in_block {
254 // return None;
255 // }
256
257 let is_stmt = match name_ref
258 .syntax()
259 .ancestors()
260 .filter_map(ast::ExprStmt::cast)
261 .next()
262 {
263 None => false,
264 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
265 };
266 let snip = match (is_stmt, fn_def.ret_type().is_some()) {
267 (true, true) => "return $0;",
268 (true, false) => "return;",
269 (false, true) => "return $0",
270 (false, false) => "return",
271 };
272 Some(keyword("return", snip))
273}
274
275fn keyword(kw: &str, snip: &str) -> CompletionItem {
276 CompletionItem {
277 label: kw.to_string(),
278 lookup: None,
279 snippet: Some(snip.to_string()),
280 }
281}
282
283fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
284 acc.push(CompletionItem {
285 label: "pd".to_string(),
286 lookup: None,
287 snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
288 });
289 acc.push(CompletionItem {
290 label: "ppd".to_string(),
291 lookup: None,
292 snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
293 });
294}