aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/Cargo.toml1
-rw-r--r--crates/libanalysis/src/db.rs121
-rw-r--r--crates/libanalysis/src/lib.rs3
-rw-r--r--crates/libanalysis/src/module_map.rs51
-rw-r--r--crates/libanalysis/src/module_map_db.rs189
-rw-r--r--crates/libanalysis/tests/tests.rs35
-rw-r--r--crates/server/Cargo.toml2
7 files changed, 361 insertions, 41 deletions
diff --git a/crates/libanalysis/Cargo.toml b/crates/libanalysis/Cargo.toml
index 4d565e95f..4c92951b1 100644
--- a/crates/libanalysis/Cargo.toml
+++ b/crates/libanalysis/Cargo.toml
@@ -11,6 +11,7 @@ parking_lot = "0.6.3"
11once_cell = "0.1.5" 11once_cell = "0.1.5"
12rayon = "1.0.2" 12rayon = "1.0.2"
13fst = "0.3.1" 13fst = "0.3.1"
14im = "12.0.0"
14libsyntax2 = { path = "../libsyntax2" } 15libsyntax2 = { path = "../libsyntax2" }
15libeditor = { path = "../libeditor" } 16libeditor = { path = "../libeditor" }
16 17
diff --git a/crates/libanalysis/src/db.rs b/crates/libanalysis/src/db.rs
new file mode 100644
index 000000000..335c79e76
--- /dev/null
+++ b/crates/libanalysis/src/db.rs
@@ -0,0 +1,121 @@
1use std::{
2 hash::Hash,
3 sync::Arc,
4};
5use libsyntax2::{File};
6use im;
7use {
8 FileId,
9 imp::{FileResolverImp},
10};
11
12#[derive(Clone)]
13pub(crate) struct Db {
14 file_resolver: FileResolverImp,
15 files: im::HashMap<FileId, Arc<String>>,
16}
17
18impl Db {
19 pub(crate) fn new() -> Db {
20 Db {
21 file_resolver: FileResolverImp::default(),
22 files: im::HashMap::new(),
23 }
24 }
25 pub(crate) fn change_file(&mut self, file_id: FileId, text: Option<String>) {
26 match text {
27 None => {
28 self.files.remove(&file_id);
29 }
30 Some(text) => {
31 self.files.insert(file_id, Arc::new(text));
32 }
33 }
34 }
35 pub(crate) fn set_file_resolver(&mut self, file_resolver: FileResolverImp) {
36 self.file_resolver = file_resolver
37 }
38 pub(crate) fn query_ctx(&self) -> QueryCtx {
39 QueryCtx { db: self.clone() }
40 }
41}
42
43pub(crate) struct QueryCtx {
44 db: Db
45}
46
47impl QueryCtx {
48 pub(crate) fn get<Q: Get>(&self, params: &Q::Params) -> Q::Output {
49 Q::get(self, params)
50 }
51}
52
53pub(crate) trait Query {
54 const ID: u32;
55 type Params: Hash;
56 type Output;
57}
58
59pub(crate) trait Get: Query {
60 fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output;
61}
62
63impl<T: Eval> Get for T {
64 fn get(ctx: &QueryCtx, params: &Self::Params) -> Self::Output {
65 Self::eval(ctx, params)
66 }
67}
68
69pub(crate) trait Eval: Query {
70 fn eval(ctx: &QueryCtx, params: &Self::Params) -> Self::Output;
71}
72
73pub(crate) struct DbFiles {
74 db: Db,
75}
76
77impl DbFiles {
78 pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item=FileId> + 'a {
79 self.db.files.keys().cloned()
80 }
81 pub(crate) fn file_resolver(&self) -> FileResolverImp {
82 self.db.file_resolver.clone()
83 }
84}
85
86pub(crate) enum Files {}
87impl Query for Files {
88 const ID: u32 = 1;
89 type Params = ();
90 type Output = DbFiles;
91}
92impl Get for Files {
93 fn get(ctx: &QueryCtx, _params: &()) -> DbFiles {
94 DbFiles { db: ctx.db.clone() }
95 }
96}
97
98enum FileText {}
99impl Query for FileText {
100 const ID: u32 = 10;
101 type Params = FileId;
102 type Output = Arc<String>;
103}
104impl Get for FileText {
105 fn get(ctx: &QueryCtx, file_id: &FileId) -> Arc<String> {
106 ctx.db.files[file_id].clone()
107 }
108}
109
110pub(crate) enum FileSyntax {}
111impl Query for FileSyntax {
112 const ID: u32 = 20;
113 type Params = FileId;
114 type Output = File;
115}
116impl Eval for FileSyntax {
117 fn eval(ctx: &QueryCtx, file_id: &FileId) -> File {
118 let text = ctx.get::<FileText>(file_id);
119 File::parse(&text)
120 }
121}
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs
index 80cde079f..68cf31e08 100644
--- a/crates/libanalysis/src/lib.rs
+++ b/crates/libanalysis/src/lib.rs
@@ -9,12 +9,15 @@ extern crate rayon;
9extern crate relative_path; 9extern crate relative_path;
10#[macro_use] 10#[macro_use]
11extern crate crossbeam_channel; 11extern crate crossbeam_channel;
12extern crate im;
12 13
13mod symbol_index; 14mod symbol_index;
14mod module_map; 15mod module_map;
16mod module_map_db;
15mod imp; 17mod imp;
16mod job; 18mod job;
17mod roots; 19mod roots;
20mod db;
18 21
19use std::{ 22use std::{
20 sync::Arc, 23 sync::Arc,
diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs
index 9acebd6e2..79b88cac2 100644
--- a/crates/libanalysis/src/module_map.rs
+++ b/crates/libanalysis/src/module_map.rs
@@ -244,31 +244,38 @@ impl Link {
244 self.points_to = Vec::new(); 244 self.points_to = Vec::new();
245 return; 245 return;
246 } 246 }
247 let (points_to, problem) = resolve_submodule(self.owner.0, &self.name(), file_resolver);
248 self.problem = problem;
249 self.points_to = points_to.into_iter().map(ModuleId).collect();
250 }
251}
247 252
248 let mod_name = file_resolver.file_stem(self.owner.0); 253pub(crate) fn resolve_submodule(file_id: FileId, name: &SmolStr, file_resolver: &FileResolverImp) -> (Vec<FileId>, Option<Problem>) {
249 let is_dir_owner = 254 let mod_name = file_resolver.file_stem(file_id);
250 mod_name == "mod" || mod_name == "lib" || mod_name == "main"; 255 let is_dir_owner =
256 mod_name == "mod" || mod_name == "lib" || mod_name == "main";
251 257
252 let file_mod = RelativePathBuf::from(format!("../{}.rs", self.name())); 258 let file_mod = RelativePathBuf::from(format!("../{}.rs", name));
253 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", self.name())); 259 let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name));
254 if is_dir_owner { 260 let points_to: Vec<FileId>;
255 self.points_to = [&file_mod, &dir_mod].iter() 261 let problem: Option<Problem>;
256 .filter_map(|path| file_resolver.resolve(self.owner.0, path)) 262 if is_dir_owner {
257 .map(ModuleId) 263 points_to = [&file_mod, &dir_mod].iter()
258 .collect(); 264 .filter_map(|path| file_resolver.resolve(file_id, path))
259 self.problem = if self.points_to.is_empty() { 265 .collect();
260 Some(Problem::UnresolvedModule { 266 problem = if points_to.is_empty() {
261 candidate: file_mod, 267 Some(Problem::UnresolvedModule {
262 })
263 } else {
264 None
265 }
266 } else {
267 self.points_to = Vec::new();
268 self.problem = Some(Problem::NotDirOwner {
269 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
270 candidate: file_mod, 268 candidate: file_mod,
271 }); 269 })
270 } else {
271 None
272 } 272 }
273 } else {
274 points_to = Vec::new();
275 problem = Some(Problem::NotDirOwner {
276 move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)),
277 candidate: file_mod,
278 });
273 } 279 }
280 (points_to, problem)
274} 281}
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 @@
1use std::sync::Arc;
2use {
3 FileId,
4 db::{Query, Eval, QueryCtx, FileSyntax, Files},
5 module_map::resolve_submodule,
6};
7
8enum ModuleDescr {}
9impl Query for ModuleDescr {
10 const ID: u32 = 30;
11 type Params = FileId;
12 type Output = Arc<descr::ModuleDescr>;
13}
14
15enum ResolveSubmodule {}
16impl Query for ResolveSubmodule {
17 const ID: u32 = 31;
18 type Params = (FileId, descr::Submodule);
19 type Output = Arc<Vec<FileId>>;
20}
21
22enum ParentModule {}
23impl Query for ParentModule {
24 const ID: u32 = 40;
25 type Params = FileId;
26 type Output = Arc<Vec<FileId>>;
27}
28
29impl 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
36impl 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
44impl 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
63mod 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)]
96mod 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}
diff --git a/crates/libanalysis/tests/tests.rs b/crates/libanalysis/tests/tests.rs
index 00efe059c..547f85958 100644
--- a/crates/libanalysis/tests/tests.rs
+++ b/crates/libanalysis/tests/tests.rs
@@ -14,24 +14,6 @@ use test_utils::assert_eq_dbg;
14#[derive(Debug)] 14#[derive(Debug)]
15struct FileMap(Vec<(FileId, RelativePathBuf)>); 15struct FileMap(Vec<(FileId, RelativePathBuf)>);
16 16
17fn analysis_host(files: &'static [(&'static str, &'static str)]) -> AnalysisHost {
18 let mut host = AnalysisHost::new();
19 let mut file_map = Vec::new();
20 for (id, &(path, contents)) in files.iter().enumerate() {
21 let file_id = FileId((id + 1) as u32);
22 assert!(path.starts_with('/'));
23 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
24 host.change_file(file_id, Some(contents.to_string()));
25 file_map.push((file_id, path));
26 }
27 host.set_file_resolver(Arc::new(FileMap(file_map)));
28 host
29}
30
31fn analysis(files: &'static [(&'static str, &'static str)]) -> Analysis {
32 analysis_host(files).analysis()
33}
34
35impl FileMap { 17impl FileMap {
36 fn iter<'a>(&'a self) -> impl Iterator<Item=(FileId, &'a RelativePath)> + 'a { 18 fn iter<'a>(&'a self) -> impl Iterator<Item=(FileId, &'a RelativePath)> + 'a {
37 self.0.iter().map(|(id, path)| (*id, path.as_relative_path())) 19 self.0.iter().map(|(id, path)| (*id, path.as_relative_path()))
@@ -56,6 +38,23 @@ impl FileResolver for FileMap {
56 } 38 }
57} 39}
58 40
41fn analysis_host(files: &'static [(&'static str, &'static str)]) -> AnalysisHost {
42 let mut host = AnalysisHost::new();
43 let mut file_map = Vec::new();
44 for (id, &(path, contents)) in files.iter().enumerate() {
45 let file_id = FileId((id + 1) as u32);
46 assert!(path.starts_with('/'));
47 let path = RelativePathBuf::from_path(&path[1..]).unwrap();
48 host.change_file(file_id, Some(contents.to_string()));
49 file_map.push((file_id, path));
50 }
51 host.set_file_resolver(Arc::new(FileMap(file_map)));
52 host
53}
54
55fn analysis(files: &'static [(&'static str, &'static str)]) -> Analysis {
56 analysis_host(files).analysis()
57}
59 58
60#[test] 59#[test]
61fn test_resolve_module() { 60fn test_resolve_module() {
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml
index 9aeea9a9b..fc20730b8 100644
--- a/crates/server/Cargo.toml
+++ b/crates/server/Cargo.toml
@@ -17,7 +17,7 @@ log = "0.4.3"
17url_serde = "0.2.0" 17url_serde = "0.2.0"
18languageserver-types = "0.49.0" 18languageserver-types = "0.49.0"
19walkdir = "2.2.0" 19walkdir = "2.2.0"
20im = { version = "11.0.1", features = ["arc"] } 20im = "12.0.0"
21cargo_metadata = "0.6.0" 21cargo_metadata = "0.6.0"
22text_unit = { version = "0.1.2", features = ["serde"] } 22text_unit = { version = "0.1.2", features = ["serde"] }
23smol_str = { version = "0.1.5", features = ["serde"] } 23smol_str = { version = "0.1.5", features = ["serde"] }