aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis')
-rw-r--r--crates/ra_analysis/src/extend_selection.rs29
-rw-r--r--crates/ra_analysis/src/imp.rs22
-rw-r--r--crates/ra_analysis/src/lib.rs14
-rw-r--r--crates/ra_analysis/src/runnables.rs72
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs4
-rw-r--r--crates/ra_analysis/tests/runnables.rs118
-rw-r--r--crates/ra_analysis/tests/tests.rs50
7 files changed, 291 insertions, 18 deletions
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
index cde6ee101..805e9059e 100644
--- a/crates/ra_analysis/src/extend_selection.rs
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -1,6 +1,6 @@
1use ra_db::SyntaxDatabase; 1use ra_db::SyntaxDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 SyntaxNodeRef, AstNode, 3 SyntaxNodeRef, AstNode, SourceFileNode,
4 ast, algo::find_covering_node, 4 ast, algo::find_covering_node,
5}; 5};
6 6
@@ -11,18 +11,23 @@ use crate::{
11 11
12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { 12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
13 let source_file = db.source_file(frange.file_id); 13 let source_file = db.source_file(frange.file_id);
14 if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) { 14 if let Some(range) = extend_selection_in_macro(db, &source_file, frange) {
15 if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) { 15 return range;
16 if let Some(dst_range) = exp.map_range_forward(frange.range) {
17 if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) {
18 if let Some(src_range) = exp.map_range_back(dst_range) {
19 return src_range;
20 }
21 }
22 }
23 }
24 } 16 }
25 ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range) 17 ra_editor::extend_selection(source_file.syntax(), frange.range).unwrap_or(frange.range)
18}
19
20fn extend_selection_in_macro(
21 db: &RootDatabase,
22 source_file: &SourceFileNode,
23 frange: FileRange,
24) -> Option<TextRange> {
25 let macro_call = find_macro_call(source_file.syntax(), frange.range)?;
26 let exp = crate::macros::expand(db, frange.file_id, macro_call)?;
27 let dst_range = exp.map_range_forward(frange.range)?;
28 let dst_range = ra_editor::extend_selection(exp.source_file().syntax(), dst_range)?;
29 let src_range = exp.map_range_back(dst_range)?;
30 Some(src_range)
26} 31}
27 32
28fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> { 33fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> {
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 5ed374c79..5669aa94d 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -181,6 +181,28 @@ impl AnalysisImpl {
181 }; 181 };
182 Ok(query.search(&buf)) 182 Ok(query.search(&buf))
183 } 183 }
184
185 pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
186 let descr = match source_binder::module_from_position(&*self.db, position)? {
187 None => return Ok(None),
188 Some(it) => it,
189 };
190 let name = match descr.name() {
191 None => return Ok(None),
192 Some(it) => it.to_string(),
193 };
194
195 let modules = descr.path_to_root();
196
197 let path = modules
198 .iter()
199 .filter_map(|s| s.name())
200 .skip(1) // name is already part of the string.
201 .fold(name, |path, it| format!("{}::{}", it, path));
202
203 Ok(Some(path.to_string()))
204 }
205
184 /// This returns `Vec` because a module may be included from several places. We 206 /// This returns `Vec` because a module may be included from several places. We
185 /// don't handle this case yet though, so the Vec has length at most one. 207 /// don't handle this case yet though, so the Vec has length at most one.
186 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 208 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index e56168510..e6cfaecc3 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -15,6 +15,7 @@ mod imp;
15mod completion; 15mod completion;
16mod symbol_index; 16mod symbol_index;
17pub mod mock_analysis; 17pub mod mock_analysis;
18mod runnables;
18 19
19mod extend_selection; 20mod extend_selection;
20mod syntax_highlighting; 21mod syntax_highlighting;
@@ -33,10 +34,12 @@ use crate::{
33 symbol_index::SymbolIndex, 34 symbol_index::SymbolIndex,
34}; 35};
35 36
36pub use crate::completion::{CompletionItem, CompletionItemKind, InsertText}; 37pub use crate::{
38 completion::{CompletionItem, CompletionItemKind, InsertText},
39 runnables::{Runnable, RunnableKind}
40};
37pub use ra_editor::{ 41pub use ra_editor::{
38 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, 42 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
39 Severity
40}; 43};
41pub use hir::FnSignatureInfo; 44pub use hir::FnSignatureInfo;
42 45
@@ -336,6 +339,9 @@ impl Analysis {
336 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { 339 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
337 self.imp.parent_module(position) 340 self.imp.parent_module(position)
338 } 341 }
342 pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> {
343 self.imp.module_path(position)
344 }
339 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 345 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
340 self.imp.crate_for(file_id) 346 self.imp.crate_for(file_id)
341 } 347 }
@@ -344,7 +350,7 @@ impl Analysis {
344 } 350 }
345 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { 351 pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> {
346 let file = self.imp.file_syntax(file_id); 352 let file = self.imp.file_syntax(file_id);
347 Ok(ra_editor::runnables(&file)) 353 Ok(runnables::runnables(self, &file, file_id))
348 } 354 }
349 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 355 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
350 syntax_highlighting::highlight(&*self.imp.db, file_id) 356 syntax_highlighting::highlight(&*self.imp.db, file_id)
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs
new file mode 100644
index 000000000..61ca0930a
--- /dev/null
+++ b/crates/ra_analysis/src/runnables.rs
@@ -0,0 +1,72 @@
1use ra_syntax::{
2 ast::{self, AstNode, NameOwner, ModuleItemOwner},
3 SourceFileNode, TextRange, SyntaxNodeRef,
4 TextUnit,
5};
6use crate::{
7 Analysis, FileId, FilePosition
8};
9
10#[derive(Debug)]
11pub struct Runnable {
12 pub range: TextRange,
13 pub kind: RunnableKind,
14}
15
16#[derive(Debug)]
17pub enum RunnableKind {
18 Test { name: String },
19 TestMod { path: String },
20 Bin,
21}
22
23pub fn runnables(
24 analysis: &Analysis,
25 file_node: &SourceFileNode,
26 file_id: FileId,
27) -> Vec<Runnable> {
28 file_node
29 .syntax()
30 .descendants()
31 .filter_map(|i| runnable(analysis, i, file_id))
32 .collect()
33}
34
35fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> {
36 if let Some(f) = ast::FnDef::cast(item) {
37 let name = f.name()?.text();
38 let kind = if name == "main" {
39 RunnableKind::Bin
40 } else if f.has_atom_attr("test") {
41 RunnableKind::Test {
42 name: name.to_string(),
43 }
44 } else {
45 return None;
46 };
47 Some(Runnable {
48 range: f.syntax().range(),
49 kind,
50 })
51 } else if let Some(m) = ast::Module::cast(item) {
52 if m.item_list()?
53 .items()
54 .map(ast::ModuleItem::syntax)
55 .filter_map(ast::FnDef::cast)
56 .any(|f| f.has_atom_attr("test"))
57 {
58 let postition = FilePosition {
59 file_id,
60 offset: m.syntax().range().start() + TextUnit::from_usize(1),
61 };
62 analysis.module_path(postition).ok()?.map(|path| Runnable {
63 range: m.syntax().range(),
64 kind: RunnableKind::TestMod { path },
65 })
66 } else {
67 None
68 }
69 } else {
70 None
71 }
72}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
index 38219da71..7e9139a74 100644
--- a/crates/ra_analysis/src/syntax_highlighting.rs
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -9,14 +9,14 @@ use crate::{
9 9
10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
11 let source_file = db.source_file(file_id); 11 let source_file = db.source_file(file_id);
12 let mut res = ra_editor::highlight(&source_file); 12 let mut res = ra_editor::highlight(source_file.syntax());
13 for macro_call in source_file 13 for macro_call in source_file
14 .syntax() 14 .syntax()
15 .descendants() 15 .descendants()
16 .filter_map(ast::MacroCall::cast) 16 .filter_map(ast::MacroCall::cast)
17 { 17 {
18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) { 18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) {
19 let mapped_ranges = ra_editor::highlight(exp.source_file()) 19 let mapped_ranges = ra_editor::highlight(exp.source_file().syntax())
20 .into_iter() 20 .into_iter()
21 .filter_map(|r| { 21 .filter_map(|r| {
22 let mapped_range = exp.map_range_back(r.range)?; 22 let mapped_range = exp.map_range_back(r.range)?;
diff --git a/crates/ra_analysis/tests/runnables.rs b/crates/ra_analysis/tests/runnables.rs
new file mode 100644
index 000000000..9e5342c46
--- /dev/null
+++ b/crates/ra_analysis/tests/runnables.rs
@@ -0,0 +1,118 @@
1extern crate ra_analysis;
2extern crate ra_editor;
3extern crate ra_syntax;
4extern crate relative_path;
5extern crate rustc_hash;
6extern crate test_utils;
7
8use test_utils::assert_eq_dbg;
9
10use ra_analysis::{
11 mock_analysis::{analysis_and_position},
12};
13
14#[test]
15fn test_runnables() {
16 let (analysis, pos) = analysis_and_position(
17 r#"
18 //- /lib.rs
19 <|> //empty
20 fn main() {}
21
22 #[test]
23 fn test_foo() {}
24
25 #[test]
26 #[ignore]
27 fn test_foo() {}
28 "#,
29 );
30 let runnables = analysis.runnables(pos.file_id).unwrap();
31 assert_eq_dbg(
32 r#"[Runnable { range: [1; 21), kind: Bin },
33 Runnable { range: [22; 46), kind: Test { name: "test_foo" } },
34 Runnable { range: [47; 81), kind: Test { name: "test_foo" } }]"#,
35 &runnables,
36 )
37}
38
39#[test]
40fn test_runnables_module() {
41 let (analysis, pos) = analysis_and_position(
42 r#"
43 //- /lib.rs
44 <|> //empty
45 mod test_mod {
46 #[test]
47 fn test_foo1() {}
48 }
49 "#,
50 );
51 let runnables = analysis.runnables(pos.file_id).unwrap();
52 assert_eq_dbg(
53 r#"[Runnable { range: [1; 59), kind: TestMod { path: "test_mod" } },
54 Runnable { range: [28; 57), kind: Test { name: "test_foo1" } }]"#,
55 &runnables,
56 )
57}
58
59#[test]
60fn test_runnables_one_depth_layer_module() {
61 let (analysis, pos) = analysis_and_position(
62 r#"
63 //- /lib.rs
64 <|> //empty
65 mod foo {
66 mod test_mod {
67 #[test]
68 fn test_foo1() {}
69 }
70 }
71 "#,
72 );
73 let runnables = analysis.runnables(pos.file_id).unwrap();
74 assert_eq_dbg(
75 r#"[Runnable { range: [23; 85), kind: TestMod { path: "foo::test_mod" } },
76 Runnable { range: [46; 79), kind: Test { name: "test_foo1" } }]"#,
77 &runnables,
78 )
79}
80
81#[test]
82fn test_runnables_multiple_depth_module() {
83 let (analysis, pos) = analysis_and_position(
84 r#"
85 //- /lib.rs
86 <|> //empty
87 mod foo {
88 mod bar {
89 mod test_mod {
90 #[test]
91 fn test_foo1() {}
92 }
93 }
94 }
95 "#,
96 );
97 let runnables = analysis.runnables(pos.file_id).unwrap();
98 assert_eq_dbg(
99 r#"[Runnable { range: [41; 115), kind: TestMod { path: "foo::bar::test_mod" } },
100 Runnable { range: [68; 105), kind: Test { name: "test_foo1" } }]"#,
101 &runnables,
102 )
103}
104
105#[test]
106fn test_runnables_no_test_function_in_module() {
107 let (analysis, pos) = analysis_and_position(
108 r#"
109 //- /lib.rs
110 <|> //empty
111 mod test_mod {
112 fn foo1() {}
113 }
114 "#,
115 );
116 let runnables = analysis.runnables(pos.file_id).unwrap();
117 assert_eq_dbg(r#"[]"#, &runnables)
118}
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index a314fbc40..b61ead752 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -132,6 +132,56 @@ fn test_resolve_parent_module_for_inline() {
132} 132}
133 133
134#[test] 134#[test]
135fn test_path_one_layer() {
136 let (analysis, pos) = analysis_and_position(
137 "
138 //- /lib.rs
139 mod foo;
140 //- /foo/mod.rs
141 mod bla;
142 //- /foo/bla.rs
143 <|> //empty
144 ",
145 );
146 let symbols = analysis.module_path(pos).unwrap().unwrap();
147 assert_eq!("foo::bla", &symbols);
148}
149
150#[test]
151fn test_path_two_layer() {
152 let (analysis, pos) = analysis_and_position(
153 "
154 //- /lib.rs
155 mod foo;
156 //- /foo/mod.rs
157 mod bla;
158 //- /foo/bla/mod.rs
159 mod more;
160 //- /foo/bla/more.rs
161 <|> //empty
162 ",
163 );
164 let symbols = analysis.module_path(pos).unwrap().unwrap();
165 assert_eq!("foo::bla::more", &symbols);
166}
167
168#[test]
169fn test_path_in_file_mod() {
170 let (analysis, pos) = analysis_and_position(
171 "
172 //- /lib.rs
173 mod foo;
174 //- /foo.rs
175 mod bar {
176 <|> //empty
177 }
178 ",
179 );
180 let symbols = analysis.module_path(pos).unwrap().unwrap();
181 assert_eq!("foo::bar", &symbols);
182}
183
184#[test]
135fn test_resolve_crate_root() { 185fn test_resolve_crate_root() {
136 let mock = MockAnalysis::with_files( 186 let mock = MockAnalysis::with_files(
137 " 187 "