diff options
Diffstat (limited to 'crates/ra_analysis/src/completion.rs')
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs new file mode 100644 index 000000000..a0fd6828d --- /dev/null +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -0,0 +1,61 @@ | |||
1 | use ra_editor::{CompletionItem, find_node_at_offset, complete_module_items}; | ||
2 | use ra_syntax::{ | ||
3 | AtomEdit, File, TextUnit, AstNode, | ||
4 | ast::{self, ModuleItemOwner}, | ||
5 | }; | ||
6 | |||
7 | use crate::{ | ||
8 | FileId, Cancelable, | ||
9 | db::{self, SyntaxDatabase}, | ||
10 | descriptors::module::{ModulesDatabase, ModuleTree, ModuleId}, | ||
11 | }; | ||
12 | |||
13 | pub(crate) fn resolve_based_completion(db: &db::RootDatabase, file_id: FileId, offset: TextUnit) -> Cancelable<Option<Vec<CompletionItem>>> { | ||
14 | let file = db.file_syntax(file_id); | ||
15 | let module_tree = db.module_tree()?; | ||
16 | let file = { | ||
17 | let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); | ||
18 | file.reparse(&edit) | ||
19 | }; | ||
20 | let target_file = match find_target_module(&module_tree, file_id, &file, offset) { | ||
21 | None => return Ok(None), | ||
22 | Some(target_module) => { | ||
23 | let file_id = target_module.file_id(&module_tree); | ||
24 | db.file_syntax(file_id) | ||
25 | } | ||
26 | }; | ||
27 | let mut res = Vec::new(); | ||
28 | complete_module_items(target_file.ast().items(), None, &mut res); | ||
29 | Ok(Some(res)) | ||
30 | } | ||
31 | |||
32 | pub(crate) fn find_target_module(module_tree: &ModuleTree, file_id: FileId, file: &File, offset: TextUnit) -> Option<ModuleId> { | ||
33 | let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset)?; | ||
34 | let mut crate_path = crate_path(name_ref)?; | ||
35 | let module_id = module_tree.any_module_for_file(file_id)?; | ||
36 | crate_path.pop(); | ||
37 | let mut target_module = module_id.root(&module_tree); | ||
38 | for name in crate_path { | ||
39 | target_module = target_module.child(module_tree, name.text().as_str())?; | ||
40 | } | ||
41 | Some(target_module) | ||
42 | } | ||
43 | |||
44 | fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> { | ||
45 | let mut path = name_ref.syntax() | ||
46 | .parent().and_then(ast::PathSegment::cast)? | ||
47 | .parent_path(); | ||
48 | let mut res = Vec::new(); | ||
49 | loop { | ||
50 | let segment = path.segment()?; | ||
51 | match segment.kind()? { | ||
52 | ast::PathSegmentKind::Name(name) => res.push(name), | ||
53 | ast::PathSegmentKind::CrateKw => break, | ||
54 | ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => | ||
55 | return None, | ||
56 | } | ||
57 | path = path.qualifier()?; | ||
58 | } | ||
59 | res.reverse(); | ||
60 | Some(res) | ||
61 | } | ||