diff options
Diffstat (limited to 'crates/libanalysis')
-rw-r--r-- | crates/libanalysis/src/api.rs | 4 | ||||
-rw-r--r-- | crates/libanalysis/src/imp.rs | 291 | ||||
-rw-r--r-- | crates/libanalysis/src/lib.rs | 288 |
3 files changed, 298 insertions, 285 deletions
diff --git a/crates/libanalysis/src/api.rs b/crates/libanalysis/src/api.rs index 8882268e6..6c06e0b32 100644 --- a/crates/libanalysis/src/api.rs +++ b/crates/libanalysis/src/api.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use relative_path::RelativePathBuf; | 1 | use relative_path::RelativePathBuf; |
2 | use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; | 2 | use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; |
3 | use libeditor; | 3 | use libeditor; |
4 | use {World, FileId, Query}; | 4 | use {imp::AnalysisImpl, FileId, Query}; |
5 | 5 | ||
6 | pub use libeditor::{ | 6 | pub use libeditor::{ |
7 | LocalEdit, StructureNode, LineIndex, FileSymbol, | 7 | LocalEdit, StructureNode, LineIndex, FileSymbol, |
@@ -49,7 +49,7 @@ pub struct Diagnostic { | |||
49 | 49 | ||
50 | #[derive(Clone, Debug)] | 50 | #[derive(Clone, Debug)] |
51 | pub struct Analysis { | 51 | pub struct Analysis { |
52 | pub(crate) imp: World | 52 | pub(crate) imp: AnalysisImpl |
53 | } | 53 | } |
54 | 54 | ||
55 | impl Analysis { | 55 | impl Analysis { |
diff --git a/crates/libanalysis/src/imp.rs b/crates/libanalysis/src/imp.rs new file mode 100644 index 000000000..76f0c0c87 --- /dev/null +++ b/crates/libanalysis/src/imp.rs | |||
@@ -0,0 +1,291 @@ | |||
1 | use std::{ | ||
2 | sync::{ | ||
3 | Arc, | ||
4 | atomic::{AtomicBool, Ordering::SeqCst}, | ||
5 | }, | ||
6 | fmt, | ||
7 | time::Instant, | ||
8 | collections::HashMap, | ||
9 | panic, | ||
10 | }; | ||
11 | |||
12 | use libsyntax2::{ | ||
13 | TextUnit, TextRange, SmolStr, File, AstNode, | ||
14 | SyntaxKind::*, | ||
15 | ast::{self, NameOwner}, | ||
16 | }; | ||
17 | use rayon::prelude::*; | ||
18 | use once_cell::sync::OnceCell; | ||
19 | use libeditor::{self, FileSymbol, LineIndex, find_node_at_offset}; | ||
20 | |||
21 | use { | ||
22 | FileId, FileResolver, Query, Diagnostic, SourceChange, FileSystemEdit, | ||
23 | module_map::Problem, | ||
24 | symbol_index::FileSymbols, | ||
25 | module_map::ModuleMap, | ||
26 | }; | ||
27 | |||
28 | |||
29 | pub(crate) struct AnalysisImpl { | ||
30 | pub(crate) needs_reindex: AtomicBool, | ||
31 | pub(crate) file_resolver: Arc<FileResolver>, | ||
32 | pub(crate) data: Arc<WorldData>, | ||
33 | } | ||
34 | |||
35 | impl fmt::Debug for AnalysisImpl { | ||
36 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
37 | (&*self.data).fmt(f) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | impl Clone for AnalysisImpl { | ||
42 | fn clone(&self) -> AnalysisImpl { | ||
43 | AnalysisImpl { | ||
44 | needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), | ||
45 | file_resolver: Arc::clone(&self.file_resolver), | ||
46 | data: Arc::clone(&self.data), | ||
47 | } | ||
48 | } | ||
49 | } | ||
50 | |||
51 | impl AnalysisImpl { | ||
52 | pub fn file_syntax(&self, file_id: FileId) -> File { | ||
53 | self.file_data(file_id).syntax().clone() | ||
54 | } | ||
55 | |||
56 | pub fn file_line_index(&self, id: FileId) -> LineIndex { | ||
57 | let data = self.file_data(id); | ||
58 | data | ||
59 | .lines | ||
60 | .get_or_init(|| LineIndex::new(&data.text)) | ||
61 | .clone() | ||
62 | } | ||
63 | |||
64 | pub fn world_symbols(&self, mut query: Query) -> Vec<(FileId, FileSymbol)> { | ||
65 | self.reindex(); | ||
66 | self.data.file_map.iter() | ||
67 | .flat_map(move |(id, data)| { | ||
68 | let symbols = data.symbols(); | ||
69 | query.process(symbols).into_iter().map(move |s| (*id, s)) | ||
70 | }) | ||
71 | .collect() | ||
72 | } | ||
73 | |||
74 | pub fn parent_module(&self, id: FileId) -> Vec<(FileId, FileSymbol)> { | ||
75 | let module_map = &self.data.module_map; | ||
76 | let id = module_map.file2module(id); | ||
77 | module_map | ||
78 | .parent_modules( | ||
79 | id, | ||
80 | &*self.file_resolver, | ||
81 | &|file_id| self.file_syntax(file_id), | ||
82 | ) | ||
83 | .into_iter() | ||
84 | .map(|(id, name, node)| { | ||
85 | let id = module_map.module2file(id); | ||
86 | let sym = FileSymbol { | ||
87 | name, | ||
88 | node_range: node.range(), | ||
89 | kind: MODULE, | ||
90 | }; | ||
91 | (id, sym) | ||
92 | }) | ||
93 | .collect() | ||
94 | } | ||
95 | |||
96 | pub fn approximately_resolve_symbol( | ||
97 | &self, | ||
98 | id: FileId, | ||
99 | offset: TextUnit, | ||
100 | ) -> Vec<(FileId, FileSymbol)> { | ||
101 | let file = self.file_syntax(id); | ||
102 | let syntax = file.syntax(); | ||
103 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { | ||
104 | return self.index_resolve(name_ref); | ||
105 | } | ||
106 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { | ||
107 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | ||
108 | if module.has_semi() { | ||
109 | let file_ids = self.resolve_module(id, module); | ||
110 | |||
111 | let res = file_ids.into_iter().map(|id| { | ||
112 | let name = module.name() | ||
113 | .map(|n| n.text()) | ||
114 | .unwrap_or_else(|| SmolStr::new("")); | ||
115 | let symbol = FileSymbol { | ||
116 | name, | ||
117 | node_range: TextRange::offset_len(0.into(), 0.into()), | ||
118 | kind: MODULE, | ||
119 | }; | ||
120 | (id, symbol) | ||
121 | }).collect(); | ||
122 | |||
123 | return res; | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | vec![] | ||
128 | } | ||
129 | |||
130 | pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> { | ||
131 | let syntax = self.file_syntax(file_id); | ||
132 | let mut res = libeditor::diagnostics(&syntax) | ||
133 | .into_iter() | ||
134 | .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None }) | ||
135 | .collect::<Vec<_>>(); | ||
136 | |||
137 | self.data.module_map.problems( | ||
138 | file_id, | ||
139 | &*self.file_resolver, | ||
140 | &|file_id| self.file_syntax(file_id), | ||
141 | |name_node, problem| { | ||
142 | let diag = match problem { | ||
143 | Problem::UnresolvedModule { candidate } => { | ||
144 | let create_file = FileSystemEdit::CreateFile { | ||
145 | anchor: file_id, | ||
146 | path: candidate.clone(), | ||
147 | }; | ||
148 | let fix = SourceChange { | ||
149 | label: "create module".to_string(), | ||
150 | source_file_edits: Vec::new(), | ||
151 | file_system_edits: vec![create_file], | ||
152 | cursor_position: None, | ||
153 | }; | ||
154 | Diagnostic { | ||
155 | range: name_node.syntax().range(), | ||
156 | message: "unresolved module".to_string(), | ||
157 | fix: Some(fix), | ||
158 | } | ||
159 | } | ||
160 | Problem::NotDirOwner { move_to, candidate } => { | ||
161 | let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() }; | ||
162 | let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) }; | ||
163 | let fix = SourceChange { | ||
164 | label: "move file and create module".to_string(), | ||
165 | source_file_edits: Vec::new(), | ||
166 | file_system_edits: vec![move_file, create_file], | ||
167 | cursor_position: None, | ||
168 | }; | ||
169 | Diagnostic { | ||
170 | range: name_node.syntax().range(), | ||
171 | message: "can't declare module at this location".to_string(), | ||
172 | fix: Some(fix), | ||
173 | } | ||
174 | } | ||
175 | }; | ||
176 | res.push(diag) | ||
177 | } | ||
178 | ); | ||
179 | res | ||
180 | } | ||
181 | |||
182 | pub fn assists(&self, file_id: FileId, offset: TextUnit) -> Vec<SourceChange> { | ||
183 | let file = self.file_syntax(file_id); | ||
184 | let actions = vec![ | ||
185 | ("flip comma", libeditor::flip_comma(&file, offset).map(|f| f())), | ||
186 | ("add `#[derive]`", libeditor::add_derive(&file, offset).map(|f| f())), | ||
187 | ("add impl", libeditor::add_impl(&file, offset).map(|f| f())), | ||
188 | ]; | ||
189 | let mut res = Vec::new(); | ||
190 | for (name, local_edit) in actions { | ||
191 | if let Some(local_edit) = local_edit { | ||
192 | res.push(SourceChange::from_local_edit( | ||
193 | file_id, name, local_edit | ||
194 | )) | ||
195 | } | ||
196 | } | ||
197 | res | ||
198 | } | ||
199 | |||
200 | fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> { | ||
201 | let name = name_ref.text(); | ||
202 | let mut query = Query::new(name.to_string()); | ||
203 | query.exact(); | ||
204 | query.limit(4); | ||
205 | self.world_symbols(query) | ||
206 | } | ||
207 | |||
208 | fn resolve_module(&self, id: FileId, module: ast::Module) -> Vec<FileId> { | ||
209 | let name = match module.name() { | ||
210 | Some(name) => name.text(), | ||
211 | None => return Vec::new(), | ||
212 | }; | ||
213 | let module_map = &self.data.module_map; | ||
214 | let id = module_map.file2module(id); | ||
215 | module_map | ||
216 | .child_module_by_name( | ||
217 | id, name.as_str(), | ||
218 | &*self.file_resolver, | ||
219 | &|file_id| self.file_syntax(file_id), | ||
220 | ) | ||
221 | .into_iter() | ||
222 | .map(|id| module_map.module2file(id)) | ||
223 | .collect() | ||
224 | } | ||
225 | |||
226 | fn reindex(&self) { | ||
227 | if self.needs_reindex.compare_and_swap(false, true, SeqCst) { | ||
228 | let now = Instant::now(); | ||
229 | let data = &*self.data; | ||
230 | data.file_map | ||
231 | .par_iter() | ||
232 | .for_each(|(_, data)| drop(data.symbols())); | ||
233 | info!("parallel indexing took {:?}", now.elapsed()); | ||
234 | } | ||
235 | } | ||
236 | |||
237 | fn file_data(&self, file_id: FileId) -> Arc<FileData> { | ||
238 | match self.data.file_map.get(&file_id) { | ||
239 | Some(data) => data.clone(), | ||
240 | None => panic!("unknown file: {:?}", file_id), | ||
241 | } | ||
242 | } | ||
243 | } | ||
244 | |||
245 | #[derive(Default, Debug)] | ||
246 | pub(crate) struct WorldData { | ||
247 | pub(crate) file_map: HashMap<FileId, Arc<FileData>>, | ||
248 | pub(crate) module_map: ModuleMap, | ||
249 | } | ||
250 | |||
251 | #[derive(Debug)] | ||
252 | pub(crate) struct FileData { | ||
253 | pub(crate) text: String, | ||
254 | pub(crate) symbols: OnceCell<FileSymbols>, | ||
255 | pub(crate) syntax: OnceCell<File>, | ||
256 | pub(crate) lines: OnceCell<LineIndex>, | ||
257 | } | ||
258 | |||
259 | impl FileData { | ||
260 | pub(crate) fn new(text: String) -> FileData { | ||
261 | FileData { | ||
262 | text, | ||
263 | symbols: OnceCell::new(), | ||
264 | syntax: OnceCell::new(), | ||
265 | lines: OnceCell::new(), | ||
266 | } | ||
267 | } | ||
268 | |||
269 | fn syntax(&self) -> &File { | ||
270 | let text = &self.text; | ||
271 | let syntax = &self.syntax; | ||
272 | match panic::catch_unwind(panic::AssertUnwindSafe(|| syntax.get_or_init(|| File::parse(text)))) { | ||
273 | Ok(file) => file, | ||
274 | Err(err) => { | ||
275 | error!("Parser paniced on:\n------\n{}\n------\n", &self.text); | ||
276 | panic::resume_unwind(err) | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | fn syntax_transient(&self) -> File { | ||
282 | self.syntax.get().map(|s| s.clone()) | ||
283 | .unwrap_or_else(|| File::parse(&self.text)) | ||
284 | } | ||
285 | |||
286 | fn symbols(&self) -> &FileSymbols { | ||
287 | let syntax = self.syntax_transient(); | ||
288 | self.symbols | ||
289 | .get_or_init(|| FileSymbols::new(&syntax)) | ||
290 | } | ||
291 | } | ||
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 | } | ||