aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_analysis/src/completion.rs148
-rw-r--r--crates/ra_analysis/src/imp.rs15
2 files changed, 75 insertions, 88 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index 6fd30aaee..27566a8a1 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 ast::{self, AstChildren, LoopBodyOwner, ModuleItemOwner}, 4 ast::{self, AstChildren, LoopBodyOwner, ModuleItemOwner},
5 AstNode, AtomEdit, SourceFileNode, 5 AstNode, AtomEdit, SourceFileNode,
6 SyntaxKind::*, 6 SyntaxKind::*,
7 SyntaxNodeRef, TextUnit, 7 SyntaxNodeRef,
8}; 8};
9use rustc_hash::{FxHashMap, FxHashSet}; 9use rustc_hash::{FxHashMap, FxHashSet};
10 10
@@ -14,7 +14,7 @@ use crate::{
14 descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, 14 descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
15 descriptors::DescriptorDatabase, 15 descriptors::DescriptorDatabase,
16 input::FilesDatabase, 16 input::FilesDatabase,
17 Cancelable, FilePosition, 17 Cancelable, FilePosition, FileId,
18}; 18};
19 19
20#[derive(Debug)] 20#[derive(Debug)]
@@ -27,47 +27,87 @@ 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 =
38 match module_tree.any_module_for_source(ModuleSource::SourceFile(position.file_id)) {
39 None => return Ok(None),
40 Some(it) => it,
41 };
42 let file = { 36 let file = {
43 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); 37 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
44 file.reparse(&edit) 38 original_file.reparse(&edit)
45 }; 39 };
46 let target_module_id = match find_target_module(&module_tree, module_id, &file, position.offset) 40
47 { 41 let mut res = Vec::new();
48 None => return Ok(None), 42 let mut has_completions = false;
43 // First, let's try to complete a reference to some declaration.
44 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
45 has_completions = true;
46 // completion from lexical scope
47 complete_name_ref(&file, name_ref, &mut res);
48 // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
49 if is_node::<ast::Param>(name_ref.syntax()) {
50 param_completions(name_ref.syntax(), &mut res);
51 }
52 // snippet completions
53 {
54 let name_range = name_ref.syntax().range();
55 let top_node = name_ref
56 .syntax()
57 .ancestors()
58 .take_while(|it| it.range() == name_range)
59 .last()
60 .unwrap();
61 match top_node.parent().map(|it| it.kind()) {
62 Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res),
63 _ => (),
64 }
65 }
66 complete_path(db, position.file_id, name_ref, &mut res)?;
67 }
68
69 // Otherwise, if this is a declaration, use heuristics to suggest a name.
70 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
71 if is_node::<ast::Param>(name.syntax()) {
72 has_completions = true;
73 param_completions(name.syntax(), &mut res);
74 }
75 }
76 let res = if has_completions { Some(res) } else { None };
77 Ok(res)
78}
79
80fn complete_path(
81 db: &db::RootDatabase,
82 file_id: FileId,
83 name_ref: ast::NameRef,
84 acc: &mut Vec<CompletionItem>,
85) -> Cancelable<()> {
86 let source_root_id = db.file_source_root(file_id);
87 let module_tree = db.module_tree(source_root_id)?;
88 let module_id = match module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
89 None => return Ok(()),
90 Some(it) => it,
91 };
92 let target_module_id = match find_target_module(&module_tree, module_id, name_ref) {
93 None => return Ok(()),
49 Some(it) => it, 94 Some(it) => it,
50 }; 95 };
51 let module_scope = db.module_scope(source_root_id, target_module_id)?; 96 let module_scope = db.module_scope(source_root_id, target_module_id)?;
52 let res: Vec<_> = module_scope 97 let completions = module_scope.entries().iter().map(|entry| CompletionItem {
53 .entries() 98 label: entry.name().to_string(),
54 .iter() 99 lookup: None,
55 .map(|entry| CompletionItem { 100 snippet: None,
56 label: entry.name().to_string(), 101 });
57 lookup: None, 102 acc.extend(completions);
58 snippet: None, 103 Ok(())
59 })
60 .collect();
61 Ok(Some(res))
62} 104}
63 105
64pub(crate) fn find_target_module( 106fn find_target_module(
65 module_tree: &ModuleTree, 107 module_tree: &ModuleTree,
66 module_id: ModuleId, 108 module_id: ModuleId,
67 file: &SourceFileNode, 109 name_ref: ast::NameRef,
68 offset: TextUnit,
69) -> Option<ModuleId> { 110) -> Option<ModuleId> {
70 let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset)?;
71 let mut crate_path = crate_path(name_ref)?; 111 let mut crate_path = crate_path(name_ref)?;
72 112
73 crate_path.pop(); 113 crate_path.pop();
@@ -98,50 +138,6 @@ fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> {
98 Some(res) 138 Some(res)
99} 139}
100 140
101pub(crate) fn scope_completion(
102 db: &db::RootDatabase,
103 position: FilePosition,
104) -> Option<Vec<CompletionItem>> {
105 let original_file = db.file_syntax(position.file_id);
106 // Insert a fake ident to get a valid parse tree
107 let file = {
108 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
109 original_file.reparse(&edit)
110 };
111 let mut has_completions = false;
112 let mut res = Vec::new();
113 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
114 has_completions = true;
115 complete_name_ref(&file, name_ref, &mut res);
116 // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
117 if is_node::<ast::Param>(name_ref.syntax()) {
118 param_completions(name_ref.syntax(), &mut res);
119 }
120 let name_range = name_ref.syntax().range();
121 let top_node = name_ref
122 .syntax()
123 .ancestors()
124 .take_while(|it| it.range() == name_range)
125 .last()
126 .unwrap();
127 match top_node.parent().map(|it| it.kind()) {
128 Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(&mut res),
129 _ => (),
130 }
131 }
132 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
133 if is_node::<ast::Param>(name.syntax()) {
134 has_completions = true;
135 param_completions(name.syntax(), &mut res);
136 }
137 }
138 if has_completions {
139 Some(res)
140 } else {
141 None
142 }
143}
144
145fn complete_module_items( 141fn complete_module_items(
146 file: &SourceFileNode, 142 file: &SourceFileNode,
147 items: AstChildren<ast::ModuleItem>, 143 items: AstChildren<ast::ModuleItem>,
@@ -383,7 +379,8 @@ mod tests {
383 379
384 fn check_scope_completion(code: &str, expected_completions: &str) { 380 fn check_scope_completion(code: &str, expected_completions: &str) {
385 let (analysis, position) = single_file_with_position(code); 381 let (analysis, position) = single_file_with_position(code);
386 let completions = scope_completion(&analysis.imp.db, position) 382 let completions = completions(&analysis.imp.db, position)
383 .unwrap()
387 .unwrap() 384 .unwrap()
388 .into_iter() 385 .into_iter()
389 .filter(|c| c.snippet.is_none()) 386 .filter(|c| c.snippet.is_none())
@@ -393,7 +390,8 @@ mod tests {
393 390
394 fn check_snippet_completion(code: &str, expected_completions: &str) { 391 fn check_snippet_completion(code: &str, expected_completions: &str) {
395 let (analysis, position) = single_file_with_position(code); 392 let (analysis, position) = single_file_with_position(code);
396 let completions = scope_completion(&analysis.imp.db, position) 393 let completions = completions(&analysis.imp.db, position)
394 .unwrap()
397 .unwrap() 395 .unwrap()
398 .into_iter() 396 .into_iter()
399 .filter(|c| c.snippet.is_some()) 397 .filter(|c| c.snippet.is_some())
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 614a6e9be..74c248a96 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -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},
@@ -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,