From 8a2803d9aea5099fcbb2b7f7b32cb3de60ab9e01 Mon Sep 17 00:00:00 2001 From: Mathew Horner Date: Sun, 11 Sep 2022 22:40:33 -0500 Subject: [PATCH] Allow configuration of annotation location. Previously, annotations would only appear above the name of an item (function signature, struct declaration, etc). Now, rust-analyzer can be configured to show annotations either above the name or above the whole item (including doc comments and attributes). --- crates/ide/src/annotations.rs | 100 ++++++++++++++++++++++----- crates/ide/src/lib.rs | 2 +- crates/rust-analyzer/src/config.rs | 30 ++++++++ crates/rust-analyzer/src/handlers.rs | 1 + docs/user/generated_config.adoc | 5 ++ editors/code/package.json | 13 ++++ 6 files changed, 132 insertions(+), 19 deletions(-) diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 210c5c7fac..7019658a16 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs @@ -41,6 +41,12 @@ pub struct AnnotationConfig { pub annotate_references: bool, pub annotate_method_references: bool, pub annotate_enum_variant_references: bool, + pub annotation_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( db: &RootDatabase, + config: &AnnotationConfig, node: InFile, source_file_id: FileId, ) -> Option { 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.annotation_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, + annotation_location: AnnotationLocation::AboveName, + }; + + fn check(ra_fixture: &str, expect: Expect, config: &AnnotationConfig) { let (analysis, file_id) = fixture::file(ra_fixture); let annotations: Vec = 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()) @@ -286,6 +303,7 @@ fn main() { }, ] "#]], + &DEFAULT_CONFIG, ); } @@ -362,6 +380,7 @@ fn main() { }, ] "#]], + &DEFAULT_CONFIG, ); } @@ -497,6 +516,7 @@ fn main() { }, ] "#]], + &DEFAULT_CONFIG, ); } @@ -540,6 +560,7 @@ fn main() {} }, ] "#]], + &DEFAULT_CONFIG, ); } @@ -654,6 +675,7 @@ fn main() { }, ] "#]], + &DEFAULT_CONFIG, ); } @@ -750,6 +772,7 @@ mod tests { }, ] "#]], + &DEFAULT_CONFIG, ); } @@ -765,6 +788,7 @@ struct Foo; expect![[r#" [] "#]], + &DEFAULT_CONFIG, ); } @@ -784,6 +808,46 @@ m!(); expect![[r#" [] "#]], + &DEFAULT_CONFIG, + ); + } + + #[test] + fn test_annotations_appear_above_whole_item_when_configured_to_do_so() { + check( + 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 { + annotation_location: AnnotationLocation::AboveWholeItem, + ..DEFAULT_CONFIG + }, ); } } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 0552330814..c1ef25b592 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -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}, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 54dcb42d99..6c6ad5f43a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -307,6 +307,8 @@ config_data! { /// Join lines unwraps trivial blocks. joinLines_unwrapTrivialBlock: bool = "true", + /// Where to render annotations. + lens_annotationLocation: AnnotationLocation = "\"above_name\"", /// Whether to show `Debug` lens. Only applies when /// `#rust-analyzer.lens.enable#` is set. lens_debug_enable: bool = "true", @@ -494,6 +496,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 annotation_location: AnnotationLocation, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum AnnotationLocation { + AboveName, + AboveWholeItem, +} + +impl From for ide::AnnotationLocation { + fn from(location: AnnotationLocation) -> Self { + match location { + AnnotationLocation::AboveName => ide::AnnotationLocation::AboveName, + AnnotationLocation::AboveWholeItem => ide::AnnotationLocation::AboveWholeItem, + } + } } impl LensConfig { @@ -1185,6 +1206,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, + annotation_location: self.data.lens_annotationLocation, } } @@ -1921,6 +1943,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), } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e79cf3d3fd..edac9de69a 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1234,6 +1234,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, + annotation_location: lens_config.annotation_location.into(), }, file_id, )?; diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 72b9257264..3cd49bc5d8 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -451,6 +451,11 @@ Join lines removes trailing commas. -- Join lines unwraps trivial blocks. -- +[[rust-analyzer.lens.annotation.location]]rust-analyzer.lens.annotation.location (default: `above_name`):: ++ +-- +Where to render annotations. +-- [[rust-analyzer.lens.debug.enable]]rust-analyzer.lens.debug.enable (default: `true`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 767c5875bf..465d866021 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -943,6 +943,19 @@ "default": true, "type": "boolean" }, + "rust-analyzer.lens.annotationLocation": { + "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.debug.enable": { "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", "default": true,