aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src/completion.rs
blob: a0fd6828d62535a312aaca18b7ec613bcfc8dc7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use ra_editor::{CompletionItem, find_node_at_offset, complete_module_items};
use ra_syntax::{
    AtomEdit, File, TextUnit, AstNode,
    ast::{self, ModuleItemOwner},
};

use crate::{
    FileId, Cancelable,
    db::{self, SyntaxDatabase},
    descriptors::module::{ModulesDatabase, ModuleTree, ModuleId},
};

pub(crate) fn resolve_based_completion(db: &db::RootDatabase, file_id: FileId, offset: TextUnit) -> Cancelable<Option<Vec<CompletionItem>>> {
    let file = db.file_syntax(file_id);
    let module_tree = db.module_tree()?;
    let file = {
        let edit = AtomEdit::insert(offset, "intellijRulezz".to_string());
        file.reparse(&edit)
    };
    let target_file = match find_target_module(&module_tree, file_id, &file, offset) {
        None => return Ok(None),
        Some(target_module) => {
            let file_id = target_module.file_id(&module_tree);
            db.file_syntax(file_id)
        }
    };
    let mut res = Vec::new();
    complete_module_items(target_file.ast().items(), None, &mut res);
    Ok(Some(res))
}

pub(crate) fn find_target_module(module_tree: &ModuleTree, file_id: FileId, file: &File, offset: TextUnit) -> Option<ModuleId> {
    let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), offset)?;
    let mut crate_path = crate_path(name_ref)?;
    let module_id = module_tree.any_module_for_file(file_id)?;
    crate_path.pop();
    let mut target_module = module_id.root(&module_tree);
    for name in crate_path {
        target_module = target_module.child(module_tree, name.text().as_str())?;
    }
    Some(target_module)
}

fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> {
    let mut path = name_ref.syntax()
        .parent().and_then(ast::PathSegment::cast)?
        .parent_path();
    let mut res = Vec::new();
    loop {
        let segment = path.segment()?;
        match segment.kind()? {
            ast::PathSegmentKind::Name(name) => res.push(name),
            ast::PathSegmentKind::CrateKw => break,
            ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw =>
                return None,
        }
        path = path.qualifier()?;
    }
    res.reverse();
    Some(res)
}