diff options
author | Aleksey Kladov <[email protected]> | 2018-09-16 10:54:24 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-09-16 11:07:39 +0100 |
commit | b5021411a84822cb3f1e3aeffad9550dd15bdeb6 (patch) | |
tree | 9dca564f8e51b298dced01c4ce669c756dce3142 /crates/ra_analysis/src/imp.rs | |
parent | ba0bfeee12e19da40b5eabc8d0408639af10e96f (diff) |
rename all things
Diffstat (limited to 'crates/ra_analysis/src/imp.rs')
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs new file mode 100644 index 000000000..90184a4b9 --- /dev/null +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -0,0 +1,342 @@ | |||
1 | use std::{ | ||
2 | sync::{ | ||
3 | Arc, | ||
4 | atomic::{AtomicBool, Ordering::SeqCst}, | ||
5 | }, | ||
6 | fmt, | ||
7 | collections::{HashSet, VecDeque}, | ||
8 | iter, | ||
9 | }; | ||
10 | |||
11 | use relative_path::RelativePath; | ||
12 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit}; | ||
13 | use ra_syntax::{ | ||
14 | TextUnit, TextRange, SmolStr, File, AstNode, | ||
15 | SyntaxKind::*, | ||
16 | ast::{self, NameOwner}, | ||
17 | }; | ||
18 | |||
19 | use { | ||
20 | FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, | ||
21 | JobToken, CrateGraph, CrateId, | ||
22 | roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, | ||
23 | descriptors::{ModuleTreeDescriptor, Problem}, | ||
24 | }; | ||
25 | |||
26 | |||
27 | #[derive(Clone, Debug)] | ||
28 | pub(crate) struct FileResolverImp { | ||
29 | inner: Arc<FileResolver> | ||
30 | } | ||
31 | |||
32 | impl FileResolverImp { | ||
33 | pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { | ||
34 | FileResolverImp { inner } | ||
35 | } | ||
36 | pub(crate) fn file_stem(&self, file_id: FileId) -> String { | ||
37 | self.inner.file_stem(file_id) | ||
38 | } | ||
39 | pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { | ||
40 | self.inner.resolve(file_id, path) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | impl Default for FileResolverImp { | ||
45 | fn default() -> FileResolverImp { | ||
46 | #[derive(Debug)] | ||
47 | struct DummyResolver; | ||
48 | impl FileResolver for DummyResolver { | ||
49 | fn file_stem(&self, _file_: FileId) -> String { | ||
50 | panic!("file resolver not set") | ||
51 | } | ||
52 | fn resolve(&self, _file_id: FileId, _path: &::relative_path::RelativePath) -> Option<FileId> { | ||
53 | panic!("file resolver not set") | ||
54 | } | ||
55 | } | ||
56 | FileResolverImp { inner: Arc::new(DummyResolver) } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | #[derive(Debug)] | ||
61 | pub(crate) struct AnalysisHostImpl { | ||
62 | data: Arc<WorldData> | ||
63 | } | ||
64 | |||
65 | impl AnalysisHostImpl { | ||
66 | pub fn new() -> AnalysisHostImpl { | ||
67 | AnalysisHostImpl { | ||
68 | data: Arc::new(WorldData::default()), | ||
69 | } | ||
70 | } | ||
71 | pub fn analysis(&self) -> AnalysisImpl { | ||
72 | AnalysisImpl { | ||
73 | needs_reindex: AtomicBool::new(false), | ||
74 | data: self.data.clone(), | ||
75 | } | ||
76 | } | ||
77 | pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { | ||
78 | let data = self.data_mut(); | ||
79 | data.root = Arc::new(data.root.apply_changes(changes, None)); | ||
80 | } | ||
81 | pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { | ||
82 | let data = self.data_mut(); | ||
83 | data.file_resolver = resolver.clone(); | ||
84 | data.root = Arc::new(data.root.apply_changes(&mut iter::empty(), Some(resolver))); | ||
85 | } | ||
86 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { | ||
87 | let mut visited = HashSet::new(); | ||
88 | for &file_id in graph.crate_roots.values() { | ||
89 | if !visited.insert(file_id) { | ||
90 | panic!("duplicate crate root: {:?}", file_id); | ||
91 | } | ||
92 | } | ||
93 | self.data_mut().crate_graph = graph; | ||
94 | } | ||
95 | pub fn add_library(&mut self, root: ReadonlySourceRoot) { | ||
96 | self.data_mut().libs.push(Arc::new(root)); | ||
97 | } | ||
98 | fn data_mut(&mut self) -> &mut WorldData { | ||
99 | Arc::make_mut(&mut self.data) | ||
100 | } | ||
101 | } | ||
102 | |||
103 | pub(crate) struct AnalysisImpl { | ||
104 | needs_reindex: AtomicBool, | ||
105 | data: Arc<WorldData>, | ||
106 | } | ||
107 | |||
108 | impl fmt::Debug for AnalysisImpl { | ||
109 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
110 | (&*self.data).fmt(f) | ||
111 | } | ||
112 | } | ||
113 | |||
114 | impl Clone for AnalysisImpl { | ||
115 | fn clone(&self) -> AnalysisImpl { | ||
116 | AnalysisImpl { | ||
117 | needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), | ||
118 | data: Arc::clone(&self.data), | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | impl AnalysisImpl { | ||
124 | fn root(&self, file_id: FileId) -> &SourceRoot { | ||
125 | if self.data.root.contains(file_id) { | ||
126 | return &*self.data.root; | ||
127 | } | ||
128 | &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() | ||
129 | } | ||
130 | pub fn file_syntax(&self, file_id: FileId) -> File { | ||
131 | self.root(file_id).syntax(file_id) | ||
132 | } | ||
133 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { | ||
134 | self.root(file_id).lines(file_id) | ||
135 | } | ||
136 | pub fn world_symbols(&self, query: Query, token: &JobToken) -> Vec<(FileId, FileSymbol)> { | ||
137 | let mut buf = Vec::new(); | ||
138 | if query.libs { | ||
139 | self.data.libs.iter() | ||
140 | .for_each(|it| it.symbols(&mut buf)); | ||
141 | } else { | ||
142 | self.data.root.symbols(&mut buf); | ||
143 | } | ||
144 | query.search(&buf, token) | ||
145 | |||
146 | } | ||
147 | pub fn parent_module(&self, file_id: FileId) -> Vec<(FileId, FileSymbol)> { | ||
148 | let root = self.root(file_id); | ||
149 | let module_tree = root.module_tree(); | ||
150 | module_tree.parent_modules(file_id) | ||
151 | .iter() | ||
152 | .map(|link| { | ||
153 | let file_id = link.owner(&module_tree); | ||
154 | let syntax = root.syntax(file_id); | ||
155 | let decl = link.bind_source(&module_tree, syntax.ast()); | ||
156 | let sym = FileSymbol { | ||
157 | name: link.name(&module_tree), | ||
158 | node_range: decl.syntax().range(), | ||
159 | kind: MODULE, | ||
160 | }; | ||
161 | (file_id, sym) | ||
162 | }) | ||
163 | .collect() | ||
164 | } | ||
165 | pub fn crate_for(&self, file_id: FileId) -> Vec<CrateId> { | ||
166 | let module_tree = self.root(file_id).module_tree(); | ||
167 | let crate_graph = &self.data.crate_graph; | ||
168 | let mut res = Vec::new(); | ||
169 | let mut work = VecDeque::new(); | ||
170 | work.push_back(file_id); | ||
171 | let mut visited = HashSet::new(); | ||
172 | while let Some(id) = work.pop_front() { | ||
173 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(id) { | ||
174 | res.push(crate_id); | ||
175 | continue; | ||
176 | } | ||
177 | let parents = module_tree | ||
178 | .parent_modules(id) | ||
179 | .into_iter() | ||
180 | .map(|link| link.owner(&module_tree)) | ||
181 | .filter(|&id| visited.insert(id)); | ||
182 | work.extend(parents); | ||
183 | } | ||
184 | res | ||
185 | } | ||
186 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | ||
187 | self.data.crate_graph.crate_roots[&crate_id] | ||
188 | } | ||
189 | pub fn approximately_resolve_symbol( | ||
190 | &self, | ||
191 | file_id: FileId, | ||
192 | offset: TextUnit, | ||
193 | token: &JobToken, | ||
194 | ) -> Vec<(FileId, FileSymbol)> { | ||
195 | let root = self.root(file_id); | ||
196 | let module_tree = root.module_tree(); | ||
197 | let file = root.syntax(file_id); | ||
198 | let syntax = file.syntax(); | ||
199 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { | ||
200 | return self.index_resolve(name_ref, token); | ||
201 | } | ||
202 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { | ||
203 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | ||
204 | if module.has_semi() { | ||
205 | let file_ids = self.resolve_module(&*module_tree, file_id, module); | ||
206 | |||
207 | let res = file_ids.into_iter().map(|id| { | ||
208 | let name = module.name() | ||
209 | .map(|n| n.text()) | ||
210 | .unwrap_or_else(|| SmolStr::new("")); | ||
211 | let symbol = FileSymbol { | ||
212 | name, | ||
213 | node_range: TextRange::offset_len(0.into(), 0.into()), | ||
214 | kind: MODULE, | ||
215 | }; | ||
216 | (id, symbol) | ||
217 | }).collect(); | ||
218 | |||
219 | return res; | ||
220 | } | ||
221 | } | ||
222 | } | ||
223 | vec![] | ||
224 | } | ||
225 | |||
226 | pub fn diagnostics(&self, file_id: FileId) -> Vec<Diagnostic> { | ||
227 | let root = self.root(file_id); | ||
228 | let module_tree = root.module_tree(); | ||
229 | let syntax = root.syntax(file_id); | ||
230 | |||
231 | let mut res = ra_editor::diagnostics(&syntax) | ||
232 | .into_iter() | ||
233 | .map(|d| Diagnostic { range: d.range, message: d.msg, fix: None }) | ||
234 | .collect::<Vec<_>>(); | ||
235 | |||
236 | for (name_node, problem) in module_tree.problems(file_id, syntax.ast()) { | ||
237 | let diag = match problem { | ||
238 | Problem::UnresolvedModule { candidate } => { | ||
239 | let create_file = FileSystemEdit::CreateFile { | ||
240 | anchor: file_id, | ||
241 | path: candidate.clone(), | ||
242 | }; | ||
243 | let fix = SourceChange { | ||
244 | label: "create module".to_string(), | ||
245 | source_file_edits: Vec::new(), | ||
246 | file_system_edits: vec![create_file], | ||
247 | cursor_position: None, | ||
248 | }; | ||
249 | Diagnostic { | ||
250 | range: name_node.syntax().range(), | ||
251 | message: "unresolved module".to_string(), | ||
252 | fix: Some(fix), | ||
253 | } | ||
254 | } | ||
255 | Problem::NotDirOwner { move_to, candidate } => { | ||
256 | let move_file = FileSystemEdit::MoveFile { file: file_id, path: move_to.clone() }; | ||
257 | let create_file = FileSystemEdit::CreateFile { anchor: file_id, path: move_to.join(candidate) }; | ||
258 | let fix = SourceChange { | ||
259 | label: "move file and create module".to_string(), | ||
260 | source_file_edits: Vec::new(), | ||
261 | file_system_edits: vec![move_file, create_file], | ||
262 | cursor_position: None, | ||
263 | }; | ||
264 | Diagnostic { | ||
265 | range: name_node.syntax().range(), | ||
266 | message: "can't declare module at this location".to_string(), | ||
267 | fix: Some(fix), | ||
268 | } | ||
269 | } | ||
270 | }; | ||
271 | res.push(diag) | ||
272 | } | ||
273 | res | ||
274 | } | ||
275 | |||
276 | pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> { | ||
277 | let file = self.file_syntax(file_id); | ||
278 | let offset = range.start(); | ||
279 | let actions = vec![ | ||
280 | ("flip comma", ra_editor::flip_comma(&file, offset).map(|f| f())), | ||
281 | ("add `#[derive]`", ra_editor::add_derive(&file, offset).map(|f| f())), | ||
282 | ("add impl", ra_editor::add_impl(&file, offset).map(|f| f())), | ||
283 | ("introduce variable", ra_editor::introduce_variable(&file, range).map(|f| f())), | ||
284 | ]; | ||
285 | actions.into_iter() | ||
286 | .filter_map(|(name, local_edit)| { | ||
287 | Some(SourceChange::from_local_edit( | ||
288 | file_id, name, local_edit?, | ||
289 | )) | ||
290 | }) | ||
291 | .collect() | ||
292 | } | ||
293 | |||
294 | fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> { | ||
295 | let name = name_ref.text(); | ||
296 | let mut query = Query::new(name.to_string()); | ||
297 | query.exact(); | ||
298 | query.limit(4); | ||
299 | self.world_symbols(query, token) | ||
300 | } | ||
301 | |||
302 | fn resolve_module(&self, module_tree: &ModuleTreeDescriptor, file_id: FileId, module: ast::Module) -> Vec<FileId> { | ||
303 | let name = match module.name() { | ||
304 | Some(name) => name.text(), | ||
305 | None => return Vec::new(), | ||
306 | }; | ||
307 | module_tree.child_module_by_name(file_id, name.as_str()) | ||
308 | } | ||
309 | } | ||
310 | |||
311 | #[derive(Default, Clone, Debug)] | ||
312 | struct WorldData { | ||
313 | file_resolver: FileResolverImp, | ||
314 | crate_graph: CrateGraph, | ||
315 | root: Arc<WritableSourceRoot>, | ||
316 | libs: Vec<Arc<ReadonlySourceRoot>>, | ||
317 | } | ||
318 | |||
319 | impl SourceChange { | ||
320 | pub(crate) fn from_local_edit(file_id: FileId, label: &str, edit: LocalEdit) -> SourceChange { | ||
321 | let file_edit = SourceFileEdit { | ||
322 | file_id, | ||
323 | edits: edit.edit.into_atoms(), | ||
324 | }; | ||
325 | SourceChange { | ||
326 | label: label.to_string(), | ||
327 | source_file_edits: vec![file_edit], | ||
328 | file_system_edits: vec![], | ||
329 | cursor_position: edit.cursor_position | ||
330 | .map(|offset| Position { offset, file_id }) | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
335 | impl CrateGraph { | ||
336 | fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | ||
337 | let (&crate_id, _) = self.crate_roots | ||
338 | .iter() | ||
339 | .find(|(_crate_id, &root_id)| root_id == file_id)?; | ||
340 | Some(crate_id) | ||
341 | } | ||
342 | } | ||