diff options
Diffstat (limited to 'crates/ide')
-rw-r--r-- | crates/ide/src/fn_references.rs | 95 | ||||
-rw-r--r-- | crates/ide/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 2 |
3 files changed, 102 insertions, 1 deletions
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs new file mode 100644 index 000000000..1989a562b --- /dev/null +++ b/crates/ide/src/fn_references.rs | |||
@@ -0,0 +1,95 @@ | |||
1 | //! This module implements a methods and free functions search in the specified file. | ||
2 | //! We have to skip tests, so cannot reuse file_structure module. | ||
3 | |||
4 | use hir::Semantics; | ||
5 | use ide_db::RootDatabase; | ||
6 | use syntax::{ast, ast::NameOwner, AstNode, SyntaxNode}; | ||
7 | |||
8 | use crate::{runnables::has_test_related_attribute, FileId, FileRange}; | ||
9 | |||
10 | pub(crate) fn find_all_methods(db: &RootDatabase, file_id: FileId) -> Vec<FileRange> { | ||
11 | let sema = Semantics::new(db); | ||
12 | let source_file = sema.parse(file_id); | ||
13 | source_file.syntax().descendants().filter_map(|it| method_range(it, file_id)).collect() | ||
14 | } | ||
15 | |||
16 | fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { | ||
17 | ast::Fn::cast(item).and_then(|fn_def| { | ||
18 | if has_test_related_attribute(&fn_def) { | ||
19 | None | ||
20 | } else { | ||
21 | fn_def.name().map(|name| FileRange { file_id, range: name.syntax().text_range() }) | ||
22 | } | ||
23 | }) | ||
24 | } | ||
25 | |||
26 | #[cfg(test)] | ||
27 | mod tests { | ||
28 | use crate::mock_analysis::analysis_and_position; | ||
29 | use crate::{FileRange, TextSize}; | ||
30 | use std::ops::RangeInclusive; | ||
31 | |||
32 | #[test] | ||
33 | fn test_find_all_methods() { | ||
34 | let (analysis, pos) = analysis_and_position( | ||
35 | r#" | ||
36 | //- /lib.rs | ||
37 | fn private_fn() {<|>} | ||
38 | |||
39 | pub fn pub_fn() {} | ||
40 | |||
41 | pub fn generic_fn<T>(arg: T) {} | ||
42 | "#, | ||
43 | ); | ||
44 | |||
45 | let refs = analysis.find_all_methods(pos.file_id).unwrap(); | ||
46 | check_result(&refs, &[3..=13, 27..=33, 47..=57]); | ||
47 | } | ||
48 | |||
49 | #[test] | ||
50 | fn test_find_trait_methods() { | ||
51 | let (analysis, pos) = analysis_and_position( | ||
52 | r#" | ||
53 | //- /lib.rs | ||
54 | trait Foo { | ||
55 | fn bar() {<|>} | ||
56 | fn baz() {} | ||
57 | } | ||
58 | "#, | ||
59 | ); | ||
60 | |||
61 | let refs = analysis.find_all_methods(pos.file_id).unwrap(); | ||
62 | check_result(&refs, &[19..=22, 35..=38]); | ||
63 | } | ||
64 | |||
65 | #[test] | ||
66 | fn test_skip_tests() { | ||
67 | let (analysis, pos) = analysis_and_position( | ||
68 | r#" | ||
69 | //- /lib.rs | ||
70 | #[test] | ||
71 | fn foo() {<|>} | ||
72 | |||
73 | pub fn pub_fn() {} | ||
74 | |||
75 | mod tests { | ||
76 | #[test] | ||
77 | fn bar() {} | ||
78 | } | ||
79 | "#, | ||
80 | ); | ||
81 | |||
82 | let refs = analysis.find_all_methods(pos.file_id).unwrap(); | ||
83 | check_result(&refs, &[28..=34]); | ||
84 | } | ||
85 | |||
86 | fn check_result(refs: &[FileRange], expected: &[RangeInclusive<u32>]) { | ||
87 | assert_eq!(refs.len(), expected.len()); | ||
88 | |||
89 | for (i, item) in refs.iter().enumerate() { | ||
90 | let range = &expected[i]; | ||
91 | assert_eq!(TextSize::from(*range.start()), item.range.start()); | ||
92 | assert_eq!(TextSize::from(*range.end()), item.range.end()); | ||
93 | } | ||
94 | } | ||
95 | } | ||
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 4763c0aac..31f2bcba3 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -38,6 +38,7 @@ mod join_lines; | |||
38 | mod matching_brace; | 38 | mod matching_brace; |
39 | mod parent_module; | 39 | mod parent_module; |
40 | mod references; | 40 | mod references; |
41 | mod fn_references; | ||
41 | mod runnables; | 42 | mod runnables; |
42 | mod status; | 43 | mod status; |
43 | mod syntax_highlighting; | 44 | mod syntax_highlighting; |
@@ -369,6 +370,11 @@ impl Analysis { | |||
369 | }) | 370 | }) |
370 | } | 371 | } |
371 | 372 | ||
373 | /// Finds all methods and free functions for the file. Does not return tests! | ||
374 | pub fn find_all_methods(&self, file_id: FileId) -> Cancelable<Vec<FileRange>> { | ||
375 | self.with_db(|db| fn_references::find_all_methods(db, file_id)) | ||
376 | } | ||
377 | |||
372 | /// Returns a short text describing element at position. | 378 | /// Returns a short text describing element at position. |
373 | pub fn hover( | 379 | pub fn hover( |
374 | &self, | 380 | &self, |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 989a63c09..cfeff40c1 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -203,7 +203,7 @@ impl TestAttr { | |||
203 | /// | 203 | /// |
204 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, | 204 | /// It may produce false positives, for example, `#[wasm_bindgen_test]` requires a different command to run the test, |
205 | /// but it's better than not to have the runnables for the tests at all. | 205 | /// but it's better than not to have the runnables for the tests at all. |
206 | fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | 206 | pub(crate) fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { |
207 | fn_def | 207 | fn_def |
208 | .attrs() | 208 | .attrs() |
209 | .filter_map(|attr| attr.path()) | 209 | .filter_map(|attr| attr.path()) |