diff options
Diffstat (limited to 'crates/libanalysis/src/module_map_db.rs')
-rw-r--r-- | crates/libanalysis/src/module_map_db.rs | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/crates/libanalysis/src/module_map_db.rs b/crates/libanalysis/src/module_map_db.rs new file mode 100644 index 000000000..1ef87ab3f --- /dev/null +++ b/crates/libanalysis/src/module_map_db.rs | |||
@@ -0,0 +1,189 @@ | |||
1 | use std::sync::Arc; | ||
2 | use { | ||
3 | FileId, | ||
4 | db::{Query, Eval, QueryCtx, FileSyntax, Files}, | ||
5 | module_map::resolve_submodule, | ||
6 | }; | ||
7 | |||
8 | enum ModuleDescr {} | ||
9 | impl Query for ModuleDescr { | ||
10 | const ID: u32 = 30; | ||
11 | type Params = FileId; | ||
12 | type Output = Arc<descr::ModuleDescr>; | ||
13 | } | ||
14 | |||
15 | enum ResolveSubmodule {} | ||
16 | impl Query for ResolveSubmodule { | ||
17 | const ID: u32 = 31; | ||
18 | type Params = (FileId, descr::Submodule); | ||
19 | type Output = Arc<Vec<FileId>>; | ||
20 | } | ||
21 | |||
22 | enum ParentModule {} | ||
23 | impl Query for ParentModule { | ||
24 | const ID: u32 = 40; | ||
25 | type Params = FileId; | ||
26 | type Output = Arc<Vec<FileId>>; | ||
27 | } | ||
28 | |||
29 | impl Eval for ModuleDescr { | ||
30 | fn eval(ctx: &QueryCtx, file_id: &FileId) -> Arc<descr::ModuleDescr> { | ||
31 | let file = ctx.get::<FileSyntax>(file_id); | ||
32 | Arc::new(descr::ModuleDescr::new(file.ast())) | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl Eval for ResolveSubmodule { | ||
37 | fn eval(ctx: &QueryCtx, &(file_id, ref submodule): &(FileId, descr::Submodule)) -> Arc<Vec<FileId>> { | ||
38 | let files = ctx.get::<Files>(&()); | ||
39 | let res = resolve_submodule(file_id, &submodule.name, &files.file_resolver()).0; | ||
40 | Arc::new(res) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | impl Eval for ParentModule { | ||
45 | fn eval(ctx: &QueryCtx, file_id: &FileId) -> Arc<Vec<FileId>> { | ||
46 | let files = ctx.get::<Files>(&()); | ||
47 | let res = files.iter() | ||
48 | .map(|parent_id| (parent_id, ctx.get::<ModuleDescr>(&parent_id))) | ||
49 | .filter(|(parent_id, descr)| { | ||
50 | descr.submodules.iter() | ||
51 | .any(|subm| { | ||
52 | ctx.get::<ResolveSubmodule>(&(*parent_id, subm.clone())) | ||
53 | .iter() | ||
54 | .any(|it| it == file_id) | ||
55 | }) | ||
56 | }) | ||
57 | .map(|(id, _)| id) | ||
58 | .collect(); | ||
59 | Arc::new(res) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | mod descr { | ||
64 | use libsyntax2::{ | ||
65 | SmolStr, | ||
66 | ast::{self, NameOwner}, | ||
67 | }; | ||
68 | |||
69 | pub struct ModuleDescr { | ||
70 | pub submodules: Vec<Submodule> | ||
71 | } | ||
72 | |||
73 | impl ModuleDescr { | ||
74 | pub fn new(root: ast::Root) -> ModuleDescr { | ||
75 | let submodules = root | ||
76 | .modules() | ||
77 | .filter_map(|module| { | ||
78 | let name = module.name()?.text(); | ||
79 | if !module.has_semi() { | ||
80 | return None; | ||
81 | } | ||
82 | Some(Submodule { name }) | ||
83 | }).collect(); | ||
84 | |||
85 | ModuleDescr { submodules } } | ||
86 | } | ||
87 | |||
88 | #[derive(Clone, Hash)] | ||
89 | pub struct Submodule { | ||
90 | pub name: SmolStr, | ||
91 | } | ||
92 | |||
93 | } | ||
94 | |||
95 | #[cfg(test)] | ||
96 | mod tests { | ||
97 | use super::*; | ||
98 | use im; | ||
99 | use relative_path::{RelativePath, RelativePathBuf}; | ||
100 | use { | ||
101 | db::Db, | ||
102 | imp::FileResolverImp, | ||
103 | FileId, FileResolver, | ||
104 | }; | ||
105 | |||
106 | #[derive(Debug)] | ||
107 | struct FileMap(im::HashMap<FileId, RelativePathBuf>); | ||
108 | |||
109 | impl FileResolver for FileMap { | ||
110 | fn file_stem(&self, file_id: FileId) -> String { | ||
111 | self.0[&file_id].file_stem().unwrap().to_string() | ||
112 | } | ||
113 | fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option<FileId> { | ||
114 | let path = self.0[&file_id].join(rel).normalize(); | ||
115 | self.0.iter() | ||
116 | .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path)) | ||
117 | .next() | ||
118 | } | ||
119 | } | ||
120 | |||
121 | struct Fixture { | ||
122 | next_file_id: u32, | ||
123 | fm: im::HashMap<FileId, RelativePathBuf>, | ||
124 | db: Db, | ||
125 | } | ||
126 | |||
127 | impl Fixture { | ||
128 | fn new() -> Fixture { | ||
129 | Fixture { | ||
130 | next_file_id: 1, | ||
131 | fm: im::HashMap::new(), | ||
132 | db: Db::new(), | ||
133 | } | ||
134 | } | ||
135 | fn add_file(&mut self, path: &str, text: &str) -> FileId { | ||
136 | assert!(path.starts_with("/")); | ||
137 | let file_id = FileId(self.next_file_id); | ||
138 | self.next_file_id += 1; | ||
139 | self.fm.insert(file_id, RelativePathBuf::from(&path[1..])); | ||
140 | self.db.change_file(file_id, Some(text.to_string())); | ||
141 | self.db.set_file_resolver(FileResolverImp::new( | ||
142 | Arc::new(FileMap(self.fm.clone())) | ||
143 | )); | ||
144 | |||
145 | file_id | ||
146 | } | ||
147 | fn remove_file(&mut self, file_id: FileId) { | ||
148 | self.fm.remove(&file_id); | ||
149 | self.db.change_file(file_id, None); | ||
150 | self.db.set_file_resolver(FileResolverImp::new( | ||
151 | Arc::new(FileMap(self.fm.clone())) | ||
152 | )) | ||
153 | } | ||
154 | fn change_file(&mut self, file_id: FileId, new_text: &str) { | ||
155 | self.db.change_file(file_id, Some(new_text.to_string())); | ||
156 | } | ||
157 | fn check_parent_modules(&self, file_id: FileId, expected: &[FileId]) { | ||
158 | let ctx = self.db.query_ctx(); | ||
159 | let actual = ctx.get::<ParentModule>(&file_id); | ||
160 | assert_eq!(actual.as_slice(), expected); | ||
161 | } | ||
162 | } | ||
163 | |||
164 | #[test] | ||
165 | fn test_parent_module() { | ||
166 | let mut f = Fixture::new(); | ||
167 | let foo = f.add_file("/foo.rs", ""); | ||
168 | f.check_parent_modules(foo, &[]); | ||
169 | |||
170 | let lib = f.add_file("/lib.rs", "mod foo;"); | ||
171 | f.check_parent_modules(foo, &[lib]); | ||
172 | |||
173 | f.change_file(lib, ""); | ||
174 | f.check_parent_modules(foo, &[]); | ||
175 | |||
176 | f.change_file(lib, "mod foo;"); | ||
177 | f.check_parent_modules(foo, &[lib]); | ||
178 | |||
179 | f.change_file(lib, "mod bar;"); | ||
180 | f.check_parent_modules(foo, &[]); | ||
181 | |||
182 | f.change_file(lib, "mod foo;"); | ||
183 | f.check_parent_modules(foo, &[lib]); | ||
184 | |||
185 | f.remove_file(lib); | ||
186 | f.check_parent_modules(foo, &[]); | ||
187 | } | ||
188 | |||
189 | } | ||