diff options
Diffstat (limited to 'crates/ra_analysis/src/imp.rs')
-rw-r--r-- | crates/ra_analysis/src/imp.rs | 169 |
1 files changed, 139 insertions, 30 deletions
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 47bc0032b..5efcaeca0 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | sync::{ | 2 | sync::{ |
3 | Arc, | 3 | Arc, |
4 | atomic::{AtomicBool, Ordering::SeqCst}, | ||
5 | }, | 4 | }, |
5 | hash::{Hash, Hasher}, | ||
6 | fmt, | 6 | fmt, |
7 | collections::VecDeque, | 7 | collections::VecDeque, |
8 | iter, | 8 | iter, |
@@ -12,24 +12,38 @@ use relative_path::RelativePath; | |||
12 | use rustc_hash::FxHashSet; | 12 | use rustc_hash::FxHashSet; |
13 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; | 13 | use ra_editor::{self, FileSymbol, LineIndex, find_node_at_offset, LocalEdit, resolve_local_name}; |
14 | use ra_syntax::{ | 14 | use ra_syntax::{ |
15 | TextUnit, TextRange, SmolStr, File, AstNode, | 15 | TextUnit, TextRange, SmolStr, File, AstNode, SyntaxNodeRef, |
16 | SyntaxKind::*, | 16 | SyntaxKind::*, |
17 | ast::{self, NameOwner}, | 17 | ast::{self, NameOwner, ArgListOwner, Expr}, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | use { | 20 | use crate::{ |
21 | FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, | 21 | FileId, FileResolver, Query, Diagnostic, SourceChange, SourceFileEdit, Position, FileSystemEdit, |
22 | JobToken, CrateGraph, CrateId, | 22 | JobToken, CrateGraph, CrateId, |
23 | roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, | 23 | roots::{SourceRoot, ReadonlySourceRoot, WritableSourceRoot}, |
24 | descriptors::{ModuleTreeDescriptor, Problem}, | 24 | descriptors::{FnDescriptor, ModuleTreeDescriptor, Problem}, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | |||
28 | #[derive(Clone, Debug)] | 27 | #[derive(Clone, Debug)] |
29 | pub(crate) struct FileResolverImp { | 28 | pub(crate) struct FileResolverImp { |
30 | inner: Arc<FileResolver> | 29 | inner: Arc<FileResolver> |
31 | } | 30 | } |
32 | 31 | ||
32 | impl PartialEq for FileResolverImp { | ||
33 | fn eq(&self, other: &FileResolverImp) -> bool { | ||
34 | self.inner() == other.inner() | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl Eq for FileResolverImp { | ||
39 | } | ||
40 | |||
41 | impl Hash for FileResolverImp { | ||
42 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
43 | self.inner().hash(hasher); | ||
44 | } | ||
45 | } | ||
46 | |||
33 | impl FileResolverImp { | 47 | impl FileResolverImp { |
34 | pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { | 48 | pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { |
35 | FileResolverImp { inner } | 49 | FileResolverImp { inner } |
@@ -40,6 +54,9 @@ impl FileResolverImp { | |||
40 | pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { | 54 | pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { |
41 | self.inner.resolve(file_id, path) | 55 | self.inner.resolve(file_id, path) |
42 | } | 56 | } |
57 | fn inner(&self) -> *const FileResolver { | ||
58 | &*self.inner | ||
59 | } | ||
43 | } | 60 | } |
44 | 61 | ||
45 | impl Default for FileResolverImp { | 62 | impl Default for FileResolverImp { |
@@ -60,29 +77,27 @@ impl Default for FileResolverImp { | |||
60 | 77 | ||
61 | #[derive(Debug)] | 78 | #[derive(Debug)] |
62 | pub(crate) struct AnalysisHostImpl { | 79 | pub(crate) struct AnalysisHostImpl { |
63 | data: Arc<WorldData> | 80 | data: WorldData |
64 | } | 81 | } |
65 | 82 | ||
66 | impl AnalysisHostImpl { | 83 | impl AnalysisHostImpl { |
67 | pub fn new() -> AnalysisHostImpl { | 84 | pub fn new() -> AnalysisHostImpl { |
68 | AnalysisHostImpl { | 85 | AnalysisHostImpl { |
69 | data: Arc::new(WorldData::default()), | 86 | data: WorldData::default(), |
70 | } | 87 | } |
71 | } | 88 | } |
72 | pub fn analysis(&self) -> AnalysisImpl { | 89 | pub fn analysis(&self) -> AnalysisImpl { |
73 | AnalysisImpl { | 90 | AnalysisImpl { |
74 | needs_reindex: AtomicBool::new(false), | ||
75 | data: self.data.clone(), | 91 | data: self.data.clone(), |
76 | } | 92 | } |
77 | } | 93 | } |
78 | pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { | 94 | pub fn change_files(&mut self, changes: &mut dyn Iterator<Item=(FileId, Option<String>)>) { |
79 | let data = self.data_mut(); | 95 | self.data_mut() |
80 | data.root = Arc::new(data.root.apply_changes(changes, None)); | 96 | .root.apply_changes(changes, None); |
81 | } | 97 | } |
82 | pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { | 98 | pub fn set_file_resolver(&mut self, resolver: FileResolverImp) { |
83 | let data = self.data_mut(); | 99 | self.data_mut() |
84 | data.file_resolver = resolver.clone(); | 100 | .root.apply_changes(&mut iter::empty(), Some(resolver)); |
85 | data.root = Arc::new(data.root.apply_changes(&mut iter::empty(), Some(resolver))); | ||
86 | } | 101 | } |
87 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { | 102 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { |
88 | let mut visited = FxHashSet::default(); | 103 | let mut visited = FxHashSet::default(); |
@@ -97,34 +112,24 @@ impl AnalysisHostImpl { | |||
97 | self.data_mut().libs.push(Arc::new(root)); | 112 | self.data_mut().libs.push(Arc::new(root)); |
98 | } | 113 | } |
99 | fn data_mut(&mut self) -> &mut WorldData { | 114 | fn data_mut(&mut self) -> &mut WorldData { |
100 | Arc::make_mut(&mut self.data) | 115 | &mut self.data |
101 | } | 116 | } |
102 | } | 117 | } |
103 | 118 | ||
104 | pub(crate) struct AnalysisImpl { | 119 | pub(crate) struct AnalysisImpl { |
105 | needs_reindex: AtomicBool, | 120 | data: WorldData, |
106 | data: Arc<WorldData>, | ||
107 | } | 121 | } |
108 | 122 | ||
109 | impl fmt::Debug for AnalysisImpl { | 123 | impl fmt::Debug for AnalysisImpl { |
110 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 124 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
111 | (&*self.data).fmt(f) | 125 | self.data.fmt(f) |
112 | } | ||
113 | } | ||
114 | |||
115 | impl Clone for AnalysisImpl { | ||
116 | fn clone(&self) -> AnalysisImpl { | ||
117 | AnalysisImpl { | ||
118 | needs_reindex: AtomicBool::new(self.needs_reindex.load(SeqCst)), | ||
119 | data: Arc::clone(&self.data), | ||
120 | } | ||
121 | } | 126 | } |
122 | } | 127 | } |
123 | 128 | ||
124 | impl AnalysisImpl { | 129 | impl AnalysisImpl { |
125 | fn root(&self, file_id: FileId) -> &SourceRoot { | 130 | fn root(&self, file_id: FileId) -> &SourceRoot { |
126 | if self.data.root.contains(file_id) { | 131 | if self.data.root.contains(file_id) { |
127 | return &*self.data.root; | 132 | return &self.data.root; |
128 | } | 133 | } |
129 | &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() | 134 | &**self.data.libs.iter().find(|it| it.contains(file_id)).unwrap() |
130 | } | 135 | } |
@@ -306,6 +311,68 @@ impl AnalysisImpl { | |||
306 | .collect() | 311 | .collect() |
307 | } | 312 | } |
308 | 313 | ||
314 | pub fn resolve_callable(&self, file_id: FileId, offset: TextUnit, token: &JobToken) | ||
315 | -> Option<(FnDescriptor, Option<usize>)> { | ||
316 | |||
317 | let root = self.root(file_id); | ||
318 | let file = root.syntax(file_id); | ||
319 | let syntax = file.syntax(); | ||
320 | |||
321 | // Find the calling expression and it's NameRef | ||
322 | let calling_node = FnCallNode::with_node(syntax, offset)?; | ||
323 | let name_ref = calling_node.name_ref()?; | ||
324 | |||
325 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | ||
326 | let file_symbols = self.index_resolve(name_ref, token); | ||
327 | for (_, fs) in file_symbols { | ||
328 | if fs.kind == FN_DEF { | ||
329 | if let Some(fn_def) = find_node_at_offset(syntax, fs.node_range.start()) { | ||
330 | if let Some(descriptor) = FnDescriptor::new(fn_def) { | ||
331 | // If we have a calling expression let's find which argument we are on | ||
332 | let mut current_parameter = None; | ||
333 | |||
334 | let num_params = descriptor.params.len(); | ||
335 | let has_self = fn_def.param_list() | ||
336 | .and_then(|l| l.self_param()) | ||
337 | .is_some(); | ||
338 | |||
339 | if num_params == 1 { | ||
340 | if !has_self { | ||
341 | current_parameter = Some(1); | ||
342 | } | ||
343 | } else if num_params > 1 { | ||
344 | // Count how many parameters into the call we are. | ||
345 | // TODO: This is best effort for now and should be fixed at some point. | ||
346 | // It may be better to see where we are in the arg_list and then check | ||
347 | // where offset is in that list (or beyond). | ||
348 | // Revisit this after we get documentation comments in. | ||
349 | if let Some(ref arg_list) = calling_node.arg_list() { | ||
350 | let start = arg_list.syntax().range().start(); | ||
351 | |||
352 | let range_search = TextRange::from_to(start, offset); | ||
353 | let mut commas: usize = arg_list.syntax().text() | ||
354 | .slice(range_search).to_string() | ||
355 | .matches(",") | ||
356 | .count(); | ||
357 | |||
358 | // If we have a method call eat the first param since it's just self. | ||
359 | if has_self { | ||
360 | commas = commas + 1; | ||
361 | } | ||
362 | |||
363 | current_parameter = Some(commas); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | return Some((descriptor, current_parameter)); | ||
368 | } | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | None | ||
374 | } | ||
375 | |||
309 | fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> { | 376 | fn index_resolve(&self, name_ref: ast::NameRef, token: &JobToken) -> Vec<(FileId, FileSymbol)> { |
310 | let name = name_ref.text(); | 377 | let name = name_ref.text(); |
311 | let mut query = Query::new(name.to_string()); | 378 | let mut query = Query::new(name.to_string()); |
@@ -325,9 +392,8 @@ impl AnalysisImpl { | |||
325 | 392 | ||
326 | #[derive(Default, Clone, Debug)] | 393 | #[derive(Default, Clone, Debug)] |
327 | struct WorldData { | 394 | struct WorldData { |
328 | file_resolver: FileResolverImp, | ||
329 | crate_graph: CrateGraph, | 395 | crate_graph: CrateGraph, |
330 | root: Arc<WritableSourceRoot>, | 396 | root: WritableSourceRoot, |
331 | libs: Vec<Arc<ReadonlySourceRoot>>, | 397 | libs: Vec<Arc<ReadonlySourceRoot>>, |
332 | } | 398 | } |
333 | 399 | ||
@@ -355,3 +421,46 @@ impl CrateGraph { | |||
355 | Some(crate_id) | 421 | Some(crate_id) |
356 | } | 422 | } |
357 | } | 423 | } |
424 | |||
425 | enum FnCallNode<'a> { | ||
426 | CallExpr(ast::CallExpr<'a>), | ||
427 | MethodCallExpr(ast::MethodCallExpr<'a>) | ||
428 | } | ||
429 | |||
430 | impl<'a> FnCallNode<'a> { | ||
431 | pub fn with_node(syntax: SyntaxNodeRef, offset: TextUnit) -> Option<FnCallNode> { | ||
432 | if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) { | ||
433 | return Some(FnCallNode::CallExpr(expr)); | ||
434 | } | ||
435 | if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) { | ||
436 | return Some(FnCallNode::MethodCallExpr(expr)); | ||
437 | } | ||
438 | None | ||
439 | } | ||
440 | |||
441 | pub fn name_ref(&self) -> Option<ast::NameRef> { | ||
442 | match *self { | ||
443 | FnCallNode::CallExpr(call_expr) => { | ||
444 | Some(match call_expr.expr()? { | ||
445 | Expr::PathExpr(path_expr) => { | ||
446 | path_expr.path()?.segment()?.name_ref()? | ||
447 | }, | ||
448 | _ => return None | ||
449 | }) | ||
450 | }, | ||
451 | |||
452 | FnCallNode::MethodCallExpr(call_expr) => { | ||
453 | call_expr.syntax().children() | ||
454 | .filter_map(ast::NameRef::cast) | ||
455 | .nth(0) | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | |||
460 | pub fn arg_list(&self) -> Option<ast::ArgList> { | ||
461 | match *self { | ||
462 | FnCallNode::CallExpr(expr) => expr.arg_list(), | ||
463 | FnCallNode::MethodCallExpr(expr) => expr.arg_list() | ||
464 | } | ||
465 | } | ||
466 | } | ||