Auto merge of #13221 - mdx97:mdx97/annotations-above-whole-item, r=Veykril

Allow configuration of annotation location.

I've added the ability to configure where lens annotations render relevant to the item they describe. Previously, these would render directly above the line the item is declared on. Now, there is the ability to render these annotations above the entire item (including doc comments, and attributes).

The names of the config options are up for debate, I did what seemed best to me but if anyone has better ideas let me know.

This is my first contribution so if I've missed anything please let me know.

Here's a preview of what the new option looks like:

<img width="577" alt="Screen Shot 2022-09-11 at 10 39 51 PM" src="https://user-images.githubusercontent.com/33100798/189570298-b4fcbf9c-ee49-4b79-aae6-1037ae4f26af.png">

closes https://github.com/rust-lang/rust-analyzer/issues/13218
This commit is contained in:
bors 2022-09-13 15:09:53 +00:00
commit dbb8fedf8b
6 changed files with 126 additions and 19 deletions

View File

@ -41,6 +41,12 @@ pub struct AnnotationConfig {
pub annotate_references: bool,
pub annotate_method_references: bool,
pub annotate_enum_variant_references: bool,
pub location: AnnotationLocation,
}
pub enum AnnotationLocation {
AboveName,
AboveWholeItem,
}
pub(crate) fn annotations(
@ -65,10 +71,10 @@ pub(crate) fn annotations(
visit_file_defs(&Semantics::new(db), file_id, &mut |def| {
let range = match def {
Definition::Const(konst) if config.annotate_references => {
konst.source(db).and_then(|node| name_range(db, node, file_id))
konst.source(db).and_then(|node| name_range(db, config, node, file_id))
}
Definition::Trait(trait_) if config.annotate_references || config.annotate_impls => {
trait_.source(db).and_then(|node| name_range(db, node, file_id))
trait_.source(db).and_then(|node| name_range(db, config, node, file_id))
}
Definition::Adt(adt) => match adt {
hir::Adt::Enum(enum_) => {
@ -77,7 +83,9 @@ pub(crate) fn annotations(
.variants(db)
.into_iter()
.map(|variant| {
variant.source(db).and_then(|node| name_range(db, node, file_id))
variant
.source(db)
.and_then(|node| name_range(db, config, node, file_id))
})
.flatten()
.for_each(|range| {
@ -88,14 +96,14 @@ pub(crate) fn annotations(
})
}
if config.annotate_references || config.annotate_impls {
enum_.source(db).and_then(|node| name_range(db, node, file_id))
enum_.source(db).and_then(|node| name_range(db, config, node, file_id))
} else {
None
}
}
_ => {
if config.annotate_references || config.annotate_impls {
adt.source(db).and_then(|node| name_range(db, node, file_id))
adt.source(db).and_then(|node| name_range(db, config, node, file_id))
} else {
None
}
@ -113,6 +121,7 @@ pub(crate) fn annotations(
annotations
.push(Annotation { range, kind: AnnotationKind::HasImpls { file_id, data: None } });
}
if config.annotate_references {
annotations.push(Annotation {
range,
@ -122,12 +131,18 @@ pub(crate) fn annotations(
fn name_range<T: HasName>(
db: &RootDatabase,
config: &AnnotationConfig,
node: InFile<T>,
source_file_id: FileId,
) -> Option<TextRange> {
if let Some(InFile { file_id, value }) = node.original_ast_node(db) {
if file_id == source_file_id.into() {
return value.name().map(|it| it.syntax().text_range());
return match config.location {
AnnotationLocation::AboveName => {
value.name().map(|name| name.syntax().text_range())
}
AnnotationLocation::AboveWholeItem => Some(value.syntax().text_range()),
};
}
}
None
@ -188,21 +203,23 @@ mod tests {
use crate::{fixture, Annotation, AnnotationConfig};
fn check(ra_fixture: &str, expect: Expect) {
use super::AnnotationLocation;
const DEFAULT_CONFIG: AnnotationConfig = AnnotationConfig {
binary_target: true,
annotate_runnables: true,
annotate_impls: true,
annotate_references: true,
annotate_method_references: true,
annotate_enum_variant_references: true,
location: AnnotationLocation::AboveName,
};
fn check_with_config(ra_fixture: &str, expect: Expect, config: &AnnotationConfig) {
let (analysis, file_id) = fixture::file(ra_fixture);
let annotations: Vec<Annotation> = analysis
.annotations(
&AnnotationConfig {
binary_target: true,
annotate_runnables: true,
annotate_impls: true,
annotate_references: true,
annotate_method_references: true,
annotate_enum_variant_references: true,
},
file_id,
)
.annotations(config, file_id)
.unwrap()
.into_iter()
.map(|annotation| analysis.resolve_annotation(annotation).unwrap())
@ -211,6 +228,10 @@ mod tests {
expect.assert_debug_eq(&annotations);
}
fn check(ra_fixture: &str, expect: Expect) {
check_with_config(ra_fixture, expect, &DEFAULT_CONFIG);
}
#[test]
fn const_annotations() {
check(
@ -786,4 +807,40 @@ m!();
"#]],
);
}
#[test]
fn test_annotations_appear_above_whole_item_when_configured_to_do_so() {
check_with_config(
r#"
/// This is a struct named Foo, obviously.
#[derive(Clone)]
struct Foo;
"#,
expect![[r#"
[
Annotation {
range: 0..71,
kind: HasImpls {
file_id: FileId(
0,
),
data: Some(
[],
),
},
},
Annotation {
range: 0..71,
kind: HasReferences {
file_id: FileId(
0,
),
data: None,
},
},
]
"#]],
&AnnotationConfig { location: AnnotationLocation::AboveWholeItem, ..DEFAULT_CONFIG },
);
}
}

View File

@ -74,7 +74,7 @@ use syntax::SourceFile;
use crate::navigation_target::{ToNav, TryToNav};
pub use crate::{
annotations::{Annotation, AnnotationConfig, AnnotationKind},
annotations::{Annotation, AnnotationConfig, AnnotationKind, AnnotationLocation},
call_hierarchy::CallItem,
expand_macro::ExpandedMacro,
file_structure::{StructureNode, StructureNodeKind},

View File

@ -308,6 +308,7 @@ config_data! {
/// Join lines unwraps trivial blocks.
joinLines_unwrapTrivialBlock: bool = "true",
/// Whether to show `Debug` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_debug_enable: bool = "true",
@ -319,6 +320,8 @@ config_data! {
/// Whether to show `Implementations` lens. Only applies when
/// `#rust-analyzer.lens.enable#` is set.
lens_implementations_enable: bool = "true",
/// Where to render annotations.
lens_location: AnnotationLocation = "\"above_name\"",
/// Whether to show `References` lens for Struct, Enum, and Union.
/// Only applies when `#rust-analyzer.lens.enable#` is set.
lens_references_adt_enable: bool = "false",
@ -498,6 +501,25 @@ pub struct LensConfig {
pub refs_adt: bool, // for Struct, Enum, Union and Trait
pub refs_trait: bool, // for Struct, Enum, Union and Trait
pub enum_variant_refs: bool,
// annotations
pub location: AnnotationLocation,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AnnotationLocation {
AboveName,
AboveWholeItem,
}
impl From<AnnotationLocation> for ide::AnnotationLocation {
fn from(location: AnnotationLocation) -> Self {
match location {
AnnotationLocation::AboveName => ide::AnnotationLocation::AboveName,
AnnotationLocation::AboveWholeItem => ide::AnnotationLocation::AboveWholeItem,
}
}
}
impl LensConfig {
@ -1196,6 +1218,7 @@ impl Config {
refs_trait: self.data.lens_enable && self.data.lens_references_trait_enable,
enum_variant_refs: self.data.lens_enable
&& self.data.lens_references_enumVariant_enable,
location: self.data.lens_location,
}
}
@ -1932,6 +1955,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"Use server-side file watching",
],
},
"AnnotationLocation" => set! {
"type": "string",
"enum": ["above_name", "above_whole_item"],
"enumDescriptions": [
"Render annotations above the name of the item.",
"Render annotations above the whole item, including documentation comments and attributes."
],
},
_ => panic!("missing entry for {}: {}", ty, default),
}

View File

@ -1240,6 +1240,7 @@ pub(crate) fn handle_code_lens(
annotate_references: lens_config.refs_adt,
annotate_method_references: lens_config.method_refs,
annotate_enum_variant_references: lens_config.enum_variant_refs,
location: lens_config.location.into(),
},
file_id,
)?;

View File

@ -479,6 +479,11 @@ client doesn't set the corresponding capability.
Whether to show `Implementations` lens. Only applies when
`#rust-analyzer.lens.enable#` is set.
--
[[rust-analyzer.lens.location]]rust-analyzer.lens.location (default: `"above_name"`)::
+
--
Where to render annotations.
--
[[rust-analyzer.lens.references.adt.enable]]rust-analyzer.lens.references.adt.enable (default: `false`)::
+
--

View File

@ -963,6 +963,19 @@
"default": true,
"type": "boolean"
},
"rust-analyzer.lens.location": {
"markdownDescription": "Where to render annotations.",
"default": "above_name",
"type": "string",
"enum": [
"above_name",
"above_whole_item"
],
"enumDescriptions": [
"Render annotations above the name of the item.",
"Render annotations above the whole item, including documentation comments and attributes."
]
},
"rust-analyzer.lens.references.adt.enable": {
"markdownDescription": "Whether to show `References` lens for Struct, Enum, and Union.\nOnly applies when `#rust-analyzer.lens.enable#` is set.",
"default": false,