diff options
author | Aleksey Kladov <[email protected]> | 2018-08-21 16:30:10 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-21 16:30:10 +0100 |
commit | b937262c9b75a361b95a6a27260a71c737e035bf (patch) | |
tree | 1838794f32d43a8c45c478b9f24bd74d257edd43 /crates/libanalysis/src | |
parent | 4d8be265849c55912467961e09af657176472dcb (diff) |
Module map implementation
Diffstat (limited to 'crates/libanalysis/src')
-rw-r--r-- | crates/libanalysis/src/lib.rs | 124 | ||||
-rw-r--r-- | crates/libanalysis/src/module_map.rs | 117 |
2 files changed, 201 insertions, 40 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 63c4c77cf..d01144627 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs | |||
@@ -10,16 +10,18 @@ extern crate fst; | |||
10 | extern crate rayon; | 10 | extern crate rayon; |
11 | 11 | ||
12 | mod symbol_index; | 12 | mod symbol_index; |
13 | mod module_map; | ||
13 | 14 | ||
14 | use once_cell::sync::OnceCell; | 15 | use once_cell::sync::OnceCell; |
15 | use rayon::prelude::*; | 16 | use rayon::prelude::*; |
16 | 17 | ||
17 | use std::{ | 18 | use std::{ |
18 | fmt, | 19 | fmt, |
19 | path::{Path, PathBuf}, | 20 | mem, |
21 | path::{Path}, | ||
20 | sync::{ | 22 | sync::{ |
21 | Arc, | 23 | Arc, |
22 | atomic::{AtomicUsize, Ordering::SeqCst}, | 24 | atomic::{AtomicBool, Ordering::SeqCst}, |
23 | }, | 25 | }, |
24 | collections::hash_map::HashMap, | 26 | collections::hash_map::HashMap, |
25 | time::Instant, | 27 | time::Instant, |
@@ -32,7 +34,10 @@ use libsyntax2::{ | |||
32 | }; | 34 | }; |
33 | use libeditor::{LineIndex, FileSymbol, find_node}; | 35 | use libeditor::{LineIndex, FileSymbol, find_node}; |
34 | 36 | ||
35 | use self::symbol_index::FileSymbols; | 37 | use self::{ |
38 | symbol_index::FileSymbols, | ||
39 | module_map::ModuleMap, | ||
40 | }; | ||
36 | pub use self::symbol_index::Query; | 41 | pub use self::symbol_index::Query; |
37 | 42 | ||
38 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 43 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
@@ -42,11 +47,12 @@ pub type FileResolver = dyn Fn(FileId, &Path) -> Option<FileId> + Send + Sync; | |||
42 | 47 | ||
43 | #[derive(Debug)] | 48 | #[derive(Debug)] |
44 | pub struct WorldState { | 49 | pub struct WorldState { |
50 | updates: Vec<FileId>, | ||
45 | data: Arc<WorldData> | 51 | data: Arc<WorldData> |
46 | } | 52 | } |
47 | 53 | ||
48 | #[derive(Clone)] | ||
49 | pub struct World { | 54 | pub struct World { |
55 | needs_reindex: AtomicBool, | ||
50 | file_resolver: Arc<FileResolver>, | 56 | file_resolver: Arc<FileResolver>, |
51 | data: Arc<WorldData>, | 57 | data: Arc<WorldData>, |
52 | } | 58 | } |
@@ -57,18 +63,48 @@ impl fmt::Debug for World { | |||
57 | } | 63 | } |
58 | } | 64 | } |
59 | 65 | ||
66 | impl Clone for World { | ||
67 | fn clone(&self) -> World { | ||
68 | World { | ||
69 | needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), | ||
70 | file_resolver: Arc::clone(&self.file_resolver), | ||
71 | data: Arc::clone(&self.data), | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
60 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 76 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
61 | pub struct FileId(pub u32); | 77 | pub struct FileId(pub u32); |
62 | 78 | ||
63 | impl WorldState { | 79 | impl WorldState { |
64 | pub fn new() -> WorldState { | 80 | pub fn new() -> WorldState { |
65 | WorldState { | 81 | WorldState { |
66 | data: Arc::new(WorldData::default()) | 82 | updates: Vec::new(), |
83 | data: Arc::new(WorldData::default()), | ||
67 | } | 84 | } |
68 | } | 85 | } |
69 | 86 | ||
70 | pub fn snapshot(&self, file_resolver: impl Fn(FileId, &Path) -> Option<FileId> + 'static + Send + Sync) -> World { | 87 | pub fn snapshot( |
88 | &mut self, | ||
89 | file_resolver: impl Fn(FileId, &Path) -> Option<FileId> + 'static + Send + Sync, | ||
90 | ) -> World { | ||
91 | let needs_reindex = self.updates.len() >= INDEXING_THRESHOLD; | ||
92 | if !self.updates.is_empty() { | ||
93 | let updates = mem::replace(&mut self.updates, Vec::new()); | ||
94 | let data = self.data_mut(); | ||
95 | for file_id in updates { | ||
96 | let syntax = data.file_map | ||
97 | .get(&file_id) | ||
98 | .map(|it| it.syntax()); | ||
99 | data.module_map.update_file( | ||
100 | file_id, | ||
101 | syntax, | ||
102 | &file_resolver, | ||
103 | ); | ||
104 | } | ||
105 | } | ||
71 | World { | 106 | World { |
107 | needs_reindex: AtomicBool::new(needs_reindex), | ||
72 | file_resolver: Arc::new(file_resolver), | 108 | file_resolver: Arc::new(file_resolver), |
73 | data: self.data.clone() | 109 | data: self.data.clone() |
74 | } | 110 | } |
@@ -79,28 +115,28 @@ impl WorldState { | |||
79 | } | 115 | } |
80 | 116 | ||
81 | pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) { | 117 | pub fn change_files(&mut self, changes: impl Iterator<Item=(FileId, Option<String>)>) { |
82 | let data = self.data_mut(); | 118 | let mut updates = Vec::new(); |
83 | let mut cnt = 0; | 119 | { |
84 | for (id, text) in changes { | 120 | let data = self.data_mut(); |
85 | cnt += 1; | 121 | for (file_id, text) in changes { |
86 | data.file_map.remove(&id); | 122 | data.file_map.remove(&file_id); |
87 | if let Some(text) = text { | 123 | if let Some(text) = text { |
88 | let file_data = FileData::new(text); | 124 | let file_data = FileData::new(text); |
89 | data.file_map.insert(id, Arc::new(file_data)); | 125 | data.file_map.insert(file_id, Arc::new(file_data)); |
90 | } else { | 126 | } else { |
91 | data.file_map.remove(&id); | 127 | data.file_map.remove(&file_id); |
128 | } | ||
129 | updates.push(file_id); | ||
92 | } | 130 | } |
93 | } | 131 | } |
94 | *data.unindexed.get_mut() += cnt; | 132 | self.updates.extend(updates) |
95 | } | 133 | } |
96 | 134 | ||
97 | fn data_mut(&mut self) -> &mut WorldData { | 135 | fn data_mut(&mut self) -> &mut WorldData { |
98 | if Arc::get_mut(&mut self.data).is_none() { | 136 | if Arc::get_mut(&mut self.data).is_none() { |
99 | self.data = Arc::new(WorldData { | 137 | self.data = Arc::new(WorldData { |
100 | unindexed: AtomicUsize::new( | ||
101 | self.data.unindexed.load(SeqCst) | ||
102 | ), | ||
103 | file_map: self.data.file_map.clone(), | 138 | file_map: self.data.file_map.clone(), |
139 | module_map: self.data.module_map.clone(), | ||
104 | }); | 140 | }); |
105 | } | 141 | } |
106 | Arc::get_mut(&mut self.data).unwrap() | 142 | Arc::get_mut(&mut self.data).unwrap() |
@@ -131,6 +167,24 @@ impl World { | |||
131 | .collect() | 167 | .collect() |
132 | } | 168 | } |
133 | 169 | ||
170 | pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> { | ||
171 | let module_map = &self.data.module_map; | ||
172 | let id = module_map.file2module(id); | ||
173 | module_map | ||
174 | .parent_modules(id) | ||
175 | .into_iter() | ||
176 | .map(|(id, m)| { | ||
177 | let id = module_map.module2file(id); | ||
178 | let sym = FileSymbol { | ||
179 | name: m.name().unwrap().text(), | ||
180 | node_range: m.syntax().range(), | ||
181 | kind: MODULE, | ||
182 | }; | ||
183 | (id, sym) | ||
184 | }) | ||
185 | .collect() | ||
186 | } | ||
187 | |||
134 | pub fn approximately_resolve_symbol( | 188 | pub fn approximately_resolve_symbol( |
135 | &self, | 189 | &self, |
136 | id: FileId, | 190 | id: FileId, |
@@ -178,32 +232,22 @@ impl World { | |||
178 | Some(name) => name.text(), | 232 | Some(name) => name.text(), |
179 | None => return Vec::new(), | 233 | None => return Vec::new(), |
180 | }; | 234 | }; |
181 | let paths = &[ | 235 | let module_map = &self.data.module_map; |
182 | PathBuf::from(format!("../{}.rs", name)), | 236 | let id = module_map.file2module(id); |
183 | PathBuf::from(format!("../{}/mod.rs", name)), | 237 | module_map |
184 | ]; | 238 | .child_module_by_name(id, name.as_str()) |
185 | paths.iter() | 239 | .into_iter() |
186 | .filter_map(|path| self.resolve_relative_path(id, path)) | 240 | .map(|id| module_map.module2file(id)) |
187 | .collect() | 241 | .collect() |
188 | } | 242 | } |
189 | 243 | ||
190 | fn resolve_relative_path(&self, id: FileId, path: &Path) -> Option<FileId> { | ||
191 | (self.file_resolver)(id, path) | ||
192 | } | ||
193 | |||
194 | fn reindex(&self) { | 244 | fn reindex(&self) { |
195 | let data = &*self.data; | 245 | if self.needs_reindex.compare_and_swap(false, true, SeqCst) { |
196 | let unindexed = data.unindexed.load(SeqCst); | ||
197 | if unindexed < INDEXING_THRESHOLD { | ||
198 | return; | ||
199 | } | ||
200 | if unindexed == data.unindexed.compare_and_swap(unindexed, 0, SeqCst) { | ||
201 | let now = Instant::now(); | 246 | let now = Instant::now(); |
247 | let data = &*self.data; | ||
202 | data.file_map | 248 | data.file_map |
203 | .par_iter() | 249 | .par_iter() |
204 | .for_each(|(_, data)| { | 250 | .for_each(|(_, data)| drop(data.symbols())); |
205 | data.symbols(); | ||
206 | }); | ||
207 | info!("parallel indexing took {:?}", now.elapsed()); | 251 | info!("parallel indexing took {:?}", now.elapsed()); |
208 | } | 252 | } |
209 | } | 253 | } |
@@ -218,8 +262,8 @@ impl World { | |||
218 | 262 | ||
219 | #[derive(Default, Debug)] | 263 | #[derive(Default, Debug)] |
220 | struct WorldData { | 264 | struct WorldData { |
221 | unindexed: AtomicUsize, | ||
222 | file_map: HashMap<FileId, Arc<FileData>>, | 265 | file_map: HashMap<FileId, Arc<FileData>>, |
266 | module_map: ModuleMap, | ||
223 | } | 267 | } |
224 | 268 | ||
225 | #[derive(Debug)] | 269 | #[derive(Debug)] |
diff --git a/crates/libanalysis/src/module_map.rs b/crates/libanalysis/src/module_map.rs new file mode 100644 index 000000000..9b4c778b6 --- /dev/null +++ b/crates/libanalysis/src/module_map.rs | |||
@@ -0,0 +1,117 @@ | |||
1 | use std::{ | ||
2 | path::{PathBuf}, | ||
3 | }; | ||
4 | |||
5 | use libsyntax2::{ | ||
6 | ast::{self, AstNode, NameOwner}, | ||
7 | SyntaxNode, ParsedFile, SmolStr, | ||
8 | }; | ||
9 | use {FileId, FileResolver}; | ||
10 | |||
11 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] | ||
12 | pub struct ModuleId(FileId); | ||
13 | |||
14 | #[derive(Clone, Debug, Default)] | ||
15 | pub struct ModuleMap { | ||
16 | links: Vec<Link>, | ||
17 | } | ||
18 | |||
19 | #[derive(Clone, Debug)] | ||
20 | struct Link { | ||
21 | owner: ModuleId, | ||
22 | syntax: SyntaxNode, | ||
23 | points_to: Vec<ModuleId>, | ||
24 | } | ||
25 | |||
26 | impl ModuleMap { | ||
27 | pub fn update_file( | ||
28 | &mut self, | ||
29 | file_id: FileId, | ||
30 | syntax: Option<&ParsedFile>, | ||
31 | file_resolver: &FileResolver, | ||
32 | ) { | ||
33 | let mod_id = ModuleId(file_id); | ||
34 | self.links.retain(|link| link.owner != mod_id); | ||
35 | match syntax { | ||
36 | None => { | ||
37 | for link in self.links.iter_mut() { | ||
38 | link.points_to.retain(|&x| x != mod_id); | ||
39 | } | ||
40 | } | ||
41 | Some(syntax) => { | ||
42 | self.links.extend( | ||
43 | syntax.ast().modules().filter_map(|it| { | ||
44 | Link::new(mod_id, it) | ||
45 | }) | ||
46 | ) | ||
47 | } | ||
48 | } | ||
49 | self.links.iter_mut().for_each(|link| { | ||
50 | link.resolve(file_resolver) | ||
51 | }) | ||
52 | } | ||
53 | |||
54 | pub fn module2file(&self, m: ModuleId) -> FileId { | ||
55 | m.0 | ||
56 | } | ||
57 | |||
58 | pub fn file2module(&self, file_id: FileId) -> ModuleId { | ||
59 | ModuleId(file_id) | ||
60 | } | ||
61 | |||
62 | pub fn child_module_by_name(&self, parent_mod: ModuleId, child_mod: &str) -> Vec<ModuleId> { | ||
63 | self.links | ||
64 | .iter() | ||
65 | .filter(|link| link.owner == parent_mod) | ||
66 | .filter(|link| link.name() == child_mod) | ||
67 | .filter_map(|it| it.points_to.first()) | ||
68 | .map(|&it| it) | ||
69 | .collect() | ||
70 | } | ||
71 | |||
72 | pub fn parent_modules<'a>(&'a self, m: ModuleId) -> impl Iterator<Item=(ModuleId, ast::Module<'a>)> + 'a { | ||
73 | self.links | ||
74 | .iter() | ||
75 | .filter(move |link| link.points_to.iter().any(|&it| it == m)) | ||
76 | .map(|link| { | ||
77 | (link.owner, link.ast()) | ||
78 | }) | ||
79 | } | ||
80 | } | ||
81 | |||
82 | impl Link { | ||
83 | fn new(owner: ModuleId, module: ast::Module) -> Option<Link> { | ||
84 | if module.name().is_none() { | ||
85 | return None; | ||
86 | } | ||
87 | let link = Link { | ||
88 | owner, | ||
89 | syntax: module.syntax().owned(), | ||
90 | points_to: Vec::new(), | ||
91 | }; | ||
92 | Some(link) | ||
93 | } | ||
94 | |||
95 | fn name(&self) -> SmolStr { | ||
96 | self.ast().name() | ||
97 | .unwrap() | ||
98 | .text() | ||
99 | } | ||
100 | |||
101 | fn ast(&self) -> ast::Module { | ||
102 | ast::Module::cast(self.syntax.borrowed()) | ||
103 | .unwrap() | ||
104 | } | ||
105 | |||
106 | fn resolve(&mut self, file_resolver: &FileResolver) { | ||
107 | let name = self.name(); | ||
108 | let paths = &[ | ||
109 | PathBuf::from(format!("../{}.rs", name)), | ||
110 | PathBuf::from(format!("../{}/mod.rs", name)), | ||
111 | ]; | ||
112 | self.points_to = paths.iter() | ||
113 | .filter_map(|path| file_resolver(self.owner.0, path)) | ||
114 | .map(ModuleId) | ||
115 | .collect(); | ||
116 | } | ||
117 | } | ||