aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis/src/module_map_db/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libanalysis/src/module_map_db/mod.rs')
-rw-r--r--crates/libanalysis/src/module_map_db/mod.rs180
1 files changed, 180 insertions, 0 deletions
diff --git a/crates/libanalysis/src/module_map_db/mod.rs b/crates/libanalysis/src/module_map_db/mod.rs
new file mode 100644
index 000000000..777f7a38a
--- /dev/null
+++ b/crates/libanalysis/src/module_map_db/mod.rs
@@ -0,0 +1,180 @@
1mod descr;
2
3use std::sync::Arc;
4use {
5 FileId,
6 db::{
7 BoxedQuery, Query, QueryCtx
8 },
9 module_map::resolve_submodule,
10};
11
12pub(crate) fn queries(acc: &mut Vec<BoxedQuery>) {
13 acc.push(MODULE_DESCR.into());
14 acc.push(RESOLVE_SUBMODULE.into());
15 acc.push(PARENT_MODULE.into());
16}
17
18impl<'a> QueryCtx<'a> {
19 fn module_descr(&self, file_id: FileId) -> Arc<descr::ModuleDescr> {
20 self.get(MODULE_DESCR, file_id)
21 }
22 fn resolve_submodule(&self, file_id: FileId, submod: descr::Submodule) -> Arc<Vec<FileId>> {
23 self.get(RESOLVE_SUBMODULE, (file_id, submod))
24 }
25}
26
27pub(crate) const MODULE_DESCR: Query<FileId, descr::ModuleDescr> = Query {
28 id: 30,
29 f: |ctx, &file_id| {
30 let file = ctx.file_syntax(file_id);
31 descr::ModuleDescr::new(file.ast())
32 }
33};
34
35pub(crate) const RESOLVE_SUBMODULE: Query<(FileId, descr::Submodule), Vec<FileId>> = Query {
36 id: 31,
37 f: |ctx, params| {
38 let files = ctx.file_set();
39 resolve_submodule(params.0, &params.1.name, &files.1).0
40 }
41};
42
43pub(crate) const PARENT_MODULE: Query<FileId, Vec<FileId>> = Query {
44 id: 40,
45 f: |ctx, file_id| {
46 let files = ctx.file_set();
47 let res = files.0.iter()
48 .map(|&parent_id| (parent_id, ctx.module_descr(parent_id)))
49 .filter(|(parent_id, descr)| {
50 descr.submodules.iter()
51 .any(|subm| {
52 ctx.resolve_submodule(*parent_id, subm.clone())
53 .iter()
54 .any(|it| it == file_id)
55 })
56 })
57 .map(|(id, _)| id)
58 .collect();
59 res
60 }
61};
62
63#[cfg(test)]
64mod tests {
65 use std::collections::HashMap;
66 use im;
67 use relative_path::{RelativePath, RelativePathBuf};
68 use {
69 db::{Query, Db, State},
70 imp::FileResolverImp,
71 FileId, FileResolver,
72 };
73 use super::*;
74
75 #[derive(Debug)]
76 struct FileMap(im::HashMap<FileId, RelativePathBuf>);
77
78 impl FileResolver for FileMap {
79 fn file_stem(&self, file_id: FileId) -> String {
80 self.0[&file_id].file_stem().unwrap().to_string()
81 }
82 fn resolve(&self, file_id: FileId, rel: &RelativePath) -> Option<FileId> {
83 let path = self.0[&file_id].join(rel).normalize();
84 self.0.iter()
85 .filter_map(|&(id, ref p)| Some(id).filter(|_| p == &path))
86 .next()
87 }
88 }
89
90 struct Fixture {
91 next_file_id: u32,
92 fm: im::HashMap<FileId, RelativePathBuf>,
93 db: Db,
94 }
95
96 impl Fixture {
97 fn new() -> Fixture {
98 Fixture {
99 next_file_id: 1,
100 fm: im::HashMap::new(),
101 db: Db::new(State::default()),
102 }
103 }
104 fn add_file(&mut self, path: &str, text: &str) -> FileId {
105 assert!(path.starts_with("/"));
106 let file_id = FileId(self.next_file_id);
107 self.next_file_id += 1;
108 self.fm.insert(file_id, RelativePathBuf::from(&path[1..]));
109 let mut new_state = self.db.state().clone();
110 new_state.file_map.insert(file_id, text.to_string().into_boxed_str().into());
111 new_state.resolver = FileResolverImp::new(
112 Arc::new(FileMap(self.fm.clone()))
113 );
114 self.db = self.db.with_state(new_state, &[file_id], true);
115 file_id
116 }
117 fn remove_file(&mut self, file_id: FileId) {
118 self.fm.remove(&file_id);
119 let mut new_state = self.db.state().clone();
120 new_state.file_map.remove(&file_id);
121 new_state.resolver = FileResolverImp::new(
122 Arc::new(FileMap(self.fm.clone()))
123 );
124 self.db = self.db.with_state(new_state, &[file_id], true);
125 }
126 fn change_file(&mut self, file_id: FileId, new_text: &str) {
127 let mut new_state = self.db.state().clone();
128 new_state.file_map.insert(file_id, new_text.to_string().into_boxed_str().into());
129 self.db = self.db.with_state(new_state, &[file_id], false);
130 }
131 fn check_parent_modules(
132 &self,
133 file_id: FileId,
134 expected: &[FileId],
135 queries: &[(u16, u64)]
136 ) {
137 let (actual, events) = self.db.get(PARENT_MODULE, file_id);
138 assert_eq!(actual.as_slice(), expected);
139 let mut counts = HashMap::new();
140 events.into_iter()
141 .for_each(|event| *counts.entry(event).or_insert(0) += 1);
142 for &(query_id, expected_count) in queries.iter() {
143 let actual_count = *counts.get(&query_id).unwrap_or(&0);
144 assert_eq!(
145 actual_count,
146 expected_count,
147 "counts for {} differ",
148 query_id,
149 )
150 }
151
152 }
153 }
154
155 #[test]
156 fn test_parent_module() {
157 let mut f = Fixture::new();
158 let foo = f.add_file("/foo.rs", "");
159 f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 1)]);
160
161 let lib = f.add_file("/lib.rs", "mod foo;");
162 f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 1)]);
163 f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 0)]);
164
165 f.change_file(lib, "");
166 f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 1)]);
167
168 f.change_file(lib, "mod foo;");
169 f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 1)]);
170
171 f.change_file(lib, "mod bar;");
172 f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 1)]);
173
174 f.change_file(lib, "mod foo;");
175 f.check_parent_modules(foo, &[lib], &[(MODULE_DESCR.id, 1)]);
176
177 f.remove_file(lib);
178 f.check_parent_modules(foo, &[], &[(MODULE_DESCR.id, 0)]);
179 }
180}