diff options
author | Aleksey Kladov <[email protected]> | 2018-08-29 16:23:57 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-08-29 16:23:57 +0100 |
commit | 09ea0ca7e5fb5d3e123dc38927b158c798b689ad (patch) | |
tree | 6e75f5a390d6b84e8d195ad2170d8876f118067c /crates/libanalysis/src/lib.rs | |
parent | 0f968ee43047af9d7bcf2953f1b367c38cb8bf1b (diff) |
rename world -> analysis impl
Diffstat (limited to 'crates/libanalysis/src/lib.rs')
-rw-r--r-- | crates/libanalysis/src/lib.rs | 288 |
1 files changed, 5 insertions, 283 deletions
diff --git a/crates/libanalysis/src/lib.rs b/crates/libanalysis/src/lib.rs index 5168814e4..027d7439b 100644 --- a/crates/libanalysis/src/lib.rs +++ b/crates/libanalysis/src/lib.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | #[macro_use] | ||
2 | extern crate failure; | 1 | extern crate failure; |
3 | extern crate parking_lot; | 2 | extern crate parking_lot; |
4 | #[macro_use] | 3 | #[macro_use] |
@@ -13,33 +12,20 @@ extern crate relative_path; | |||
13 | mod symbol_index; | 12 | mod symbol_index; |
14 | mod module_map; | 13 | mod module_map; |
15 | mod api; | 14 | mod api; |
15 | mod imp; | ||
16 | 16 | ||
17 | use std::{ | 17 | use std::{ |
18 | fmt, | ||
19 | panic, | ||
20 | sync::{ | 18 | sync::{ |
21 | Arc, | 19 | Arc, |
22 | atomic::{AtomicBool, Ordering::SeqCst}, | 20 | atomic::{AtomicBool}, |
23 | }, | 21 | }, |
24 | collections::hash_map::HashMap, | ||
25 | time::Instant, | ||
26 | }; | 22 | }; |
27 | 23 | ||
28 | use relative_path::RelativePath; | 24 | use relative_path::RelativePath; |
29 | use once_cell::sync::OnceCell; | ||
30 | use rayon::prelude::*; | ||
31 | |||
32 | use libsyntax2::{ | ||
33 | File, | ||
34 | TextUnit, TextRange, SmolStr, | ||
35 | ast::{self, AstNode, NameOwner}, | ||
36 | SyntaxKind::*, | ||
37 | }; | ||
38 | use libeditor::{LineIndex, FileSymbol, find_node_at_offset}; | ||
39 | 25 | ||
40 | use self::{ | 26 | use self::{ |
41 | symbol_index::FileSymbols, | 27 | module_map::{ChangeKind}, |
42 | module_map::{ModuleMap, ChangeKind, Problem}, | 28 | imp::{WorldData, FileData}, |
43 | }; | 29 | }; |
44 | pub use self::symbol_index::Query; | 30 | pub use self::symbol_index::Query; |
45 | pub use self::api::{ | 31 | pub use self::api::{ |
@@ -58,28 +44,6 @@ pub struct WorldState { | |||
58 | data: Arc<WorldData> | 44 | data: Arc<WorldData> |
59 | } | 45 | } |
60 | 46 | ||
61 | pub(crate) struct World { | ||
62 | needs_reindex: AtomicBool, | ||
63 | file_resolver: Arc<FileResolver>, | ||
64 | data: Arc<WorldData>, | ||
65 | } | ||
66 | |||
67 | impl fmt::Debug for World { | ||
68 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
69 | (&*self.data).fmt(f) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl Clone for World { | ||
74 | fn clone(&self) -> World { | ||
75 | World { | ||
76 | needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), | ||
77 | file_resolver: Arc::clone(&self.file_resolver), | ||
78 | data: Arc::clone(&self.data), | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | |||
83 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 47 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
84 | pub struct FileId(pub u32); | 48 | pub struct FileId(pub u32); |
85 | 49 | ||
@@ -94,7 +58,7 @@ impl WorldState { | |||
94 | &self, | 58 | &self, |
95 | file_resolver: impl FileResolver, | 59 | file_resolver: impl FileResolver, |
96 | ) -> Analysis { | 60 | ) -> Analysis { |
97 | let imp = World { | 61 | let imp = imp::AnalysisImpl { |
98 | needs_reindex: AtomicBool::new(false), | 62 | needs_reindex: AtomicBool::new(false), |
99 | file_resolver: Arc::new(file_resolver), | 63 | file_resolver: Arc::new(file_resolver), |
100 | data: self.data.clone() | 64 | data: self.data.clone() |
@@ -139,245 +103,3 @@ impl WorldState { | |||
139 | Arc::get_mut(&mut self.data).unwrap() | 103 | Arc::get_mut(&mut self.data).unwrap() |
140 | } | 104 | } |
141 | } | 105 | } |
142 | |||
143 | impl World { | ||
144 | pub fn file_syntax(&self, file_id: FileId) -> File { | ||
145 | self.file_data(file_id).syntax().clone() | ||
146 | } | ||
147 | |||
148 | pub fn file_line_index(&self, id: FileId) -> LineIndex { | ||
149 | let data = self.file_data(id); | ||
150 | data | ||
151 | .lines | ||
152 | .get_or_init(|| LineIndex::new(&data.text)) | ||
153 | .clone() | ||
154 | } | ||
155 | |||
156 | pub fn world_symbols(&self, mut query: Query) -> Vec<(FileId, FileSymbol)> { | ||
157 | self.reindex(); | ||
158 | self.data.file_map.iter() | ||
159 | .flat_map(move |(id, data)| { | ||
160 | let symbols = data.symbols(); | ||
161 | query.process(symbols).into_iter().map(move |s| (*id, s)) | ||
162 | }) | ||
163 | .collect() | ||
164 | } | ||
165 | |||
166 | pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> { | ||
167 | let module_map = &self.data.module_map; | ||
168 | let id = module_map.file2module(id); | ||
169 | module_map | ||
170 | .parent_modules( | ||
171 | id, | ||
172 | &*self.file_resolver, | ||
173 | &|file_id| self.file_syntax(file_id), | ||
174 | ) | ||
175 | .into_iter() | ||
176 | .map(|(id, name, node)| { | ||
177 | let id = module_map.module2file(id); | ||
178 | let sym = FileSymbol { | ||
179 | name, | ||
180 | node_range: node.range(), | ||
181 | kind: MODULE, | ||
182 | }; | ||
183 | (id, sym) | ||
184 | }) | ||
185 | .collect() | ||
186 | } | ||
187 | |||
188 | pub fn approximately_resolve_symbol( | ||
189 | &self, | ||
190 | id: FileId, | ||
191 | offset: TextUnit, | ||
192 | ) -> Vec<(FileId, FileSymbol)> { | ||
193 | let file = self.file_syntax(id); | ||
194 | let syntax = file.syntax(); | ||
195 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { | ||
196 | return self.index_resolve(name_ref); | ||
197 | } | ||
198 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { | ||
199 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | ||
200 | if module.has_semi() { | ||
201 | let file_ids = self.resolve_module(id, module); | ||
202 | |||
203 | let res = file_ids.into_iter().map(|id| { | ||
204 | let name = module.name() | ||
205 | .map(|n| n.text()) | ||
206 | .unwrap_or_else(|| SmolStr::new("")); | ||
207 | let symbol = FileSymbol { | ||
208 | name, | ||
209 | node_range: TextRange::offset_len(0.into(), 0.into()), | ||
210 | kind: MODULE, | ||
211 | }; | ||
212 | (id, symbol) | ||
213 | }).collect(); | ||
214 | |||
215 | return res; | ||
216 | } | ||
217 | } | ||
218 | } | ||
219 | vec![] | ||
220 | } | ||
221 | |||
222 | pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> { | ||
223 | let syntax = self.file_syntax(file_id); | ||
224 | let mut res = libeditor::diagnostics(&syntax) | ||
225 | .into_iter() | ||
226 | .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None }) | ||
227 | .collect::<Vec<_>>(); | ||
228 | |||
229 | self.data.module_map.problems( | ||
230 | file_id, | ||
231 | &*self.file_resolver, | ||
232 | &|file_id| self.file_syntax(file_id), | ||
233 | |name_node, problem| { | ||
234 | let diag = match problem { | ||
235 | Problem::UnresolvedModule { candidate } => { | ||
236 | let create_file = FileSystemEdit::CreateFile { | ||
237 | anchor: file_id, | ||
238 | path: candidate.clone(), | ||
239 | }; | ||
240 | let fix = SourceChange { | ||
241 | label: "create module".to_string(), | ||
242 | source_file_edits: Vec::new(), | ||
243 | file_system_edits: vec![create_file], | ||
244 | cursor_position: None, | ||
245 | }; | ||
246 | Diagnostic { | ||
247 | range: name_node.syntax().range(), | ||
248 | message: "unresolved module".to_string(), | ||
249 | fix: Some(fix), | ||
250 | } | ||
251 | } | ||
252 | Problem::NotDirOwner { move_to, candidate } => { | ||
253 | let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() }; | ||
254 | let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) }; | ||
255 | let fix = SourceChange { | ||
256 | label: "move file and create module".to_string(), | ||
257 | source_file_edits: Vec::new(), | ||
258 | file_system_edits: vec![move_file, create_file], | ||
259 | cursor_position: None, | ||
260 | }; | ||
261 | Diagnostic { | ||
262 | range: name_node.syntax().range(), | ||
263 | message: "can't declare module at this location".to_string(), | ||
264 | fix: Some(fix), | ||
265 | } | ||
266 | } | ||
267 | }; | ||
268 | res.push(diag) | ||
269 | } | ||
270 | ); | ||
271 | res | ||
272 | } | ||
273 | |||
274 | pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> { | ||
275 | let file = self.file_syntax(file_id); | ||
276 | let actions = vec![ | ||
277 | ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())), | ||
278 | ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), | ||
279 | ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), | ||
280 | ]; | ||
281 | let mut res = Vec::new(); | ||
282 | for (name, local_edit) in actions { | ||
283 | if let Some(local_edit) = local_edit { | ||
284 | res.push(SourceChange::from_local_edit( | ||
285 | file_id, name, local_edit | ||
286 | )) | ||
287 | } | ||
288 | } | ||
289 | res | ||
290 | } | ||
291 | |||
292 | fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { | ||
293 | let name = name_ref.text(); | ||
294 | let mut query = Query::new(name.to_string()); | ||
295 | query.exact(); | ||
296 | query.limit(4); | ||
297 | self.world_symbols(query) | ||
298 | } | ||
299 | |||
300 | fn resolve_module(&self, id: FileId, module: ast::Module) -> Vec<FileId> { | ||
301 | let name = match module.name() { | ||
302 | Some(name) => name.text(), | ||
303 | None => return Vec::new(), | ||
304 | }; | ||
305 | let module_map = &self.data.module_map; | ||
306 | let id = module_map.file2module(id); | ||
307 | module_map | ||
308 | .child_module_by_name( | ||
309 | id, name.as_str(), | ||
310 | &*self.file_resolver, | ||
311 | &|file_id| self.file_syntax(file_id), | ||
312 | ) | ||
313 | .into_iter() | ||
314 | .map(|id| module_map.module2file(id)) | ||
315 | .collect() | ||
316 | } | ||
317 | |||
318 | fn reindex(&self) { | ||
319 | if self.needs_reindex.compare_and_swap(false, true, SeqCst) { | ||
320 | let now = Instant::now(); | ||
321 | let data = &*self.data; | ||
322 | data.file_map | ||
323 | .par_iter() | ||
324 | .for_each(|(_, data)| drop(data.symbols())); | ||
325 | info!("parallel indexing took {:?}", now.elapsed()); | ||
326 | } | ||
327 | } | ||
328 | |||
329 | fn file_data(&self, file_id: FileId) -> Arc<FileData> { | ||
330 | match self.data.file_map.get(&file_id) { | ||
331 | Some(data) => data.clone(), | ||
332 | None => panic!("unknown file: {:?}", file_id), | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | |||
337 | #[derive(Default, Debug)] | ||
338 | struct WorldData { | ||
339 | file_map: HashMap<FileId, Arc<FileData>>, | ||
340 | module_map: ModuleMap, | ||
341 | } | ||
342 | |||
343 | #[derive(Debug)] | ||
344 | struct FileData { | ||
345 | text: String, | ||
346 | symbols: OnceCell<FileSymbols>, | ||
347 | syntax: OnceCell<File>, | ||
348 | lines: OnceCell<LineIndex>, | ||
349 | } | ||
350 | |||
351 | impl FileData { | ||
352 | fn new(text: String) -> FileData { | ||
353 | FileData { | ||
354 | text, | ||
355 | symbols: OnceCell::new(), | ||
356 | syntax: OnceCell::new(), | ||
357 | lines: OnceCell::new(), | ||
358 | } | ||
359 | } | ||
360 | |||
361 | fn syntax(&self) -> &File { | ||
362 | let text = &self.text; | ||
363 | let syntax = &self.syntax; | ||
364 | match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) { | ||
365 | Ok(file) => file, | ||
366 | Err(err) => { | ||
367 | error!("Parser paniced on:\n------\n{}\n------\n", &self.text); | ||
368 | panic::resume_unwind(err) | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | fn syntax_transient(&self) -> File { | ||
374 | self.syntax.get().map(|s| s.clone()) | ||
375 | .unwrap_or_else(|| File::parse(&self.text)) | ||
376 | } | ||
377 | |||
378 | fn symbols(&self) -> &FileSymbols { | ||
379 | let syntax = self.syntax_transient(); | ||
380 | self.symbols | ||
381 | .get_or_init(|| FileSymbols::new(&syntax)) | ||
382 | } | ||
383 | } | ||