aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/libanalysis/src/api.rs4
-rw-r--r--crates/libanalysis/src/imp.rs291
-rw-r--r--crates/libanalysis/src/lib.rs288
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 @@
1use relative_path::RelativePathBuf; 1use relative_path::RelativePathBuf;
2use libsyntax2::{File, TextRange, TextUnit, AtomEdit}; 2use libsyntax2::{File, TextRange, TextUnit, AtomEdit};
3use libeditor; 3use libeditor;
4use {World, FileId, Query}; 4use {imp::AnalysisImpl, FileId, Query};
5 5
6pub use libeditor::{ 6pub 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)]
51pub struct Analysis { 51pub struct Analysis {
52 pub(crate) imp: World 52 pub(crate) imp: AnalysisImpl
53} 53}
54 54
55impl Analysis { 55impl 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 @@
1use std::{
2 sync::{
3 Arc,
4 atomic::{AtomicBool, Ordering::SeqCst},
5 },
6 fmt,
7 time::Instant,
8 collections::HashMap,
9 panic,
10};
11
12use libsyntax2::{
13 TextUnit, TextRange, SmolStr, File, AstNode,
14 SyntaxKind::*,
15 ast::{self, NameOwner},
16};
17use rayon::prelude::*;
18use once_cell::sync::OnceCell;
19use libeditor::{self, FileSymbol, LineIndex, find_node_at_offset};
20
21use {
22 FileId, FileResolver, Query, Diagnostic, SourceChange, FileSystemEdit,
23 module_map::Problem,
24 symbol_index::FileSymbols,
25 module_map::ModuleMap,
26};
27
28
29pub(crate) struct AnalysisImpl {
30 pub(crate) needs_reindex: AtomicBool,
31 pub(crate) file_resolver: Arc<FileResolver>,
32 pub(crate) data: Arc<WorldData>,
33}
34
35impl fmt::Debug for AnalysisImpl {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 (&*self.data).fmt(f)
38 }
39}
40
41impl 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
51impl 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)]
246pub(crate) struct WorldData {
247 pub(crate) file_map: HashMap<FileId, Arc<FileData>>,
248 pub(crate) module_map: ModuleMap,
249}
250
251#[derive(Debug)]
252pub(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
259impl 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]
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}