aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-21 16:30:10 +0100
committerAleksey Kladov <[email protected]>2018-08-21 16:30:10 +0100
commitb937262c9b75a361b95a6a27260a71c737e035bf (patch)
tree1838794f32d43a8c45c478b9f24bd74d257edd43 /crates/libanalysis
parent4d8be265849c55912467961e09af657176472dcb (diff)
Module map implementation
Diffstat (limited to 'crates/libanalysis')
-rw-r--r--crates/libanalysis/src/lib.rs124
-rw-r--r--crates/libanalysis/src/module_map.rs117
-rw-r--r--crates/libanalysis/tests/tests.rs21
3 files changed, 222 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;
10extern crate rayon; 10extern crate rayon;
11 11
12mod symbol_index; 12mod symbol_index;
13mod module_map;
13 14
14use once_cell::sync::OnceCell; 15use once_cell::sync::OnceCell;
15use rayon::prelude::*; 16use rayon::prelude::*;
16 17
17use std::{ 18use 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};
33use libeditor::{LineIndex, FileSymbol, find_node}; 35use libeditor::{LineIndex, FileSymbol, find_node};
34 36
35use self::symbol_index::FileSymbols; 37use self::{
38 symbol_index::FileSymbols,
39 module_map::ModuleMap,
40};
36pub use self::symbol_index::Query; 41pub use self::symbol_index::Query;
37 42
38pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 43pub 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)]
44pub struct WorldState { 49pub struct WorldState {
50 updates: Vec<FileId>,
45 data: Arc<WorldData> 51 data: Arc<WorldData>
46} 52}
47 53
48#[derive(Clone)]
49pub struct World { 54pub 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
66impl 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)]
61pub struct FileId(pub u32); 77pub struct FileId(pub u32);
62 78
63impl WorldState { 79impl 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)]
220struct WorldData { 264struct 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 @@
1use std::{
2 path::{PathBuf},
3};
4
5use libsyntax2::{
6 ast::{self, AstNode, NameOwner},
7 SyntaxNode, ParsedFile, SmolStr,
8};
9use {FileId, FileResolver};
10
11#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
12pub struct ModuleId(FileId);
13
14#[derive(Clone, Debug, Default)]
15pub struct ModuleMap {
16 links: Vec<Link>,
17}
18
19#[derive(Clone, Debug)]
20struct Link {
21 owner: ModuleId,
22 syntax: SyntaxNode,
23 points_to: Vec<ModuleId>,
24}
25
26impl 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
82impl 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}
diff --git a/crates/libanalysis/tests/tests.rs b/crates/libanalysis/tests/tests.rs
index 9ef5200af..931ab4183 100644
--- a/crates/libanalysis/tests/tests.rs
+++ b/crates/libanalysis/tests/tests.rs
@@ -43,3 +43,24 @@ fn test_resolve_module() {
43 &symbols, 43 &symbols,
44 ); 44 );
45} 45}
46
47#[test]
48fn test_resolve_parent_module() {
49 let mut world = WorldState::new();
50 world.change_file(FileId(1), Some("mod foo;".to_string()));
51 world.change_file(FileId(2), Some("".to_string()));
52
53 let snap = world.snapshot(|id, path| {
54 assert_eq!(id, FileId(1));
55 if path == PathBuf::from("../foo/mod.rs") {
56 return None;
57 }
58 assert_eq!(path, PathBuf::from("../foo.rs"));
59 Some(FileId(2))
60 });
61 let symbols = snap.parent_module(FileId(2));
62 assert_eq_dbg(
63 r#"[(FileId(1), FileSymbol { name: "foo", node_range: [0; 8), kind: MODULE })]"#,
64 &symbols,
65 );
66}