mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
Auto merge of #16441 - Young-Flash:exclude_tests_refs, r=Veykril
feat: enable excluding refs search results in test ## Change Here I introduce a new `ReferenceCategory::Test` type to indicate whether the function where this reference is located is marked as `#[test]`, and expose an config item (`rust-analyzer.references.excludeTests`) to client. I also changed the signature of `ReferenceCategory::new`, adding a `sema: &Semantics<'_, RootDatabase>` param to do some hir analysis. Hope the current implementation is good to go. ## Demo `"rust-analyzer.references.excludeTests": false` ![include](https://github.com/rust-lang/rust-analyzer/assets/71162630/9f1176d4-7b41-4f49-ac79-55d25a42d5d1) `"rust-analyzer.references.excludeTests": true` ![exclude](https://github.com/rust-lang/rust-analyzer/assets/71162630/2938b44b-9e5b-48de-a049-453f5bbc09d0) close https://github.com/rust-lang/rust-analyzer/issues/14530
This commit is contained in:
commit
e4146af294
8 changed files with 91 additions and 8 deletions
|
@ -134,6 +134,7 @@ pub enum ReferenceCategory {
|
||||||
// FIXME: Some day should be able to search in doc comments. Would probably
|
// FIXME: Some day should be able to search in doc comments. Would probably
|
||||||
// need to switch from enum to bitflags then?
|
// need to switch from enum to bitflags then?
|
||||||
// DocComment
|
// DocComment
|
||||||
|
Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generally, `search_scope` returns files that might contain references for the element.
|
/// Generally, `search_scope` returns files that might contain references for the element.
|
||||||
|
@ -743,7 +744,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::NameRef(name_ref.clone()),
|
name: FileReferenceNode::NameRef(name_ref.clone()),
|
||||||
category: ReferenceCategory::new(&def, name_ref),
|
category: ReferenceCategory::new(self.sema, &def, name_ref),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -759,7 +760,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::NameRef(name_ref.clone()),
|
name: FileReferenceNode::NameRef(name_ref.clone()),
|
||||||
category: ReferenceCategory::new(&def, name_ref),
|
category: ReferenceCategory::new(self.sema, &def, name_ref),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
}
|
}
|
||||||
|
@ -769,7 +770,7 @@ impl<'a> FindUsages<'a> {
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
name: FileReferenceNode::NameRef(name_ref.clone()),
|
name: FileReferenceNode::NameRef(name_ref.clone()),
|
||||||
category: ReferenceCategory::new(&def, name_ref),
|
category: ReferenceCategory::new(self.sema, &def, name_ref),
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
sink(file_id, reference)
|
||||||
} else {
|
} else {
|
||||||
|
@ -783,10 +784,10 @@ impl<'a> FindUsages<'a> {
|
||||||
let local = Definition::Local(local);
|
let local = Definition::Local(local);
|
||||||
let access = match self.def {
|
let access = match self.def {
|
||||||
Definition::Field(_) if field == self.def => {
|
Definition::Field(_) if field == self.def => {
|
||||||
ReferenceCategory::new(&field, name_ref)
|
ReferenceCategory::new(self.sema, &field, name_ref)
|
||||||
}
|
}
|
||||||
Definition::Local(_) if local == self.def => {
|
Definition::Local(_) if local == self.def => {
|
||||||
ReferenceCategory::new(&local, name_ref)
|
ReferenceCategory::new(self.sema, &local, name_ref)
|
||||||
}
|
}
|
||||||
_ => return false,
|
_ => return false,
|
||||||
};
|
};
|
||||||
|
@ -871,7 +872,15 @@ fn def_to_ty(sema: &Semantics<'_, RootDatabase>, def: &Definition) -> Option<hir
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReferenceCategory {
|
impl ReferenceCategory {
|
||||||
fn new(def: &Definition, r: &ast::NameRef) -> Option<ReferenceCategory> {
|
fn new(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
def: &Definition,
|
||||||
|
r: &ast::NameRef,
|
||||||
|
) -> Option<ReferenceCategory> {
|
||||||
|
if is_name_ref_in_test(sema, r) {
|
||||||
|
return Some(ReferenceCategory::Test);
|
||||||
|
}
|
||||||
|
|
||||||
// Only Locals and Fields have accesses for now.
|
// Only Locals and Fields have accesses for now.
|
||||||
if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
|
if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
|
||||||
return is_name_ref_in_import(r).then_some(ReferenceCategory::Import);
|
return is_name_ref_in_import(r).then_some(ReferenceCategory::Import);
|
||||||
|
@ -910,3 +919,10 @@ fn is_name_ref_in_import(name_ref: &ast::NameRef) -> bool {
|
||||||
.and_then(|it| it.parent_path().top_path().syntax().parent())
|
.and_then(|it| it.parent_path().top_path().syntax().parent())
|
||||||
.map_or(false, |it| it.kind() == SyntaxKind::USE_TREE)
|
.map_or(false, |it| it.kind() == SyntaxKind::USE_TREE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_name_ref_in_test(sema: &Semantics<'_, RootDatabase>, name_ref: &ast::NameRef) -> bool {
|
||||||
|
name_ref.syntax().ancestors().any(|node| match ast::Fn::cast(node) {
|
||||||
|
Some(it) => sema.to_def(&it).map_or(false, |func| func.is_test(sema.db)),
|
||||||
|
None => false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -519,6 +519,7 @@ mod tests {
|
||||||
ReferenceCategory::Read => "read",
|
ReferenceCategory::Read => "read",
|
||||||
ReferenceCategory::Write => "write",
|
ReferenceCategory::Write => "write",
|
||||||
ReferenceCategory::Import => "import",
|
ReferenceCategory::Import => "import",
|
||||||
|
ReferenceCategory::Test => "test",
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -307,6 +307,51 @@ mod tests {
|
||||||
|
|
||||||
use crate::{fixture, SearchScope};
|
use crate::{fixture, SearchScope};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exclude_tests() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test_func() {}
|
||||||
|
|
||||||
|
fn func() {
|
||||||
|
test_func$0();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
test_func();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
test_func Function FileId(0) 0..17 3..12
|
||||||
|
|
||||||
|
FileId(0) 35..44
|
||||||
|
FileId(0) 75..84 Test
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn test_func() {}
|
||||||
|
|
||||||
|
fn func() {
|
||||||
|
test_func$0();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[::core::prelude::v1::test]
|
||||||
|
fn test() {
|
||||||
|
test_func();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
test_func Function FileId(0) 0..17 3..12
|
||||||
|
|
||||||
|
FileId(0) 35..44
|
||||||
|
FileId(0) 96..105 Test
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_struct_literal_after_space() {
|
fn test_struct_literal_after_space() {
|
||||||
check(
|
check(
|
||||||
|
@ -454,6 +499,7 @@ fn main() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_variant_tuple_before_paren() {
|
fn test_variant_tuple_before_paren() {
|
||||||
check(
|
check(
|
||||||
|
@ -1435,7 +1481,7 @@ fn test$0() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
test Function FileId(0) 0..33 11..15
|
test Function FileId(0) 0..33 11..15
|
||||||
|
|
||||||
FileId(0) 24..28
|
FileId(0) 24..28 Test
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -494,6 +494,9 @@ config_data! {
|
||||||
/// Exclude imports from find-all-references.
|
/// Exclude imports from find-all-references.
|
||||||
references_excludeImports: bool = "false",
|
references_excludeImports: bool = "false",
|
||||||
|
|
||||||
|
/// Exclude tests from find-all-references.
|
||||||
|
references_excludeTests: bool = "false",
|
||||||
|
|
||||||
/// Allow renaming of items not belonging to the loaded workspaces.
|
/// Allow renaming of items not belonging to the loaded workspaces.
|
||||||
rename_allowExternalItems: bool = "false",
|
rename_allowExternalItems: bool = "false",
|
||||||
|
|
||||||
|
@ -1545,6 +1548,10 @@ impl Config {
|
||||||
self.data.references_excludeImports
|
self.data.references_excludeImports
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find_all_refs_exclude_tests(&self) -> bool {
|
||||||
|
self.data.references_excludeTests
|
||||||
|
}
|
||||||
|
|
||||||
pub fn snippet_cap(&self) -> bool {
|
pub fn snippet_cap(&self) -> bool {
|
||||||
self.experimental("snippetTextEdit")
|
self.experimental("snippetTextEdit")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1055,6 +1055,7 @@ pub(crate) fn handle_references(
|
||||||
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
let position = from_proto::file_position(&snap, params.text_document_position)?;
|
||||||
|
|
||||||
let exclude_imports = snap.config.find_all_refs_exclude_imports();
|
let exclude_imports = snap.config.find_all_refs_exclude_imports();
|
||||||
|
let exclude_tests = snap.config.find_all_refs_exclude_tests();
|
||||||
|
|
||||||
let refs = match snap.analysis.find_all_refs(position, None)? {
|
let refs = match snap.analysis.find_all_refs(position, None)? {
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
|
@ -1078,7 +1079,8 @@ pub(crate) fn handle_references(
|
||||||
.flat_map(|(file_id, refs)| {
|
.flat_map(|(file_id, refs)| {
|
||||||
refs.into_iter()
|
refs.into_iter()
|
||||||
.filter(|&(_, category)| {
|
.filter(|&(_, category)| {
|
||||||
!exclude_imports || category != Some(ReferenceCategory::Import)
|
(!exclude_imports || category != Some(ReferenceCategory::Import))
|
||||||
|
&& (!exclude_tests || category != Some(ReferenceCategory::Test))
|
||||||
})
|
})
|
||||||
.map(move |(range, _)| FileRange { file_id, range })
|
.map(move |(range, _)| FileRange { file_id, range })
|
||||||
})
|
})
|
||||||
|
|
|
@ -92,6 +92,7 @@ pub(crate) fn document_highlight_kind(
|
||||||
ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ),
|
ReferenceCategory::Read => Some(lsp_types::DocumentHighlightKind::READ),
|
||||||
ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE),
|
ReferenceCategory::Write => Some(lsp_types::DocumentHighlightKind::WRITE),
|
||||||
ReferenceCategory::Import => None,
|
ReferenceCategory::Import => None,
|
||||||
|
ReferenceCategory::Test => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -777,6 +777,11 @@ Internal config, path to proc-macro server executable.
|
||||||
--
|
--
|
||||||
Exclude imports from find-all-references.
|
Exclude imports from find-all-references.
|
||||||
--
|
--
|
||||||
|
[[rust-analyzer.references.excludeTests]]rust-analyzer.references.excludeTests (default: `false`)::
|
||||||
|
+
|
||||||
|
--
|
||||||
|
Exclude tests from find-all-references.
|
||||||
|
--
|
||||||
[[rust-analyzer.rename.allowExternalItems]]rust-analyzer.rename.allowExternalItems (default: `false`)::
|
[[rust-analyzer.rename.allowExternalItems]]rust-analyzer.rename.allowExternalItems (default: `false`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
@ -1505,6 +1505,11 @@
|
||||||
"default": false,
|
"default": false,
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"rust-analyzer.references.excludeTests": {
|
||||||
|
"markdownDescription": "Exclude tests from find-all-references.",
|
||||||
|
"default": false,
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"rust-analyzer.rename.allowExternalItems": {
|
"rust-analyzer.rename.allowExternalItems": {
|
||||||
"markdownDescription": "Allow renaming of items not belonging to the loaded workspaces.",
|
"markdownDescription": "Allow renaming of items not belonging to the loaded workspaces.",
|
||||||
"default": false,
|
"default": false,
|
||||||
|
|
Loading…
Reference in a new issue