aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/libanalysis/src/lib.rs')
-rw-r--r--crates/libanalysis/src/lib.rs288
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]
2extern crate failure; 1extern crate failure;
3extern crate parking_lot; 2extern crate parking_lot;
4#[macro_use] 3#[macro_use]
@@ -13,33 +12,20 @@ extern crate relative_path;
13mod symbol_index; 12mod symbol_index;
14mod module_map; 13mod module_map;
15mod api; 14mod api;
15mod imp;
16 16
17use std::{ 17use 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
28use relative_path::RelativePath; 24use relative_path::RelativePath;
29use once_cell::sync::OnceCell;
30use rayon::prelude::*;
31
32use libsyntax2::{
33 File,
34 TextUnit, TextRange, SmolStr,
35 ast::{self, AstNode, NameOwner},
36 SyntaxKind::*,
37};
38use libeditor::{LineIndex, FileSymbol, find_node_at_offset};
39 25
40use self::{ 26use self::{
41 symbol_index::FileSymbols, 27 module_map::{ChangeKind},
42 module_map::{ModuleMap, ChangeKind, Problem}, 28 imp::{WorldData, FileData},
43}; 29};
44pub use self::symbol_index::Query; 30pub use self::symbol_index::Query;
45pub use self::api::{ 31pub use self::api::{
@@ -58,28 +44,6 @@ pub struct WorldState {
58 data: Arc<WorldData> 44 data: Arc<WorldData>
59} 45}
60 46
61pub(crate) struct World {
62 needs_reindex: AtomicBool,
63 file_resolver: Arc<FileResolver>,
64 data: Arc<WorldData>,
65}
66
67impl fmt::Debug for World {
68 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69 (&*self.data).fmt(f)
70 }
71}
72
73impl 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)]
84pub struct FileId(pub u32); 48pub 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
143impl 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)]
338struct WorldData {
339 file_map: HashMap<FileId, Arc<FileData>>,
340 module_map: ModuleMap,
341}
342
343#[derive(Debug)]
344struct FileData {
345 text: String,
346 symbols: OnceCell<FileSymbols>,
347 syntax: OnceCell<File>,
348 lines: OnceCell<LineIndex>,
349}
350
351impl 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}