aboutsummaryrefslogtreecommitdiff
path: root/crates/libanalysis/src/imp.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2018-08-29 16:23:57 +0100
committerAleksey Kladov <[email protected]>2018-08-29 16:23:57 +0100
commit09ea0ca7e5fb5d3e123dc38927b158c798b689ad (patch)
tree6e75f5a390d6b84e8d195ad2170d8876f118067c /crates/libanalysis/src/imp.rs
parent0f968ee43047af9d7bcf2953f1b367c38cb8bf1b (diff)
rename world -> analysis impl
Diffstat (limited to 'crates/libanalysis/src/imp.rs')
-rw-r--r--crates/libanalysis/src/imp.rs291
1 files changed, 291 insertions, 0 deletions
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}