aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis')
-rw-r--r--crates/ra_analysis/src/completion/mod.rs (renamed from crates/ra_analysis/src/completion.rs)328
-rw-r--r--crates/ra_analysis/src/completion/reference_completion.rs316
-rw-r--r--crates/ra_analysis/src/db.rs8
-rw-r--r--crates/ra_analysis/src/descriptors/function/scope.rs6
-rw-r--r--crates/ra_analysis/src/descriptors/module/imp.rs16
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs43
-rw-r--r--crates/ra_analysis/src/descriptors/module/scope.rs6
-rw-r--r--crates/ra_analysis/src/imp.rs41
-rw-r--r--crates/ra_analysis/src/lib.rs23
-rw-r--r--crates/ra_analysis/src/symbol_index.rs8
-rw-r--r--crates/ra_analysis/src/syntax_ptr.rs11
-rw-r--r--crates/ra_analysis/tests/tests.rs41
12 files changed, 492 insertions, 355 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion/mod.rs
index 7c3476e5c..2e082705e 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion/mod.rs
@@ -1,20 +1,20 @@
1mod reference_completion;
2
1use ra_editor::find_node_at_offset; 3use ra_editor::find_node_at_offset;
2use ra_syntax::{ 4use ra_syntax::{
3 algo::visit::{visitor, visitor_ctx, Visitor, VisitorCtx}, 5 algo::find_leaf_at_offset,
4 ast::{self, AstChildren, LoopBodyOwner, ModuleItemOwner}, 6 algo::visit::{visitor_ctx, VisitorCtx},
5 AstNode, AtomEdit, File, 7 ast,
6 SyntaxKind::*, 8 AstNode, AtomEdit,
7 SyntaxNodeRef, TextUnit, 9 SyntaxNodeRef,
8}; 10};
9use rustc_hash::{FxHashMap, FxHashSet}; 11use rustc_hash::{FxHashMap};
10 12
11use crate::{ 13use crate::{
12 db::{self, SyntaxDatabase}, 14 db::{self, SyntaxDatabase},
13 descriptors::function::FnScopes, 15 descriptors::{DescriptorDatabase, module::ModuleSource},
14 descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, 16 input::{FilesDatabase},
15 descriptors::DescriptorDatabase, 17 Cancelable, FilePosition
16 input::FilesDatabase,
17 Cancelable, FilePosition,
18}; 18};
19 19
20#[derive(Debug)] 20#[derive(Debug)]
@@ -27,175 +27,65 @@ pub struct CompletionItem {
27 pub snippet: Option<String>, 27 pub snippet: Option<String>,
28} 28}
29 29
30pub(crate) fn resolve_based_completion( 30pub(crate) fn completions(
31 db: &db::RootDatabase, 31 db: &db::RootDatabase,
32 position: FilePosition, 32 position: FilePosition,
33) -> Cancelable<Option<Vec<CompletionItem>>> { 33) -> Cancelable<Option<Vec<CompletionItem>>> {
34 let source_root_id = db.file_source_root(position.file_id); 34 let original_file = db.file_syntax(position.file_id);
35 let file = db.file_syntax(position.file_id); 35 // Insert a fake ident to get a valid parse tree
36 let module_tree = db.module_tree(source_root_id)?;
37 let module_id = match module_tree.any_module_for_source(ModuleSource::File(position.file_id)) {
38 None => return Ok(None),
39 Some(it) => it,
40 };
41 let file = { 36 let file = {
42 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); 37 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
43 file.reparse(&edit) 38 original_file.reparse(&edit)
39 };
40
41 let leaf = match find_leaf_at_offset(original_file.syntax(), position.offset).left_biased() {
42 None => return Ok(None),
43 Some(it) => it,
44 }; 44 };
45 let target_module_id = match find_target_module(&module_tree, module_id, &file, position.offset) 45 let source_root_id = db.file_source_root(position.file_id);
46 { 46 let module_tree = db.module_tree(source_root_id)?;
47 let module_source = ModuleSource::for_node(position.file_id, leaf);
48 let module_id = match module_tree.any_module_for_source(module_source) {
47 None => return Ok(None), 49 None => return Ok(None),
48 Some(it) => it, 50 Some(it) => it,
49 }; 51 };
50 let module_scope = db.module_scope(source_root_id, target_module_id)?;
51 let res: Vec<_> = module_scope
52 .entries()
53 .iter()
54 .map(|entry| CompletionItem {
55 label: entry.name().to_string(),
56 lookup: None,
57 snippet: None,
58 })
59 .collect();
60 Ok(Some(res))
61}
62
63pub(crate) fn find_target_module(
64 module_tree: &ModuleTree,
65 module_id: ModuleId,
66 file: &File,
67 offset: TextUnit,
68) -> Option<ModuleId> {
69 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset)?;
70 let mut crate_path = crate_path(name_ref)?;
71
72 crate_path.pop();
73 let mut target_module = module_id.root(&module_tree);
74 for name in crate_path {
75 target_module = target_module.child(module_tree, name.text().as_str())?;
76 }
77 Some(target_module)
78}
79 52
80fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> {
81 let mut path = name_ref
82 .syntax()
83 .parent()
84 .and_then(ast::PathSegment::cast)?
85 .parent_path();
86 let mut res = Vec::new(); 53 let mut res = Vec::new();
87 loop {
88 let segment = path.segment()?;
89 match segment.kind()? {
90 ast::PathSegmentKind::Name(name) => res.push(name),
91 ast::PathSegmentKind::CrateKw => break,
92 ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
93 }
94 path = path.qualifier()?;
95 }
96 res.reverse();
97 Some(res)
98}
99
100pub(crate) fn scope_completion(
101 db: &db::RootDatabase,
102 position: FilePosition,
103) -> Option<Vec<CompletionItem>> {
104 let original_file = db.file_syntax(position.file_id);
105 // Insert a fake ident to get a valid parse tree
106 let file = {
107 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
108 original_file.reparse(&edit)
109 };
110 let mut has_completions = false; 54 let mut has_completions = false;
111 let mut res = Vec::new(); 55 // First, let's try to complete a reference to some declaration.
112 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { 56 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
113 has_completions = true; 57 has_completions = true;
114 complete_name_ref(&file, name_ref, &mut res); 58 reference_completion::completions(
59 &mut res,
60 db,
61 source_root_id,
62 &module_tree,
63 module_id,
64 &file,
65 name_ref,
66 )?;
115 // special case, `trait T { fn foo(i_am_a_name_ref) {} }` 67 // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
116 if is_node::<ast::Param>(name_ref.syntax()) { 68 if is_node::<ast::Param>(name_ref.syntax()) {
117 param_completions(name_ref.syntax(), &mut res); 69 param_completions(name_ref.syntax(), &mut res);
118 } 70 }
119 let name_range = name_ref.syntax().range();
120 let top_node = name_ref
121 .syntax()
122 .ancestors()
123 .take_while(|it| it.range() == name_range)
124 .last()
125 .unwrap();
126 match top_node.parent().map(|it| it.kind()) {
127 Some(ROOT) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res),
128 _ => (),
129 }
130 } 71 }
72
73 // Otherwise, if this is a declaration, use heuristics to suggest a name.
131 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { 74 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
132 if is_node::<ast::Param>(name.syntax()) { 75 if is_node::<ast::Param>(name.syntax()) {
133 has_completions = true; 76 has_completions = true;
134 param_completions(name.syntax(), &mut res); 77 param_completions(name.syntax(), &mut res);
135 } 78 }
136 } 79 }
137 if has_completions { 80 let res = if has_completions { Some(res) } else { None };
138 Some(res) 81 Ok(res)
139 } else {
140 None
141 }
142}
143
144fn complete_module_items(
145 file: &File,
146 items: AstChildren<ast::ModuleItem>,
147 this_item: Option<ast::NameRef>,
148 acc: &mut Vec<CompletionItem>,
149) {
150 let scope = ModuleScope::new(items); // FIXME
151 acc.extend(
152 scope
153 .entries()
154 .iter()
155 .filter(|entry| {
156 let syntax = entry.ptr().resolve(file);
157 Some(syntax.borrowed()) != this_item.map(|it| it.syntax())
158 })
159 .map(|entry| CompletionItem {
160 label: entry.name().to_string(),
161 lookup: None,
162 snippet: None,
163 }),
164 );
165}
166
167fn complete_name_ref(file: &File, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
168 if !is_node::<ast::Path>(name_ref.syntax()) {
169 return;
170 }
171 let mut visited_fn = false;
172 for node in name_ref.syntax().ancestors() {
173 if let Some(items) = visitor()
174 .visit::<ast::Root, _>(|it| Some(it.items()))
175 .visit::<ast::Module, _>(|it| Some(it.item_list()?.items()))
176 .accept(node)
177 {
178 if let Some(items) = items {
179 complete_module_items(file, items, Some(name_ref), acc);
180 }
181 break;
182 } else if !visited_fn {
183 if let Some(fn_def) = ast::FnDef::cast(node) {
184 visited_fn = true;
185 complete_expr_keywords(&file, fn_def, name_ref, acc);
186 complete_expr_snippets(acc);
187 let scopes = FnScopes::new(fn_def);
188 complete_fn(name_ref, &scopes, acc);
189 }
190 }
191 }
192} 82}
193 83
194fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) { 84fn param_completions(ctx: SyntaxNodeRef, acc: &mut Vec<CompletionItem>) {
195 let mut params = FxHashMap::default(); 85 let mut params = FxHashMap::default();
196 for node in ctx.ancestors() { 86 for node in ctx.ancestors() {
197 let _ = visitor_ctx(&mut params) 87 let _ = visitor_ctx(&mut params)
198 .visit::<ast::Root, _>(process) 88 .visit::<ast::SourceFile, _>(process)
199 .visit::<ast::ItemList, _>(process) 89 .visit::<ast::ItemList, _>(process)
200 .accept(node); 90 .accept(node);
201 } 91 }
@@ -238,140 +128,6 @@ fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
238 } 128 }
239} 129}
240 130
241fn complete_expr_keywords(
242 file: &File,
243 fn_def: ast::FnDef,
244 name_ref: ast::NameRef,
245 acc: &mut Vec<CompletionItem>,
246) {
247 acc.push(keyword("if", "if $0 {}"));
248 acc.push(keyword("match", "match $0 {}"));
249 acc.push(keyword("while", "while $0 {}"));
250 acc.push(keyword("loop", "loop {$0}"));
251
252 if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
253 if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
254 if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
255 acc.push(keyword("else", "else {$0}"));
256 acc.push(keyword("else if", "else if $0 {}"));
257 }
258 }
259 }
260 if is_in_loop_body(name_ref) {
261 acc.push(keyword("continue", "continue"));
262 acc.push(keyword("break", "break"));
263 }
264 acc.extend(complete_return(fn_def, name_ref));
265}
266
267fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
268 for node in name_ref.syntax().ancestors() {
269 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
270 break;
271 }
272 let loop_body = visitor()
273 .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
274 .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
275 .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
276 .accept(node);
277 if let Some(Some(body)) = loop_body {
278 if name_ref
279 .syntax()
280 .range()
281 .is_subrange(&body.syntax().range())
282 {
283 return true;
284 }
285 }
286 }
287 false
288}
289
290fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
291 // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
292 // .next()
293 // .and_then(|it| it.syntax().parent())
294 // .and_then(ast::Block::cast)
295 // .is_some();
296
297 // if is_last_in_block {
298 // return None;
299 // }
300
301 let is_stmt = match name_ref
302 .syntax()
303 .ancestors()
304 .filter_map(ast::ExprStmt::cast)
305 .next()
306 {
307 None => false,
308 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
309 };
310 let snip = match (is_stmt, fn_def.ret_type().is_some()) {
311 (true, true) => "return $0;",
312 (true, false) => "return;",
313 (false, true) => "return $0",
314 (false, false) => "return",
315 };
316 Some(keyword("return", snip))
317}
318
319fn keyword(kw: &str, snip: &str) -> CompletionItem {
320 CompletionItem {
321 label: kw.to_string(),
322 lookup: None,
323 snippet: Some(snip.to_string()),
324 }
325}
326
327fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
328 acc.push(CompletionItem {
329 label: "pd".to_string(),
330 lookup: None,
331 snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
332 });
333 acc.push(CompletionItem {
334 label: "ppd".to_string(),
335 lookup: None,
336 snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
337 });
338}
339
340fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
341 acc.push(CompletionItem {
342 label: "tfn".to_string(),
343 lookup: None,
344 snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()),
345 });
346 acc.push(CompletionItem {
347 label: "pub(crate)".to_string(),
348 lookup: None,
349 snippet: Some("pub(crate) $0".to_string()),
350 })
351}
352
353fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
354 let mut shadowed = FxHashSet::default();
355 acc.extend(
356 scopes
357 .scope_chain(name_ref.syntax())
358 .flat_map(|scope| scopes.entries(scope).iter())
359 .filter(|entry| shadowed.insert(entry.name()))
360 .map(|entry| CompletionItem {
361 label: entry.name().to_string(),
362 lookup: None,
363 snippet: None,
364 }),
365 );
366 if scopes.self_param.is_some() {
367 acc.push(CompletionItem {
368 label: "self".to_string(),
369 lookup: None,
370 snippet: None,
371 })
372 }
373}
374
375#[cfg(test)] 131#[cfg(test)]
376mod tests { 132mod tests {
377 use test_utils::assert_eq_dbg; 133 use test_utils::assert_eq_dbg;
@@ -382,7 +138,8 @@ mod tests {
382 138
383 fn check_scope_completion(code: &str, expected_completions: &str) { 139 fn check_scope_completion(code: &str, expected_completions: &str) {
384 let (analysis, position) = single_file_with_position(code); 140 let (analysis, position) = single_file_with_position(code);
385 let completions = scope_completion(&analysis.imp.db, position) 141 let completions = completions(&analysis.imp.db, position)
142 .unwrap()
386 .unwrap() 143 .unwrap()
387 .into_iter() 144 .into_iter()
388 .filter(|c| c.snippet.is_none()) 145 .filter(|c| c.snippet.is_none())
@@ -392,7 +149,8 @@ mod tests {
392 149
393 fn check_snippet_completion(code: &str, expected_completions: &str) { 150 fn check_snippet_completion(code: &str, expected_completions: &str) {
394 let (analysis, position) = single_file_with_position(code); 151 let (analysis, position) = single_file_with_position(code);
395 let completions = scope_completion(&analysis.imp.db, position) 152 let completions = completions(&analysis.imp.db, position)
153 .unwrap()
396 .unwrap() 154 .unwrap()
397 .into_iter() 155 .into_iter()
398 .filter(|c| c.snippet.is_some()) 156 .filter(|c| c.snippet.is_some())
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
new file mode 100644
index 000000000..6c5fd0be6
--- /dev/null
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -0,0 +1,316 @@
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};
9
10use crate::{
11 db::RootDatabase,
12 input::{SourceRootId},
13 completion::CompletionItem,
14 descriptors::module::{ModuleId, ModuleTree},
15 descriptors::function::FnScopes,
16 descriptors::DescriptorDatabase,
17 Cancelable
18};
19
20pub(super) fn completions(
21 acc: &mut Vec<CompletionItem>,
22 db: &RootDatabase,
23 source_root_id: SourceRootId,
24 module_tree: &ModuleTree,
25 module_id: ModuleId,
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 = db.module_scope(source_root_id, module_id)?;
44 acc.extend(
45 module_scope
46 .entries()
47 .iter()
48 .filter(|entry| {
49 // Don't expose this item
50 !entry.ptr().range().is_subrange(&name_ref.syntax().range())
51 })
52 .map(|entry| CompletionItem {
53 label: entry.name().to_string(),
54 lookup: None,
55 snippet: None,
56 }),
57 );
58 }
59 NameRefKind::CratePath(path) => {
60 complete_path(acc, db, source_root_id, module_tree, module_id, path)?
61 }
62 NameRefKind::BareIdentInMod => {
63 let name_range = name_ref.syntax().range();
64 let top_node = name_ref
65 .syntax()
66 .ancestors()
67 .take_while(|it| it.range() == name_range)
68 .last()
69 .unwrap();
70 match top_node.parent().map(|it| it.kind()) {
71 Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc),
72 _ => (),
73 }
74 }
75 }
76 Ok(())
77}
78
79enum NameRefKind<'a> {
80 /// NameRef is a part of single-segment path, for example, a refernece to a
81 /// local variable.
82 LocalRef {
83 enclosing_fn: Option<ast::FnDef<'a>>,
84 },
85 /// NameRef is the last segment in crate:: path
86 CratePath(Vec<ast::NameRef<'a>>),
87 /// NameRef is bare identifier at the module's root.
88 /// Used for keyword completion
89 BareIdentInMod,
90}
91
92fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
93 let name_range = name_ref.syntax().range();
94 let top_node = name_ref
95 .syntax()
96 .ancestors()
97 .take_while(|it| it.range() == name_range)
98 .last()
99 .unwrap();
100 match top_node.parent().map(|it| it.kind()) {
101 Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod),
102 _ => (),
103 }
104
105 let parent = name_ref.syntax().parent()?;
106 if let Some(segment) = ast::PathSegment::cast(parent) {
107 let path = segment.parent_path();
108 if let Some(crate_path) = crate_path(path) {
109 return Some(NameRefKind::CratePath(crate_path));
110 }
111 if path.qualifier().is_none() {
112 let enclosing_fn = name_ref
113 .syntax()
114 .ancestors()
115 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
116 .find_map(ast::FnDef::cast);
117 return Some(NameRefKind::LocalRef { enclosing_fn });
118 }
119 }
120 None
121}
122
123fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
124 let mut res = Vec::new();
125 loop {
126 let segment = path.segment()?;
127 match segment.kind()? {
128 ast::PathSegmentKind::Name(name) => res.push(name),
129 ast::PathSegmentKind::CrateKw => break,
130 ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
131 }
132 path = qualifier(path)?;
133 }
134 res.reverse();
135 return Some(res);
136
137 fn qualifier(path: ast::Path) -> Option<ast::Path> {
138 if let Some(q) = path.qualifier() {
139 return Some(q);
140 }
141 // TODO: this bottom up traversal is not too precise.
142 // Should we handle do a top-down analysiss, recording results?
143 let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
144 let use_tree = use_tree_list.parent_use_tree();
145 use_tree.path()
146 }
147}
148
149fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
150 let mut shadowed = FxHashSet::default();
151 acc.extend(
152 scopes
153 .scope_chain(name_ref.syntax())
154 .flat_map(|scope| scopes.entries(scope).iter())
155 .filter(|entry| shadowed.insert(entry.name()))
156 .map(|entry| CompletionItem {
157 label: entry.name().to_string(),
158 lookup: None,
159 snippet: None,
160 }),
161 );
162 if scopes.self_param.is_some() {
163 acc.push(CompletionItem {
164 label: "self".to_string(),
165 lookup: None,
166 snippet: None,
167 })
168 }
169}
170
171fn complete_path(
172 acc: &mut Vec<CompletionItem>,
173 db: &RootDatabase,
174 source_root_id: SourceRootId,
175 module_tree: &ModuleTree,
176 module_id: ModuleId,
177 crate_path: Vec<ast::NameRef>,
178) -> Cancelable<()> {
179 let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
180 None => return Ok(()),
181 Some(it) => it,
182 };
183 let module_scope = db.module_scope(source_root_id, target_module_id)?;
184 let completions = module_scope.entries().iter().map(|entry| CompletionItem {
185 label: entry.name().to_string(),
186 lookup: None,
187 snippet: None,
188 });
189 acc.extend(completions);
190 Ok(())
191}
192
193fn find_target_module(
194 module_tree: &ModuleTree,
195 module_id: ModuleId,
196 mut crate_path: Vec<ast::NameRef>,
197) -> Option<ModuleId> {
198 crate_path.pop();
199 let mut target_module = module_id.root(&module_tree);
200 for name in crate_path {
201 target_module = target_module.child(module_tree, name.text().as_str())?;
202 }
203 Some(target_module)
204}
205
206fn complete_mod_item_snippets(acc: &mut Vec<CompletionItem>) {
207 acc.push(CompletionItem {
208 label: "tfn".to_string(),
209 lookup: None,
210 snippet: Some("#[test]\nfn $1() {\n $0\n}".to_string()),
211 });
212 acc.push(CompletionItem {
213 label: "pub(crate)".to_string(),
214 lookup: None,
215 snippet: Some("pub(crate) $0".to_string()),
216 })
217}
218
219fn complete_expr_keywords(
220 file: &SourceFileNode,
221 fn_def: ast::FnDef,
222 name_ref: ast::NameRef,
223 acc: &mut Vec<CompletionItem>,
224) {
225 acc.push(keyword("if", "if $0 {}"));
226 acc.push(keyword("match", "match $0 {}"));
227 acc.push(keyword("while", "while $0 {}"));
228 acc.push(keyword("loop", "loop {$0}"));
229
230 if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) {
231 if let Some(if_expr) = find_node_at_offset::<ast::IfExpr>(file.syntax(), off) {
232 if if_expr.syntax().range().end() < name_ref.syntax().range().start() {
233 acc.push(keyword("else", "else {$0}"));
234 acc.push(keyword("else if", "else if $0 {}"));
235 }
236 }
237 }
238 if is_in_loop_body(name_ref) {
239 acc.push(keyword("continue", "continue"));
240 acc.push(keyword("break", "break"));
241 }
242 acc.extend(complete_return(fn_def, name_ref));
243}
244
245fn is_in_loop_body(name_ref: ast::NameRef) -> bool {
246 for node in name_ref.syntax().ancestors() {
247 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
248 break;
249 }
250 let loop_body = visitor()
251 .visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
252 .visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
253 .visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
254 .accept(node);
255 if let Some(Some(body)) = loop_body {
256 if name_ref
257 .syntax()
258 .range()
259 .is_subrange(&body.syntax().range())
260 {
261 return true;
262 }
263 }
264 }
265 false
266}
267
268fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<CompletionItem> {
269 // let is_last_in_block = name_ref.syntax().ancestors().filter_map(ast::Expr::cast)
270 // .next()
271 // .and_then(|it| it.syntax().parent())
272 // .and_then(ast::Block::cast)
273 // .is_some();
274
275 // if is_last_in_block {
276 // return None;
277 // }
278
279 let is_stmt = match name_ref
280 .syntax()
281 .ancestors()
282 .filter_map(ast::ExprStmt::cast)
283 .next()
284 {
285 None => false,
286 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
287 };
288 let snip = match (is_stmt, fn_def.ret_type().is_some()) {
289 (true, true) => "return $0;",
290 (true, false) => "return;",
291 (false, true) => "return $0",
292 (false, false) => "return",
293 };
294 Some(keyword("return", snip))
295}
296
297fn keyword(kw: &str, snip: &str) -> CompletionItem {
298 CompletionItem {
299 label: kw.to_string(),
300 lookup: None,
301 snippet: Some(snip.to_string()),
302 }
303}
304
305fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
306 acc.push(CompletionItem {
307 label: "pd".to_string(),
308 lookup: None,
309 snippet: Some("eprintln!(\"$0 = {:?}\", $0);".to_string()),
310 });
311 acc.push(CompletionItem {
312 label: "ppd".to_string(),
313 lookup: None,
314 snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
315 });
316}
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 627512553..194f1a6b0 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -1,7 +1,7 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_editor::LineIndex; 3use ra_editor::LineIndex;
4use ra_syntax::{File, SyntaxNode}; 4use ra_syntax::{SourceFileNode, SyntaxNode};
5use salsa::{self, Database}; 5use salsa::{self, Database};
6 6
7use crate::{ 7use crate::{
@@ -85,7 +85,7 @@ salsa::database_storage! {
85 85
86salsa::query_group! { 86salsa::query_group! {
87 pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase { 87 pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase {
88 fn file_syntax(file_id: FileId) -> File { 88 fn file_syntax(file_id: FileId) -> SourceFileNode {
89 type FileSyntaxQuery; 89 type FileSyntaxQuery;
90 } 90 }
91 fn file_lines(file_id: FileId) -> Arc<LineIndex> { 91 fn file_lines(file_id: FileId) -> Arc<LineIndex> {
@@ -103,9 +103,9 @@ salsa::query_group! {
103 } 103 }
104} 104}
105 105
106fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> File { 106fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode {
107 let text = db.file_text(file_id); 107 let text = db.file_text(file_id);
108 File::parse(&*text) 108 SourceFileNode::parse(&*text)
109} 109}
110fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> { 110fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> {
111 let text = db.file_text(file_id); 111 let text = db.file_text(file_id);
diff --git a/crates/ra_analysis/src/descriptors/function/scope.rs b/crates/ra_analysis/src/descriptors/function/scope.rs
index 62b46ffba..bbe16947c 100644
--- a/crates/ra_analysis/src/descriptors/function/scope.rs
+++ b/crates/ra_analysis/src/descriptors/function/scope.rs
@@ -272,7 +272,7 @@ pub fn resolve_local_name<'a>(
272#[cfg(test)] 272#[cfg(test)]
273mod tests { 273mod tests {
274 use ra_editor::find_node_at_offset; 274 use ra_editor::find_node_at_offset;
275 use ra_syntax::File; 275 use ra_syntax::SourceFileNode;
276 use test_utils::extract_offset; 276 use test_utils::extract_offset;
277 277
278 use super::*; 278 use super::*;
@@ -287,7 +287,7 @@ mod tests {
287 buf.push_str(&code[off..]); 287 buf.push_str(&code[off..]);
288 buf 288 buf
289 }; 289 };
290 let file = File::parse(&code); 290 let file = SourceFileNode::parse(&code);
291 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); 291 let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
292 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 292 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
293 let scopes = FnScopes::new(fn_def); 293 let scopes = FnScopes::new(fn_def);
@@ -376,7 +376,7 @@ mod tests {
376 376
377 fn do_check_local_name(code: &str, expected_offset: u32) { 377 fn do_check_local_name(code: &str, expected_offset: u32) {
378 let (off, code) = extract_offset(code); 378 let (off, code) = extract_offset(code);
379 let file = File::parse(&code); 379 let file = SourceFileNode::parse(&code);
380 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); 380 let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
381 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap(); 381 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
382 382
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_analysis/src/descriptors/module/imp.rs
index b5c232ea4..ade96ddc0 100644
--- a/crates/ra_analysis/src/descriptors/module/imp.rs
+++ b/crates/ra_analysis/src/descriptors/module/imp.rs
@@ -41,8 +41,8 @@ pub(crate) fn submodules(
41 db::check_canceled(db)?; 41 db::check_canceled(db)?;
42 let file_id = source.file_id(); 42 let file_id = source.file_id();
43 let submodules = match source.resolve(db) { 43 let submodules = match source.resolve(db) {
44 ModuleSourceNode::Root(it) => collect_submodules(file_id, it.borrowed()), 44 ModuleSourceNode::SourceFile(it) => collect_submodules(file_id, it.borrowed()),
45 ModuleSourceNode::Inline(it) => it 45 ModuleSourceNode::Module(it) => it
46 .borrowed() 46 .borrowed()
47 .item_list() 47 .item_list()
48 .map(|it| collect_submodules(file_id, it)) 48 .map(|it| collect_submodules(file_id, it))
@@ -89,8 +89,8 @@ pub(crate) fn module_scope(
89 let tree = db.module_tree(source_root_id)?; 89 let tree = db.module_tree(source_root_id)?;
90 let source = module_id.source(&tree).resolve(db); 90 let source = module_id.source(&tree).resolve(db);
91 let res = match source { 91 let res = match source {
92 ModuleSourceNode::Root(root) => ModuleScope::new(root.borrowed().items()), 92 ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
93 ModuleSourceNode::Inline(inline) => match inline.borrowed().item_list() { 93 ModuleSourceNode::Module(it) => match it.borrowed().item_list() {
94 Some(items) => ModuleScope::new(items.items()), 94 Some(items) => ModuleScope::new(items.items()),
95 None => ModuleScope::new(std::iter::empty()), 95 None => ModuleScope::new(std::iter::empty()),
96 }, 96 },
@@ -121,7 +121,7 @@ fn create_module_tree<'a>(
121 121
122 let source_root = db.source_root(source_root); 122 let source_root = db.source_root(source_root);
123 for &file_id in source_root.files.iter() { 123 for &file_id in source_root.files.iter() {
124 let source = ModuleSource::File(file_id); 124 let source = ModuleSource::SourceFile(file_id);
125 if visited.contains(&source) { 125 if visited.contains(&source) {
126 continue; // TODO: use explicit crate_roots here 126 continue; // TODO: use explicit crate_roots here
127 } 127 }
@@ -181,7 +181,7 @@ fn build_subtree(
181 visited, 181 visited,
182 roots, 182 roots,
183 Some(link), 183 Some(link),
184 ModuleSource::File(file_id), 184 ModuleSource::SourceFile(file_id),
185 ), 185 ),
186 }) 186 })
187 .collect::<Cancelable<Vec<_>>>()?; 187 .collect::<Cancelable<Vec<_>>>()?;
@@ -213,8 +213,8 @@ fn resolve_submodule(
213 file_resolver: &FileResolverImp, 213 file_resolver: &FileResolverImp,
214) -> (Vec<FileId>, Option<Problem>) { 214) -> (Vec<FileId>, Option<Problem>) {
215 let file_id = match source { 215 let file_id = match source {
216 ModuleSource::File(it) => it, 216 ModuleSource::SourceFile(it) => it,
217 ModuleSource::Inline(..) => { 217 ModuleSource::Module(..) => {
218 // TODO 218 // TODO
219 return (Vec::new(), None); 219 return (Vec::new(), None);
220 } 220 }
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs
index 03330240d..bc1148b22 100644
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ b/crates/ra_analysis/src/descriptors/module/mod.rs
@@ -3,7 +3,7 @@ pub(crate) mod scope;
3 3
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, AstNode, NameOwner}, 5 ast::{self, AstNode, NameOwner},
6 SmolStr, SyntaxNode, 6 SmolStr, SyntaxNode, SyntaxNodeRef,
7}; 7};
8use relative_path::RelativePathBuf; 8use relative_path::RelativePathBuf;
9 9
@@ -41,19 +41,18 @@ impl ModuleTree {
41 41
42/// `ModuleSource` is the syntax tree element that produced this module: 42/// `ModuleSource` is the syntax tree element that produced this module:
43/// either a file, or an inlinde module. 43/// either a file, or an inlinde module.
44/// TODO: we don't produce Inline modules yet
45#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 44#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46pub(crate) enum ModuleSource { 45pub(crate) enum ModuleSource {
47 File(FileId), 46 SourceFile(FileId),
48 #[allow(dead_code)] 47 #[allow(dead_code)]
49 Inline(SyntaxPtr), 48 Module(SyntaxPtr),
50} 49}
51 50
52/// An owned syntax node for a module. Unlike `ModuleSource`, 51/// An owned syntax node for a module. Unlike `ModuleSource`,
53/// this holds onto the AST for the whole file. 52/// this holds onto the AST for the whole file.
54enum ModuleSourceNode { 53enum ModuleSourceNode {
55 Root(ast::RootNode), 54 SourceFile(ast::SourceFileNode),
56 Inline(ast::ModuleNode), 55 Module(ast::ModuleNode),
57} 56}
58 57
59#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] 58#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
@@ -135,14 +134,14 @@ impl LinkId {
135 ) -> ast::ModuleNode { 134 ) -> ast::ModuleNode {
136 let owner = self.owner(tree); 135 let owner = self.owner(tree);
137 match owner.source(tree).resolve(db) { 136 match owner.source(tree).resolve(db) {
138 ModuleSourceNode::Root(root) => { 137 ModuleSourceNode::SourceFile(root) => {
139 let ast = imp::modules(root.borrowed()) 138 let ast = imp::modules(root.borrowed())
140 .find(|(name, _)| name == &tree.link(self).name) 139 .find(|(name, _)| name == &tree.link(self).name)
141 .unwrap() 140 .unwrap()
142 .1; 141 .1;
143 ast.owned() 142 ast.owned()
144 } 143 }
145 ModuleSourceNode::Inline(it) => it, 144 ModuleSourceNode::Module(it) => it,
146 } 145 }
147 } 146 }
148} 147}
@@ -155,37 +154,47 @@ struct ModuleData {
155} 154}
156 155
157impl ModuleSource { 156impl ModuleSource {
157 pub(crate) fn for_node(file_id: FileId, node: SyntaxNodeRef) -> ModuleSource {
158 for node in node.ancestors() {
159 if let Some(m) = ast::Module::cast(node) {
160 if !m.has_semi() {
161 return ModuleSource::new_inline(file_id, m);
162 }
163 }
164 }
165 ModuleSource::SourceFile(file_id)
166 }
158 pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource { 167 pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
159 assert!(!module.has_semi()); 168 assert!(!module.has_semi());
160 let ptr = SyntaxPtr::new(file_id, module.syntax()); 169 let ptr = SyntaxPtr::new(file_id, module.syntax());
161 ModuleSource::Inline(ptr) 170 ModuleSource::Module(ptr)
162 } 171 }
163 172
164 pub(crate) fn as_file(self) -> Option<FileId> { 173 pub(crate) fn as_file(self) -> Option<FileId> {
165 match self { 174 match self {
166 ModuleSource::File(f) => Some(f), 175 ModuleSource::SourceFile(f) => Some(f),
167 ModuleSource::Inline(..) => None, 176 ModuleSource::Module(..) => None,
168 } 177 }
169 } 178 }
170 179
171 pub(crate) fn file_id(self) -> FileId { 180 pub(crate) fn file_id(self) -> FileId {
172 match self { 181 match self {
173 ModuleSource::File(f) => f, 182 ModuleSource::SourceFile(f) => f,
174 ModuleSource::Inline(ptr) => ptr.file_id(), 183 ModuleSource::Module(ptr) => ptr.file_id(),
175 } 184 }
176 } 185 }
177 186
178 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode { 187 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
179 match self { 188 match self {
180 ModuleSource::File(file_id) => { 189 ModuleSource::SourceFile(file_id) => {
181 let syntax = db.file_syntax(file_id); 190 let syntax = db.file_syntax(file_id);
182 ModuleSourceNode::Root(syntax.ast().owned()) 191 ModuleSourceNode::SourceFile(syntax.ast().owned())
183 } 192 }
184 ModuleSource::Inline(ptr) => { 193 ModuleSource::Module(ptr) => {
185 let syntax = db.resolve_syntax_ptr(ptr); 194 let syntax = db.resolve_syntax_ptr(ptr);
186 let syntax = syntax.borrowed(); 195 let syntax = syntax.borrowed();
187 let module = ast::Module::cast(syntax).unwrap(); 196 let module = ast::Module::cast(syntax).unwrap();
188 ModuleSourceNode::Inline(module.owned()) 197 ModuleSourceNode::Module(module.owned())
189 } 198 }
190 } 199 }
191 } 200 }
diff --git a/crates/ra_analysis/src/descriptors/module/scope.rs b/crates/ra_analysis/src/descriptors/module/scope.rs
index 215b31f8e..4490228e4 100644
--- a/crates/ra_analysis/src/descriptors/module/scope.rs
+++ b/crates/ra_analysis/src/descriptors/module/scope.rs
@@ -25,7 +25,7 @@ enum EntryKind {
25} 25}
26 26
27impl ModuleScope { 27impl ModuleScope {
28 pub(crate) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope { 28 pub(super) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope {
29 let mut entries = Vec::new(); 29 let mut entries = Vec::new();
30 for item in items { 30 for item in items {
31 let entry = match item { 31 let entry = match item {
@@ -95,10 +95,10 @@ fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) {
95#[cfg(test)] 95#[cfg(test)]
96mod tests { 96mod tests {
97 use super::*; 97 use super::*;
98 use ra_syntax::{ast::ModuleItemOwner, File}; 98 use ra_syntax::{ast::ModuleItemOwner, SourceFileNode};
99 99
100 fn do_check(code: &str, expected: &[&str]) { 100 fn do_check(code: &str, expected: &[&str]) {
101 let file = File::parse(&code); 101 let file = SourceFileNode::parse(&code);
102 let scope = ModuleScope::new(file.ast().items()); 102 let scope = ModuleScope::new(file.ast().items());
103 let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>(); 103 let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>();
104 assert_eq!(expected, actual.as_slice()); 104 assert_eq!(expected, actual.as_slice());
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 819827b95..74c248a96 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -7,7 +7,7 @@ use std::{
7use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit}; 7use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit};
8use ra_syntax::{ 8use ra_syntax::{
9 ast::{self, ArgListOwner, Expr, NameOwner}, 9 ast::{self, ArgListOwner, Expr, NameOwner},
10 AstNode, File, SmolStr, 10 AstNode, SourceFileNode, SmolStr,
11 SyntaxKind::*, 11 SyntaxKind::*,
12 SyntaxNodeRef, TextRange, TextUnit, 12 SyntaxNodeRef, TextRange, TextUnit,
13}; 13};
@@ -17,7 +17,7 @@ use rustc_hash::FxHashSet;
17use salsa::{Database, ParallelDatabase}; 17use salsa::{Database, ParallelDatabase};
18 18
19use crate::{ 19use crate::{
20 completion::{resolve_based_completion, scope_completion, CompletionItem}, 20 completion::{completions, CompletionItem},
21 db::{self, FileSyntaxQuery, SyntaxDatabase}, 21 db::{self, FileSyntaxQuery, SyntaxDatabase},
22 descriptors::{ 22 descriptors::{
23 function::{FnDescriptor, FnId}, 23 function::{FnDescriptor, FnId},
@@ -27,7 +27,7 @@ use crate::{
27 input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE}, 27 input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
28 symbol_index::SymbolIndex, 28 symbol_index::SymbolIndex,
29 AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver, 29 AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver,
30 FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit, 30 FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit,
31}; 31};
32 32
33#[derive(Clone, Debug)] 33#[derive(Clone, Debug)]
@@ -180,7 +180,7 @@ impl fmt::Debug for AnalysisImpl {
180} 180}
181 181
182impl AnalysisImpl { 182impl AnalysisImpl {
183 pub fn file_syntax(&self, file_id: FileId) -> File { 183 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
184 self.db.file_syntax(file_id) 184 self.db.file_syntax(file_id)
185 } 185 }
186 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 186 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
@@ -226,7 +226,7 @@ impl AnalysisImpl {
226 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) 226 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
227 { 227 {
228 Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m), 228 Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
229 _ => ModuleSource::File(position.file_id), 229 _ => ModuleSource::SourceFile(position.file_id),
230 }; 230 };
231 231
232 let res = module_tree 232 let res = module_tree
@@ -254,7 +254,7 @@ impl AnalysisImpl {
254 let module_tree = self.module_tree(file_id)?; 254 let module_tree = self.module_tree(file_id)?;
255 let crate_graph = self.db.crate_graph(); 255 let crate_graph = self.db.crate_graph();
256 let res = module_tree 256 let res = module_tree
257 .modules_for_source(ModuleSource::File(file_id)) 257 .modules_for_source(ModuleSource::SourceFile(file_id))
258 .into_iter() 258 .into_iter()
259 .map(|it| it.root(&module_tree)) 259 .map(|it| it.root(&module_tree))
260 .filter_map(|it| it.source(&module_tree).as_file()) 260 .filter_map(|it| it.source(&module_tree).as_file())
@@ -267,18 +267,7 @@ impl AnalysisImpl {
267 self.db.crate_graph().crate_roots[&crate_id] 267 self.db.crate_graph().crate_roots[&crate_id]
268 } 268 }
269 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 269 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
270 let mut res = Vec::new(); 270 completions(&self.db, position)
271 let mut has_completions = false;
272 if let Some(scope_based) = scope_completion(&self.db, position) {
273 res.extend(scope_based);
274 has_completions = true;
275 }
276 if let Some(scope_based) = resolve_based_completion(&self.db, position)? {
277 res.extend(scope_based);
278 has_completions = true;
279 }
280 let res = if has_completions { Some(res) } else { None };
281 Ok(res)
282 } 271 }
283 pub fn approximately_resolve_symbol( 272 pub fn approximately_resolve_symbol(
284 &self, 273 &self,
@@ -364,6 +353,16 @@ impl AnalysisImpl {
364 ret 353 ret
365 } 354 }
366 355
356 pub fn doc_comment_for(
357 &self,
358 file_id: FileId,
359 symbol: FileSymbol,
360 ) -> Cancelable<Option<String>> {
361 let file = self.db.file_syntax(file_id);
362
363 Ok(symbol.docs(&file))
364 }
365
367 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 366 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
368 let module_tree = self.module_tree(file_id)?; 367 let module_tree = self.module_tree(file_id)?;
369 let syntax = self.db.file_syntax(file_id); 368 let syntax = self.db.file_syntax(file_id);
@@ -376,7 +375,7 @@ impl AnalysisImpl {
376 fix: None, 375 fix: None,
377 }) 376 })
378 .collect::<Vec<_>>(); 377 .collect::<Vec<_>>();
379 if let Some(m) = module_tree.any_module_for_source(ModuleSource::File(file_id)) { 378 if let Some(m) = module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
380 for (name_node, problem) in m.problems(&module_tree, &*self.db) { 379 for (name_node, problem) in m.problems(&module_tree, &*self.db) {
381 let diag = match problem { 380 let diag = match problem {
382 Problem::UnresolvedModule { candidate } => { 381 Problem::UnresolvedModule { candidate } => {
@@ -538,7 +537,7 @@ impl AnalysisImpl {
538 Some(name) => name.text(), 537 Some(name) => name.text(),
539 None => return Vec::new(), 538 None => return Vec::new(),
540 }; 539 };
541 let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) { 540 let module_id = match module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
542 Some(id) => id, 541 Some(id) => id,
543 None => return Vec::new(), 542 None => return Vec::new(),
544 }; 543 };
@@ -552,7 +551,7 @@ impl AnalysisImpl {
552 551
553impl SourceChange { 552impl SourceChange {
554 pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange { 553 pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange {
555 let file_edit = SourceFileEdit { 554 let file_edit = SourceFileNodeEdit {
556 file_id, 555 file_id,
557 edits: edit.edit.into_atoms(), 556 edits: edit.edit.into_atoms(),
558 }; 557 };
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 0ea9ebee7..ab0e3cb0c 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -1,5 +1,5 @@
1//! ra_analyzer crate is the brain of Rust analyzer. It relies on the `salsa` 1//! ra_analyzer crate is the brain of Rust analyzer. It relies on the `salsa`
2//! crate, which provides and incremental on-deman database of facts. 2//! crate, which provides and incremental on-demand database of facts.
3 3
4extern crate fst; 4extern crate fst;
5extern crate ra_editor; 5extern crate ra_editor;
@@ -20,7 +20,7 @@ pub mod mock_analysis;
20 20
21use std::{fmt, sync::Arc}; 21use std::{fmt, sync::Arc};
22 22
23use ra_syntax::{AtomEdit, File, TextRange, TextUnit}; 23use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit};
24use rayon::prelude::*; 24use rayon::prelude::*;
25use relative_path::RelativePathBuf; 25use relative_path::RelativePathBuf;
26 26
@@ -128,13 +128,13 @@ pub struct FilePosition {
128#[derive(Debug)] 128#[derive(Debug)]
129pub struct SourceChange { 129pub struct SourceChange {
130 pub label: String, 130 pub label: String,
131 pub source_file_edits: Vec<SourceFileEdit>, 131 pub source_file_edits: Vec<SourceFileNodeEdit>,
132 pub file_system_edits: Vec<FileSystemEdit>, 132 pub file_system_edits: Vec<FileSystemEdit>,
133 pub cursor_position: Option<FilePosition>, 133 pub cursor_position: Option<FilePosition>,
134} 134}
135 135
136#[derive(Debug)] 136#[derive(Debug)]
137pub struct SourceFileEdit { 137pub struct SourceFileNodeEdit {
138 pub file_id: FileId, 138 pub file_id: FileId,
139 pub edits: Vec<AtomEdit>, 139 pub edits: Vec<AtomEdit>,
140} 140}
@@ -204,16 +204,16 @@ pub struct Analysis {
204} 204}
205 205
206impl Analysis { 206impl Analysis {
207 pub fn file_syntax(&self, file_id: FileId) -> File { 207 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
208 self.imp.file_syntax(file_id).clone() 208 self.imp.file_syntax(file_id).clone()
209 } 209 }
210 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 210 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
211 self.imp.file_line_index(file_id) 211 self.imp.file_line_index(file_id)
212 } 212 }
213 pub fn extend_selection(&self, file: &File, range: TextRange) -> TextRange { 213 pub fn extend_selection(&self, file: &SourceFileNode, range: TextRange) -> TextRange {
214 ra_editor::extend_selection(file, range).unwrap_or(range) 214 ra_editor::extend_selection(file, range).unwrap_or(range)
215 } 215 }
216 pub fn matching_brace(&self, file: &File, offset: TextUnit) -> Option<TextUnit> { 216 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
217 ra_editor::matching_brace(file, offset) 217 ra_editor::matching_brace(file, offset)
218 } 218 }
219 pub fn syntax_tree(&self, file_id: FileId) -> String { 219 pub fn syntax_tree(&self, file_id: FileId) -> String {
@@ -258,6 +258,13 @@ impl Analysis {
258 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 258 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
259 Ok(self.imp.find_all_refs(position)) 259 Ok(self.imp.find_all_refs(position))
260 } 260 }
261 pub fn doc_comment_for(
262 &self,
263 file_id: FileId,
264 symbol: FileSymbol,
265 ) -> Cancelable<Option<String>> {
266 self.imp.doc_comment_for(file_id, symbol)
267 }
261 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 268 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
262 self.imp.parent_module(position) 269 self.imp.parent_module(position)
263 } 270 }
@@ -302,7 +309,7 @@ pub struct LibraryData {
302impl LibraryData { 309impl LibraryData {
303 pub fn prepare(files: Vec<(FileId, String)>, file_resolver: Arc<FileResolver>) -> LibraryData { 310 pub fn prepare(files: Vec<(FileId, String)>, file_resolver: Arc<FileResolver>) -> LibraryData {
304 let symbol_index = SymbolIndex::for_files(files.par_iter().map(|(file_id, text)| { 311 let symbol_index = SymbolIndex::for_files(files.par_iter().map(|(file_id, text)| {
305 let file = File::parse(text); 312 let file = SourceFileNode::parse(text);
306 (*file_id, file) 313 (*file_id, file)
307 })); 314 }));
308 LibraryData { 315 LibraryData {
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs
index b57ad5d33..3a0667ecd 100644
--- a/crates/ra_analysis/src/symbol_index.rs
+++ b/crates/ra_analysis/src/symbol_index.rs
@@ -6,7 +6,7 @@ use std::{
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use ra_editor::{file_symbols, FileSymbol}; 7use ra_editor::{file_symbols, FileSymbol};
8use ra_syntax::{ 8use ra_syntax::{
9 File, 9 SourceFileNode,
10 SyntaxKind::{self, *}, 10 SyntaxKind::{self, *},
11}; 11};
12use rayon::prelude::*; 12use rayon::prelude::*;
@@ -34,7 +34,9 @@ impl Hash for SymbolIndex {
34} 34}
35 35
36impl SymbolIndex { 36impl SymbolIndex {
37 pub(crate) fn for_files(files: impl ParallelIterator<Item = (FileId, File)>) -> SymbolIndex { 37 pub(crate) fn for_files(
38 files: impl ParallelIterator<Item = (FileId, SourceFileNode)>,
39 ) -> SymbolIndex {
38 let mut symbols = files 40 let mut symbols = files
39 .flat_map(|(file_id, file)| { 41 .flat_map(|(file_id, file)| {
40 file_symbols(&file) 42 file_symbols(&file)
@@ -51,7 +53,7 @@ impl SymbolIndex {
51 SymbolIndex { symbols, map } 53 SymbolIndex { symbols, map }
52 } 54 }
53 55
54 pub(crate) fn for_file(file_id: FileId, file: File) -> SymbolIndex { 56 pub(crate) fn for_file(file_id: FileId, file: SourceFileNode) -> SymbolIndex {
55 SymbolIndex::for_files(rayon::iter::once((file_id, file))) 57 SymbolIndex::for_files(rayon::iter::once((file_id, file)))
56 } 58 }
57} 59}
diff --git a/crates/ra_analysis/src/syntax_ptr.rs b/crates/ra_analysis/src/syntax_ptr.rs
index 4afb1fc93..194b94584 100644
--- a/crates/ra_analysis/src/syntax_ptr.rs
+++ b/crates/ra_analysis/src/syntax_ptr.rs
@@ -1,4 +1,4 @@
1use ra_syntax::{File, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; 1use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange};
2 2
3use crate::db::SyntaxDatabase; 3use crate::db::SyntaxDatabase;
4use crate::FileId; 4use crate::FileId;
@@ -43,7 +43,7 @@ impl LocalSyntaxPtr {
43 } 43 }
44 } 44 }
45 45
46 pub(crate) fn resolve(self, file: &File) -> SyntaxNode { 46 pub(crate) fn resolve(self, file: &SourceFileNode) -> SyntaxNode {
47 let mut curr = file.syntax(); 47 let mut curr = file.syntax();
48 loop { 48 loop {
49 if curr.range() == self.range && curr.kind() == self.kind { 49 if curr.range() == self.range && curr.kind() == self.kind {
@@ -62,12 +62,17 @@ impl LocalSyntaxPtr {
62 local: self, 62 local: self,
63 } 63 }
64 } 64 }
65
66 // Seems unfortunate to expose
67 pub(crate) fn range(self) -> TextRange {
68 self.range
69 }
65} 70}
66 71
67#[test] 72#[test]
68fn test_local_syntax_ptr() { 73fn test_local_syntax_ptr() {
69 use ra_syntax::{ast, AstNode}; 74 use ra_syntax::{ast, AstNode};
70 let file = File::parse("struct Foo { f: u32, }"); 75 let file = SourceFileNode::parse("struct Foo { f: u32, }");
71 let field = file 76 let field = file
72 .syntax() 77 .syntax()
73 .descendants() 78 .descendants()
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index c605d34f0..719c166b5 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -452,3 +452,44 @@ fn test_complete_crate_path() {
452 &completions, 452 &completions,
453 ); 453 );
454} 454}
455
456#[test]
457fn test_complete_crate_path_with_braces() {
458 let (analysis, position) = analysis_and_position(
459 "
460 //- /lib.rs
461 mod foo;
462 struct Spam;
463 //- /foo.rs
464 use crate::{Sp<|>};
465 ",
466 );
467 let completions = analysis.completions(position).unwrap().unwrap();
468 assert_eq_dbg(
469 r#"[CompletionItem { label: "foo", lookup: None, snippet: None },
470 CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
471 &completions,
472 );
473}
474
475#[test]
476fn test_complete_crate_path_in_nested_tree() {
477 let (analysis, position) = analysis_and_position(
478 "
479 //- /lib.rs
480 mod foo;
481 pub mod bar {
482 pub mod baz {
483 pub struct Spam;
484 }
485 }
486 //- /foo.rs
487 use crate::{bar::{baz::Sp<|>}};
488 ",
489 );
490 let completions = analysis.completions(position).unwrap().unwrap();
491 assert_eq_dbg(
492 r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
493 &completions,
494 );
495}