mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 11:59:49 +00:00
Resolve tests per file instead of per crate in test explorer
This commit is contained in:
parent
ad51a17c62
commit
beec6914c8
8 changed files with 143 additions and 41 deletions
|
@ -353,6 +353,10 @@ impl Analysis {
|
|||
self.with_db(|db| test_explorer::discover_tests_in_crate(db, crate_id))
|
||||
}
|
||||
|
||||
pub fn discover_tests_in_file(&self, file_id: FileId) -> Cancellable<Vec<TestItem>> {
|
||||
self.with_db(|db| test_explorer::discover_tests_in_file(db, file_id))
|
||||
}
|
||||
|
||||
/// Renders the crate graph to GraphViz "dot" syntax.
|
||||
pub fn view_crate_graph(&self, full: bool) -> Cancellable<Result<String, String>> {
|
||||
self.with_db(|db| view_crate_graph::view_crate_graph(db, full))
|
||||
|
|
|
@ -7,7 +7,7 @@ use ide_db::{
|
|||
};
|
||||
use syntax::TextRange;
|
||||
|
||||
use crate::{navigation_target::ToNav, runnables::runnable_fn, Runnable, TryToNav};
|
||||
use crate::{runnables::runnable_fn, NavigationTarget, Runnable, TryToNav};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TestItemKind {
|
||||
|
@ -56,7 +56,12 @@ fn find_crate_by_id(crate_graph: &CrateGraph, crate_id: &str) -> Option<CrateId>
|
|||
})
|
||||
}
|
||||
|
||||
fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String) -> Vec<TestItem> {
|
||||
fn discover_tests_in_module(
|
||||
db: &RootDatabase,
|
||||
module: Module,
|
||||
prefix_id: String,
|
||||
only_in_this_file: bool,
|
||||
) -> Vec<TestItem> {
|
||||
let sema = Semantics::new(db);
|
||||
|
||||
let mut r = vec![];
|
||||
|
@ -64,9 +69,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String
|
|||
let module_name =
|
||||
c.name(db).as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]").to_owned();
|
||||
let module_id = format!("{prefix_id}::{module_name}");
|
||||
let module_children = discover_tests_in_module(db, c, module_id.clone());
|
||||
let module_children = discover_tests_in_module(db, c, module_id.clone(), only_in_this_file);
|
||||
if !module_children.is_empty() {
|
||||
let nav = c.to_nav(db).call_site;
|
||||
let nav = NavigationTarget::from_module_to_decl(sema.db, c).call_site;
|
||||
r.push(TestItem {
|
||||
id: module_id,
|
||||
kind: TestItemKind::Module,
|
||||
|
@ -76,7 +81,9 @@ fn discover_tests_in_module(db: &RootDatabase, module: Module, prefix_id: String
|
|||
text_range: Some(nav.focus_or_full_range()),
|
||||
runnable: None,
|
||||
});
|
||||
r.extend(module_children);
|
||||
if !only_in_this_file || c.is_inline(db) {
|
||||
r.extend(module_children);
|
||||
}
|
||||
}
|
||||
}
|
||||
for def in module.declarations(db) {
|
||||
|
@ -112,6 +119,55 @@ pub(crate) fn discover_tests_in_crate_by_test_id(
|
|||
discover_tests_in_crate(db, crate_id)
|
||||
}
|
||||
|
||||
pub(crate) fn discover_tests_in_file(db: &RootDatabase, file_id: FileId) -> Vec<TestItem> {
|
||||
let sema = Semantics::new(db);
|
||||
|
||||
let Some(module) = sema.file_to_module_def(file_id) else { return vec![] };
|
||||
let Some((mut tests, id)) = find_module_id_and_test_parents(&sema, module) else {
|
||||
return vec![];
|
||||
};
|
||||
tests.extend(discover_tests_in_module(db, module, id, true));
|
||||
tests
|
||||
}
|
||||
|
||||
fn find_module_id_and_test_parents(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
module: Module,
|
||||
) -> Option<(Vec<TestItem>, String)> {
|
||||
let Some(parent) = module.parent(sema.db) else {
|
||||
let name = module.krate().display_name(sema.db)?.to_string();
|
||||
return Some((
|
||||
vec![TestItem {
|
||||
id: name.clone(),
|
||||
kind: TestItemKind::Crate(module.krate().into()),
|
||||
label: name.clone(),
|
||||
parent: None,
|
||||
file: None,
|
||||
text_range: None,
|
||||
runnable: None,
|
||||
}],
|
||||
name,
|
||||
));
|
||||
};
|
||||
let (mut r, mut id) = find_module_id_and_test_parents(sema, parent)?;
|
||||
let parent = Some(id.clone());
|
||||
id += "::";
|
||||
let module_name = &module.name(sema.db);
|
||||
let module_name = module_name.as_ref().and_then(|n| n.as_str()).unwrap_or("[mod without name]");
|
||||
id += module_name;
|
||||
let nav = NavigationTarget::from_module_to_decl(sema.db, module).call_site;
|
||||
r.push(TestItem {
|
||||
id: id.clone(),
|
||||
kind: TestItemKind::Module,
|
||||
label: module_name.to_owned(),
|
||||
parent,
|
||||
file: Some(nav.file_id),
|
||||
text_range: Some(nav.focus_or_full_range()),
|
||||
runnable: None,
|
||||
});
|
||||
Some((r, id))
|
||||
}
|
||||
|
||||
pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> Vec<TestItem> {
|
||||
let crate_graph = db.crate_graph();
|
||||
if !crate_graph[crate_id].origin.is_local() {
|
||||
|
@ -133,6 +189,6 @@ pub(crate) fn discover_tests_in_crate(db: &RootDatabase, crate_id: CrateId) -> V
|
|||
text_range: None,
|
||||
runnable: None,
|
||||
}];
|
||||
r.extend(discover_tests_in_module(db, module, crate_test_id));
|
||||
r.extend(discover_tests_in_module(db, module, crate_test_id, false));
|
||||
r
|
||||
}
|
||||
|
|
|
@ -238,9 +238,12 @@ pub(crate) fn handle_discover_test(
|
|||
let (tests, scope) = match params.test_id {
|
||||
Some(id) => {
|
||||
let crate_id = id.split_once("::").map(|it| it.0).unwrap_or(&id);
|
||||
(snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?, vec![crate_id.to_owned()])
|
||||
(
|
||||
snap.analysis.discover_tests_in_crate_by_test_id(crate_id)?,
|
||||
Some(vec![crate_id.to_owned()]),
|
||||
)
|
||||
}
|
||||
None => (snap.analysis.discover_test_roots()?, vec![]),
|
||||
None => (snap.analysis.discover_test_roots()?, None),
|
||||
};
|
||||
for t in &tests {
|
||||
hack_recover_crate_name::insert_name(t.id.clone());
|
||||
|
@ -254,6 +257,7 @@ pub(crate) fn handle_discover_test(
|
|||
})
|
||||
.collect(),
|
||||
scope,
|
||||
scope_file: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -194,7 +194,8 @@ pub struct TestItem {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DiscoverTestResults {
|
||||
pub tests: Vec<TestItem>,
|
||||
pub scope: Vec<String>,
|
||||
pub scope: Option<Vec<String>>,
|
||||
pub scope_file: Option<Vec<TextDocumentIdentifier>>,
|
||||
}
|
||||
|
||||
pub enum DiscoverTest {}
|
||||
|
|
|
@ -9,9 +9,8 @@ use std::{
|
|||
use always_assert::always;
|
||||
use crossbeam_channel::{never, select, Receiver};
|
||||
use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
|
||||
use itertools::Itertools;
|
||||
use lsp_server::{Connection, Notification, Request};
|
||||
use lsp_types::notification::Notification as _;
|
||||
use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
|
||||
use stdx::thread::ThreadIntent;
|
||||
use vfs::FileId;
|
||||
|
||||
|
@ -533,22 +532,14 @@ impl GlobalState {
|
|||
let snapshot = self.snapshot();
|
||||
move || {
|
||||
let tests = subscriptions
|
||||
.into_iter()
|
||||
.filter_map(|f| snapshot.analysis.crates_for(f).ok())
|
||||
.flatten()
|
||||
.unique()
|
||||
.filter_map(|c| snapshot.analysis.discover_tests_in_crate(c).ok())
|
||||
.iter()
|
||||
.copied()
|
||||
.filter_map(|f| snapshot.analysis.discover_tests_in_file(f).ok())
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
for t in &tests {
|
||||
hack_recover_crate_name::insert_name(t.id.clone());
|
||||
}
|
||||
let scope = tests
|
||||
.iter()
|
||||
.filter_map(|t| Some(t.id.split_once("::")?.0))
|
||||
.unique()
|
||||
.map(|it| it.to_owned())
|
||||
.collect();
|
||||
Task::DiscoverTest(lsp_ext::DiscoverTestResults {
|
||||
tests: tests
|
||||
.into_iter()
|
||||
|
@ -557,7 +548,13 @@ impl GlobalState {
|
|||
to_proto::test_item(&snapshot, t, line_index.as_ref())
|
||||
})
|
||||
.collect(),
|
||||
scope,
|
||||
scope: None,
|
||||
scope_file: Some(
|
||||
subscriptions
|
||||
.into_iter()
|
||||
.map(|f| TextDocumentIdentifier { uri: to_proto::url(&snapshot, f) })
|
||||
.collect(),
|
||||
),
|
||||
})
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue