diff --git a/Cargo.lock b/Cargo.lock index 6435bfb47b..5f82e92f40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -667,6 +667,7 @@ name = "ra_analysis" version = "0.1.0" dependencies = [ "fst 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "ra_db 0.1.0", diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml index a5d4f65ab8..c0174cdc50 100644 --- a/crates/ra_analysis/Cargo.toml +++ b/crates/ra_analysis/Cargo.toml @@ -5,6 +5,7 @@ version = "0.1.0" authors = ["Aleksey Kladov "] [dependencies] +itertools = "0.8.0" log = "0.4.5" relative-path = "0.4.0" rayon = "1.0.2" @@ -12,6 +13,7 @@ fst = "0.3.1" salsa = "0.9.0" rustc-hash = "1.0" parking_lot = "0.7.0" + ra_syntax = { path = "../ra_syntax" } ra_editor = { path = "../ra_editor" } ra_text_edit = { path = "../ra_text_edit" } diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 0faf8b85d9..ff13247de3 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs @@ -100,27 +100,6 @@ impl db::RootDatabase { } impl db::RootDatabase { - pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable> { - let descr = match source_binder::module_from_position(self, position)? { - None => return Ok(None), - Some(it) => it, - }; - let name = match descr.name() { - None => return Ok(None), - Some(it) => it.to_string(), - }; - - let modules = descr.path_to_root(); - - let path = modules - .iter() - .filter_map(|s| s.name()) - .skip(1) // name is already part of the string. - .fold(name, |path, it| format!("{}::{}", it, path)); - - Ok(Some(path.to_string())) - } - /// This returns `Vec` because a module may be included from several places. We /// don't handle this case yet though, so the Vec has length at most one. pub(crate) fn parent_module( diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index 54eb2f4d3a..a01febf4e0 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs @@ -382,10 +382,6 @@ impl Analysis { pub fn parent_module(&self, position: FilePosition) -> Cancelable> { self.db.parent_module(position) } - /// Returns `::` separated path to the current module from the crate root. - pub fn module_path(&self, position: FilePosition) -> Cancelable> { - self.db.module_path(position) - } /// Returns crates this file belongs too. pub fn crate_for(&self, file_id: FileId) -> Cancelable> { self.db.crate_for(file_id) @@ -396,8 +392,7 @@ impl Analysis { } /// Returns the set of possible targets to run for the current file. pub fn runnables(&self, file_id: FileId) -> Cancelable> { - let file = self.db.source_file(file_id); - Ok(runnables::runnables(self, &file, file_id)) + runnables::runnables(&*self.db, file_id) } /// Computes syntax highlighting for the given file. pub fn highlight(&self, file_id: FileId) -> Cancelable> { diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs index 61ca0930a7..4742676059 100644 --- a/crates/ra_analysis/src/runnables.rs +++ b/crates/ra_analysis/src/runnables.rs @@ -1,11 +1,11 @@ +use itertools::Itertools; use ra_syntax::{ ast::{self, AstNode, NameOwner, ModuleItemOwner}, - SourceFileNode, TextRange, SyntaxNodeRef, - TextUnit, -}; -use crate::{ - Analysis, FileId, FilePosition + TextRange, SyntaxNodeRef, }; +use ra_db::{Cancelable, SyntaxDatabase}; + +use crate::{db::RootDatabase, FileId}; #[derive(Debug)] pub struct Runnable { @@ -20,53 +20,67 @@ pub enum RunnableKind { Bin, } -pub fn runnables( - analysis: &Analysis, - file_node: &SourceFileNode, - file_id: FileId, -) -> Vec { - file_node +pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Cancelable> { + let source_file = db.source_file(file_id); + let res = source_file .syntax() .descendants() - .filter_map(|i| runnable(analysis, i, file_id)) - .collect() + .filter_map(|i| runnable(db, file_id, i)) + .collect(); + Ok(res) } -fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option { - if let Some(f) = ast::FnDef::cast(item) { - let name = f.name()?.text(); - let kind = if name == "main" { - RunnableKind::Bin - } else if f.has_atom_attr("test") { - RunnableKind::Test { - name: name.to_string(), - } - } else { - return None; - }; - Some(Runnable { - range: f.syntax().range(), - kind, - }) +fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNodeRef) -> Option { + if let Some(fn_def) = ast::FnDef::cast(item) { + runnable_fn(fn_def) } else if let Some(m) = ast::Module::cast(item) { - if m.item_list()? - .items() - .map(ast::ModuleItem::syntax) - .filter_map(ast::FnDef::cast) - .any(|f| f.has_atom_attr("test")) - { - let postition = FilePosition { - file_id, - offset: m.syntax().range().start() + TextUnit::from_usize(1), - }; - analysis.module_path(postition).ok()?.map(|path| Runnable { - range: m.syntax().range(), - kind: RunnableKind::TestMod { path }, - }) - } else { - None - } + runnable_mod(db, file_id, m) } else { None } } + +fn runnable_fn(fn_def: ast::FnDef) -> Option { + let name = fn_def.name()?.text(); + let kind = if name == "main" { + RunnableKind::Bin + } else if fn_def.has_atom_attr("test") { + RunnableKind::Test { + name: name.to_string(), + } + } else { + return None; + }; + Some(Runnable { + range: fn_def.syntax().range(), + kind, + }) +} + +fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option { + let has_test_function = module + .item_list()? + .items() + .filter_map(|it| match it { + ast::ModuleItem::FnDef(it) => Some(it), + _ => None, + }) + .any(|f| f.has_atom_attr("test")); + if !has_test_function { + return None; + } + let range = module.syntax().range(); + let module = + hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??; + let path = module + .path_to_root() + .into_iter() + .rev() + .into_iter() + .filter_map(|it| it.name().map(Clone::clone)) + .join("::"); + Some(Runnable { + range, + kind: RunnableKind::TestMod { path }, + }) +} diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index 845fff3c6d..3045c2e781 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs @@ -131,56 +131,6 @@ fn test_resolve_parent_module_for_inline() { ); } -#[test] -fn test_path_one_layer() { - let (analysis, pos) = analysis_and_position( - " - //- /lib.rs - mod foo; - //- /foo/mod.rs - mod bla; - //- /foo/bla.rs - <|> //empty - ", - ); - let symbols = analysis.module_path(pos).unwrap().unwrap(); - assert_eq!("foo::bla", &symbols); -} - -#[test] -fn test_path_two_layer() { - let (analysis, pos) = analysis_and_position( - " - //- /lib.rs - mod foo; - //- /foo/mod.rs - mod bla; - //- /foo/bla/mod.rs - mod more; - //- /foo/bla/more.rs - <|> //empty - ", - ); - let symbols = analysis.module_path(pos).unwrap().unwrap(); - assert_eq!("foo::bla::more", &symbols); -} - -#[test] -fn test_path_in_file_mod() { - let (analysis, pos) = analysis_and_position( - " - //- /lib.rs - mod foo; - //- /foo.rs - mod bar { - <|> //empty - } - ", - ); - let symbols = analysis.module_path(pos).unwrap().unwrap(); - assert_eq!("foo::bar", &symbols); -} - #[test] fn test_resolve_crate_root() { let mock = MockAnalysis::with_files( diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 26171d27c3..a53b69d203 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -80,7 +80,7 @@ impl Module { Some(Crate::new(crate_id)) } - /// Returns the all modulkes on the way to the root. + /// Returns the all modules on the way to the root. pub fn path_to_root(&self) -> Vec { generate(Some(self.clone()), move |it| it.parent()).collect::>() }