diff options
Diffstat (limited to 'crates/libanalysis/src/roots.rs')
-rw-r--r-- | crates/libanalysis/src/roots.rs | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/crates/libanalysis/src/roots.rs b/crates/libanalysis/src/roots.rs new file mode 100644 index 000000000..675bce54c --- /dev/null +++ b/crates/libanalysis/src/roots.rs | |||
@@ -0,0 +1,113 @@ | |||
1 | use std::{ | ||
2 | collections::HashMap, | ||
3 | time::Instant, | ||
4 | sync::Arc, | ||
5 | panic, | ||
6 | }; | ||
7 | |||
8 | use once_cell::sync::OnceCell; | ||
9 | use rayon::prelude::*; | ||
10 | use libeditor::LineIndex; | ||
11 | use libsyntax2::File; | ||
12 | |||
13 | use { | ||
14 | FileId, | ||
15 | module_map::{ModuleMap, ChangeKind}, | ||
16 | symbol_index::FileSymbols, | ||
17 | }; | ||
18 | |||
19 | #[derive(Clone, Default, Debug)] | ||
20 | pub(crate) struct SourceRoot { | ||
21 | file_map: HashMap<FileId, Arc<FileData>>, | ||
22 | module_map: ModuleMap, | ||
23 | } | ||
24 | |||
25 | impl SourceRoot { | ||
26 | pub fn update(&mut self, file_id: FileId, text: Option<String>) { | ||
27 | let change_kind = if self.file_map.remove(&file_id).is_some() { | ||
28 | if text.is_some() { | ||
29 | ChangeKind::Update | ||
30 | } else { | ||
31 | ChangeKind::Delete | ||
32 | } | ||
33 | } else { | ||
34 | ChangeKind::Insert | ||
35 | }; | ||
36 | self.module_map.update_file(file_id, change_kind); | ||
37 | self.file_map.remove(&file_id); | ||
38 | if let Some(text) = text { | ||
39 | let file_data = FileData::new(text); | ||
40 | self.file_map.insert(file_id, Arc::new(file_data)); | ||
41 | } else { | ||
42 | self.file_map.remove(&file_id); | ||
43 | } | ||
44 | } | ||
45 | pub fn module_map(&self) -> &ModuleMap { | ||
46 | &self.module_map | ||
47 | } | ||
48 | pub fn lines(&self, file_id: FileId) -> &LineIndex { | ||
49 | let data = self.data(file_id); | ||
50 | data.lines.get_or_init(|| LineIndex::new(&data.text)) | ||
51 | } | ||
52 | pub fn syntax(&self, file_id: FileId) -> &File { | ||
53 | let data = self.data(file_id); | ||
54 | let text = &data.text; | ||
55 | let syntax = &data.syntax; | ||
56 | match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) { | ||
57 | Ok(file) => file, | ||
58 | Err(err) => { | ||
59 | error!("Parser paniced on:\n------\n{}\n------\n", &data.text); | ||
60 | panic::resume_unwind(err) | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | pub(crate) fn symbols(&self) -> Vec<(FileId, &FileSymbols)> { | ||
65 | self.file_map | ||
66 | .iter() | ||
67 | .map(|(&file_id, data)| (file_id, data.symbols())) | ||
68 | .collect() | ||
69 | } | ||
70 | pub fn reindex(&self) { | ||
71 | let now = Instant::now(); | ||
72 | self.file_map | ||
73 | .par_iter() | ||
74 | .for_each(|(_, data)| { | ||
75 | data.symbols(); | ||
76 | }); | ||
77 | info!("parallel indexing took {:?}", now.elapsed()); | ||
78 | |||
79 | } | ||
80 | fn data(&self, file_id: FileId) -> &FileData { | ||
81 | match self.file_map.get(&file_id) { | ||
82 | Some(data) => data, | ||
83 | None => panic!("unknown file: {:?}", file_id), | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | #[derive(Debug)] | ||
89 | struct FileData { | ||
90 | text: String, | ||
91 | lines: OnceCell<LineIndex>, | ||
92 | syntax: OnceCell<File>, | ||
93 | symbols: OnceCell<FileSymbols>, | ||
94 | } | ||
95 | |||
96 | impl FileData { | ||
97 | fn new(text: String) -> FileData { | ||
98 | FileData { | ||
99 | text, | ||
100 | symbols: OnceCell::new(), | ||
101 | syntax: OnceCell::new(), | ||
102 | lines: OnceCell::new(), | ||
103 | } | ||
104 | } | ||
105 | fn syntax_transient(&self) -> File { | ||
106 | self.syntax.get().map(|s| s.clone()) | ||
107 | .unwrap_or_else(|| File::parse(&self.text)) | ||
108 | } | ||
109 | fn symbols(&self) -> &FileSymbols { | ||
110 | let syntax = self.syntax_transient(); | ||
111 | self.symbols.get_or_init(|| FileSymbols::new(&syntax)) | ||
112 | } | ||
113 | } | ||