aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src
diff options
context:
space:
mode:
authorJan Jansen <[email protected]>2018-12-27 20:45:16 +0000
committerJan Jansen <[email protected]>2018-12-31 14:00:04 +0000
commit05daa86634b41aa3f311a1ac0b02bf7fed7ed569 (patch)
treeb703cfe2fc7788631703141d9f4be63b0f5d7fe5 /crates/ra_analysis/src
parent690826871202c77326836a895a38532ebd83c54b (diff)
Make modules with tests runnable
Fixes #154
Diffstat (limited to 'crates/ra_analysis/src')
-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
3 files changed, 104 insertions, 4 deletions
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}