diff options
Diffstat (limited to 'crates/ra_hir/src/source_binder.rs')
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs new file mode 100644 index 000000000..479155805 --- /dev/null +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -0,0 +1,96 @@ | |||
1 | /// Lookup hir elements using position in the source code. This is a lossy | ||
2 | /// transformation: in general, a single source might correspond to several | ||
3 | /// modules, functions, etc, due to macros, cfgs and `#[path=]` attributes on | ||
4 | /// modules. | ||
5 | /// | ||
6 | /// So, this modules should not be used during hir construction, it exists | ||
7 | /// purely for "IDE needs". | ||
8 | use ra_db::{FileId, FilePosition, Cancelable}; | ||
9 | use ra_editor::find_node_at_offset; | ||
10 | use ra_syntax::{ | ||
11 | ast::{self, AstNode}, | ||
12 | SyntaxNodeRef, | ||
13 | }; | ||
14 | |||
15 | use crate::{ | ||
16 | HirDatabase, Module, Function, SourceItemId, | ||
17 | module::ModuleSource, | ||
18 | DefKind, DefLoc | ||
19 | }; | ||
20 | |||
21 | /// Locates the module by `FileId`. Picks topmost module in the file. | ||
22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { | ||
23 | let module_source = ModuleSource::new_file(db, file_id); | ||
24 | module_from_source(db, module_source) | ||
25 | } | ||
26 | |||
27 | /// Locates the module by position in the source code. | ||
28 | pub fn module_from_position( | ||
29 | db: &impl HirDatabase, | ||
30 | position: FilePosition, | ||
31 | ) -> Cancelable<Option<Module>> { | ||
32 | let file = db.source_file(position.file_id); | ||
33 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { | ||
34 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), | ||
35 | _ => ModuleSource::new_file(db, position.file_id), | ||
36 | }; | ||
37 | module_from_source(db, module_source) | ||
38 | } | ||
39 | |||
40 | /// Locates the module by child syntax element within the module | ||
41 | pub fn module_from_child_node( | ||
42 | db: &impl HirDatabase, | ||
43 | file_id: FileId, | ||
44 | child: SyntaxNodeRef, | ||
45 | ) -> Cancelable<Option<Module>> { | ||
46 | let module_source = if let Some(m) = child | ||
47 | .ancestors() | ||
48 | .filter_map(ast::Module::cast) | ||
49 | .find(|it| !it.has_semi()) | ||
50 | { | ||
51 | ModuleSource::new_inline(db, file_id, m) | ||
52 | } else { | ||
53 | ModuleSource::new_file(db, file_id) | ||
54 | }; | ||
55 | module_from_source(db, module_source) | ||
56 | } | ||
57 | |||
58 | fn module_from_source( | ||
59 | db: &impl HirDatabase, | ||
60 | module_source: ModuleSource, | ||
61 | ) -> Cancelable<Option<Module>> { | ||
62 | let source_root_id = db.file_source_root(module_source.file_id()); | ||
63 | let module_tree = db.module_tree(source_root_id)?; | ||
64 | let m = module_tree | ||
65 | .modules_with_sources() | ||
66 | .find(|(_id, src)| src == &module_source); | ||
67 | let module_id = ctry!(m).0; | ||
68 | Ok(Some(Module::new(db, source_root_id, module_id)?)) | ||
69 | } | ||
70 | |||
71 | pub fn function_from_source( | ||
72 | db: &impl HirDatabase, | ||
73 | file_id: FileId, | ||
74 | fn_def: ast::FnDef, | ||
75 | ) -> Cancelable<Option<Function>> { | ||
76 | let module = ctry!(module_from_child_node(db, file_id, fn_def.syntax())?); | ||
77 | let file_items = db.file_items(file_id); | ||
78 | let item_id = file_items.id_of(fn_def.syntax()); | ||
79 | let source_item_id = SourceItemId { file_id, item_id }; | ||
80 | let def_loc = DefLoc { | ||
81 | kind: DefKind::Function, | ||
82 | source_root_id: module.source_root_id, | ||
83 | module_id: module.module_id, | ||
84 | source_item_id, | ||
85 | }; | ||
86 | Ok(Some(Function::new(def_loc.id(db)))) | ||
87 | } | ||
88 | |||
89 | pub fn function_from_child_node( | ||
90 | db: &impl HirDatabase, | ||
91 | file_id: FileId, | ||
92 | node: SyntaxNodeRef, | ||
93 | ) -> Cancelable<Option<Function>> { | ||
94 | let fn_def = ctry!(node.ancestors().find_map(ast::FnDef::cast)); | ||
95 | function_from_source(db, file_id, fn_def) | ||
96 | } | ||