From 801a2231bf154a720db97d50e8e581d1f8666e2a Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Thu, 1 Dec 2022 19:09:57 +0330 Subject: [PATCH 1/2] Implement location link for type inlay hints --- crates/hir-ty/src/display.rs | 54 +++++-- crates/hir/src/lib.rs | 10 +- crates/ide/src/inlay_hints.rs | 128 +++++++++++++--- crates/ide/src/inlay_hints/bind_pat.rs | 58 ++++--- crates/ide/src/inlay_hints/chaining.rs | 179 +++++++++++++++++++--- crates/ide/src/inlay_hints/closure_ret.rs | 15 +- 6 files changed, 362 insertions(+), 82 deletions(-) diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 9d453eef71689..57a15d114f071 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -16,7 +16,7 @@ use hir_def::{ path::{Path, PathKind}, type_ref::{ConstScalar, TraitBoundModifier, TypeBound, TypeRef}, visibility::Visibility, - HasModule, ItemContainerId, Lookup, ModuleId, TraitId, + HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, TraitId, }; use hir_expand::{hygiene::Hygiene, name::Name}; use itertools::Itertools; @@ -35,9 +35,27 @@ use crate::{ TraitRefExt, Ty, TyExt, TyKind, WhereClause, }; +pub trait HirWrite: fmt::Write { + fn start_location_link(&mut self, location: ModuleDefId); + fn end_location_link(&mut self); +} + +// String will ignore link metadata +impl HirWrite for String { + fn start_location_link(&mut self, _: ModuleDefId) {} + + fn end_location_link(&mut self) {} +} + +// `core::Formatter` will ignore metadata +impl HirWrite for fmt::Formatter<'_> { + fn start_location_link(&mut self, _: ModuleDefId) {} + fn end_location_link(&mut self) {} +} + pub struct HirFormatter<'a> { pub db: &'a dyn HirDatabase, - fmt: &'a mut dyn fmt::Write, + fmt: &'a mut dyn HirWrite, buf: String, curr_size: usize, pub(crate) max_size: Option, @@ -45,6 +63,16 @@ pub struct HirFormatter<'a> { display_target: DisplayTarget, } +impl HirFormatter<'_> { + fn start_location_link(&mut self, location: ModuleDefId) { + self.fmt.start_location_link(location); + } + + fn end_location_link(&mut self) { + self.fmt.end_location_link(); + } +} + pub trait HirDisplay { fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError>; @@ -245,12 +273,9 @@ pub struct HirDisplayWrapper<'a, T> { display_target: DisplayTarget, } -impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> -where - T: HirDisplay, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.t.hir_fmt(&mut HirFormatter { +impl HirDisplayWrapper<'_, T> { + pub fn write_to(&self, f: &mut F) -> Result<(), HirDisplayError> { + self.t.hir_fmt(&mut HirFormatter { db: self.db, fmt: f, buf: String::with_capacity(20), @@ -258,7 +283,16 @@ where max_size: self.max_size, omit_verbose_types: self.omit_verbose_types, display_target: self.display_target, - }) { + }) + } +} + +impl<'a, T> fmt::Display for HirDisplayWrapper<'a, T> +where + T: HirDisplay, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.write_to(f) { Ok(()) => Ok(()), Err(HirDisplayError::FmtError) => Err(fmt::Error), Err(HirDisplayError::DisplaySourceCodeError(_)) => { @@ -530,6 +564,7 @@ impl HirDisplay for Ty { } } TyKind::Adt(AdtId(def_id), parameters) => { + f.start_location_link((*def_id).into()); match f.display_target { DisplayTarget::Diagnostics | DisplayTarget::Test => { let name = match *def_id { @@ -554,6 +589,7 @@ impl HirDisplay for Ty { } } } + f.end_location_link(); if parameters.len(Interner) > 0 { let parameters_to_write = if f.display_target.is_source_code() diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 316f3938c6ccc..80b3a7b02bcb8 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -114,12 +114,20 @@ pub use { path::{ModPath, PathKind}, type_ref::{Mutability, TypeRef}, visibility::Visibility, + // FIXME: This is here since it is input of a method in `HirWrite` + // and things outside of hir need to implement that trait. We probably + // should move whole `hir_ty::display` to this crate so we will become + // able to use `ModuleDef` or `Definition` instead of `ModuleDefId`. + ModuleDefId, }, hir_expand::{ name::{known, Name}, ExpandResult, HirFileId, InFile, MacroFile, Origin, }, - hir_ty::{display::HirDisplay, PointerCast, Safety}, + hir_ty::{ + display::{HirDisplay, HirWrite}, + PointerCast, Safety, + }, }; // These are negative re-exports: pub using these names is forbidden, they diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 152f31b3a5733..8163697fbfcb8 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,15 +1,19 @@ -use std::fmt; +use std::{ + fmt::{self, Write}, + mem::take, +}; use either::Either; -use hir::{known, HasVisibility, HirDisplay, Semantics}; +use hir::{known, HasVisibility, HirDisplay, HirWrite, ModuleDef, ModuleDefId, Semantics}; use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; use itertools::Itertools; +use stdx::never; use syntax::{ ast::{self, AstNode}, match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, }; -use crate::FileId; +use crate::{navigation_target::TryToNav, FileId}; mod closing_brace; mod implicit_static; @@ -89,6 +93,7 @@ pub enum InlayTooltip { HoverOffset(FileId, TextSize), } +#[derive(Default)] pub struct InlayHintLabel { pub parts: Vec, } @@ -172,6 +177,96 @@ impl fmt::Debug for InlayHintLabelPart { } } +#[derive(Debug)] +struct InlayHintLabelBuilder<'a> { + db: &'a RootDatabase, + result: InlayHintLabel, + last_part: String, + location: Option, +} + +impl fmt::Write for InlayHintLabelBuilder<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.last_part.write_str(s) + } +} + +impl HirWrite for InlayHintLabelBuilder<'_> { + fn start_location_link(&mut self, def: ModuleDefId) { + if self.location.is_some() { + never!("location link is already started"); + } + self.make_new_part(); + let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = + FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; + self.location = Some(location); + } + + fn end_location_link(&mut self) { + self.make_new_part(); + } +} + +impl InlayHintLabelBuilder<'_> { + fn make_new_part(&mut self) { + self.result.parts.push(InlayHintLabelPart { + text: take(&mut self.last_part), + linked_location: self.location.take(), + }); + } + + fn finish(mut self) -> InlayHintLabel { + self.make_new_part(); + self.result + } +} + +fn label_of_ty( + sema: &Semantics<'_, RootDatabase>, + desc_pat: &impl AstNode, + config: &InlayHintsConfig, + ty: hir::Type, +) -> Option { + fn rec( + sema: &Semantics<'_, RootDatabase>, + famous_defs: &FamousDefs<'_, '_>, + mut max_length: Option, + ty: hir::Type, + label_builder: &mut InlayHintLabelBuilder<'_>, + ) { + let iter_item_type = hint_iterator(sema, &famous_defs, &ty); + match iter_item_type { + Some(ty) => { + const LABEL_START: &str = "impl Iterator { + let _ = ty.display_truncated(sema.db, max_length).write_to(label_builder); + } + }; + } + + let krate = sema.scope(desc_pat.syntax())?.krate(); + let famous_defs = FamousDefs(sema, krate); + let mut label_builder = InlayHintLabelBuilder { + db: sema.db, + last_part: String::new(), + location: None, + result: InlayHintLabel::default(), + }; + rec(sema, &famous_defs, config.max_length, ty, &mut label_builder); + let r = label_builder.finish(); + Some(r) +} + // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -224,7 +319,7 @@ pub(crate) fn inlay_hints( fn hints( hints: &mut Vec, - famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: FileId, node: SyntaxNode, @@ -233,14 +328,14 @@ fn hints( match_ast! { match node { ast::Expr(expr) => { - chaining::hints(hints, sema, &famous_defs, config, file_id, &expr); + chaining::hints(hints, sema, config, file_id, &expr); adjustment::hints(hints, sema, config, &expr); match expr { ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { param_name::hints(hints, sema, config, ast::Expr::from(it)) } - ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, sema, &famous_defs, config, file_id, it), + ast::Expr::ClosureExpr(it) => closure_ret::hints(hints, sema, config, file_id, it), // We could show reborrows for all expressions, but usually that is just noise to the user // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr), @@ -270,13 +365,12 @@ fn hints( }; } -/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator`. +/// Checks if the type is an Iterator from std::iter and returns its item type. fn hint_iterator( sema: &Semantics<'_, RootDatabase>, famous_defs: &FamousDefs<'_, '_>, - config: &InlayHintsConfig, ty: &hir::Type, -) -> Option { +) -> Option { let db = sema.db; let strukt = ty.strip_references().as_adt()?; let krate = strukt.module(db).krate(); @@ -299,21 +393,7 @@ fn hint_iterator( _ => None, })?; if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) { - const LABEL_START: &str = "impl Iterator i32 { a + b } //! let _x /* i32 */= f(4, 4); //! ``` -use hir::{HirDisplay, Semantics, TypeInfo}; -use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; +use hir::{Semantics, TypeInfo}; +use ide_db::{base_db::FileId, RootDatabase}; use itertools::Itertools; use syntax::{ @@ -13,10 +13,11 @@ use syntax::{ }; use crate::{ - inlay_hints::{closure_has_block_body, hint_iterator}, - InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, + inlay_hints::closure_has_block_body, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, }; +use super::label_of_ty; + pub(super) fn hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, @@ -36,22 +37,13 @@ pub(super) fn hints( return None; } - let krate = sema.scope(desc_pat.syntax())?.krate(); - let famous_defs = FamousDefs(sema, krate); - let label = hint_iterator(sema, &famous_defs, config, &ty); + let label = label_of_ty(sema, desc_pat, config, ty)?; - let label = match label { - Some(label) => label, - None => { - let ty_name = ty.display_truncated(sema.db, config.max_length).to_string(); - if config.hide_named_constructor_hints - && is_named_constructor(sema, pat, &ty_name).is_some() - { - return None; - } - ty_name - } - }; + if config.hide_named_constructor_hints + && is_named_constructor(sema, pat, &label.to_string()).is_some() + { + return None; + } acc.push(InlayHint { range: match pat.name() { @@ -59,7 +51,7 @@ pub(super) fn hints( None => pat.syntax().text_range(), }, kind: InlayKind::TypeHint, - label: label.into(), + label, tooltip: pat .name() .map(|it| it.syntax().text_range()) @@ -346,7 +338,31 @@ fn main(a: SliceIter<'_, Container>) { range: 484..485, kind: ChainingHint, label: [ - "SliceIter", + "", + InlayHintLabelPart { + text: "SliceIter", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 289..298, + }, + ), + }, + "<", + InlayHintLabelPart { + text: "Container", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 238..247, + }, + ), + }, + ">", ], tooltip: Some( HoverRanged( diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 32421afd39ff3..c9aabcbb044a3 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -1,19 +1,18 @@ //! Implementation of "chaining" inlay hints. -use hir::{HirDisplay, Semantics}; -use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use hir::Semantics; +use ide_db::RootDatabase; use syntax::{ ast::{self, AstNode}, Direction, NodeOrToken, SyntaxKind, T, }; -use crate::{ - inlay_hints::hint_iterator, FileId, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, -}; +use crate::{FileId, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip}; + +use super::label_of_ty; pub(super) fn hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, - famous_defs: &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: FileId, expr: &ast::Expr, @@ -62,9 +61,7 @@ pub(super) fn hints( acc.push(InlayHint { range: expr.syntax().text_range(), kind: InlayKind::ChainingHint, - label: hint_iterator(sema, &famous_defs, config, &ty) - .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()) - .into(), + label: label_of_ty(sema, desc_expr, config, ty)?, tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())), }); } @@ -110,7 +107,19 @@ fn main() { range: 147..172, kind: ChainingHint, label: [ - "B", + "", + InlayHintLabelPart { + text: "B", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 63..64, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -125,7 +134,19 @@ fn main() { range: 147..154, kind: ChainingHint, label: [ - "A", + "", + InlayHintLabelPart { + text: "A", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -185,7 +206,19 @@ fn main() { range: 143..190, kind: ChainingHint, label: [ - "C", + "", + InlayHintLabelPart { + text: "C", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -200,7 +233,19 @@ fn main() { range: 143..179, kind: ChainingHint, label: [ - "B", + "", + InlayHintLabelPart { + text: "B", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -245,7 +290,31 @@ fn main() { range: 246..283, kind: ChainingHint, label: [ - "B>", + "", + InlayHintLabelPart { + text: "B", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 23..24, + }, + ), + }, + "<", + InlayHintLabelPart { + text: "X", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), + }, + ">", ], tooltip: Some( HoverRanged( @@ -260,7 +329,31 @@ fn main() { range: 246..265, kind: ChainingHint, label: [ - "A>", + "", + InlayHintLabelPart { + text: "A", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), + }, + "<", + InlayHintLabelPart { + text: "X", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), + }, + ">", ], tooltip: Some( HoverRanged( @@ -352,7 +445,19 @@ fn main() { range: 174..189, kind: ChainingHint, label: [ - "&mut MyIter", + "&mut ", + InlayHintLabelPart { + text: "MyIter", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 24..30, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -396,7 +501,19 @@ fn main() { range: 124..130, kind: TypeHint, label: [ - "Struct", + "", + InlayHintLabelPart { + text: "Struct", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -411,7 +528,19 @@ fn main() { range: 145..185, kind: ChainingHint, label: [ - "Struct", + "", + InlayHintLabelPart { + text: "Struct", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( @@ -426,7 +555,19 @@ fn main() { range: 145..168, kind: ChainingHint, label: [ - "Struct", + "", + InlayHintLabelPart { + text: "Struct", + linked_location: Some( + FileRange { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), + }, + "", ], tooltip: Some( HoverRanged( diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index de04f3ac75169..247a4abcc5654 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs @@ -1,17 +1,18 @@ //! Implementation of "closure return type" inlay hints. -use hir::{HirDisplay, Semantics}; -use ide_db::{base_db::FileId, famous_defs::FamousDefs, RootDatabase}; +use hir::Semantics; +use ide_db::{base_db::FileId, RootDatabase}; use syntax::ast::{self, AstNode}; use crate::{ - inlay_hints::{closure_has_block_body, hint_iterator}, - ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip, + inlay_hints::closure_has_block_body, ClosureReturnTypeHints, InlayHint, InlayHintsConfig, + InlayKind, InlayTooltip, }; +use super::label_of_ty; + pub(super) fn hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, - famous_defs: &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: FileId, closure: ast::ClosureExpr, @@ -42,9 +43,7 @@ pub(super) fn hints( acc.push(InlayHint { range: param_list.syntax().text_range(), kind: InlayKind::ClosureReturnTypeHint, - label: hint_iterator(sema, &famous_defs, config, &ty) - .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string()) - .into(), + label: label_of_ty(sema, ¶m_list, config, ty)?, tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())), }); Some(()) From e1aa73ef40b8902b2cbdd8272978fcc1c47cf3c7 Mon Sep 17 00:00:00 2001 From: hkalbasi Date: Wed, 21 Dec 2022 18:54:49 +0330 Subject: [PATCH 2/2] Disable inlay hint location links on vscode < 1.76 --- crates/ide/src/inlay_hints.rs | 14 +++- crates/ide/src/inlay_hints/bind_pat.rs | 5 +- crates/ide/src/inlay_hints/chaining.rs | 76 +++++++++++++++++++-- crates/ide/src/inlay_hints/closing_brace.rs | 5 +- crates/ide/src/static_index.rs | 1 + crates/rust-analyzer/src/bin/main.rs | 2 + crates/rust-analyzer/src/config.rs | 18 ++++- docs/user/generated_config.adoc | 5 ++ editors/code/package.json | 5 ++ 9 files changed, 121 insertions(+), 10 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 8163697fbfcb8..9aef78143d6dd 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -27,6 +27,7 @@ mod bind_pat; #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { + pub location_links: bool, pub render_colons: bool, pub type_hints: bool, pub parameter_hints: bool, @@ -182,6 +183,7 @@ struct InlayHintLabelBuilder<'a> { db: &'a RootDatabase, result: InlayHintLabel, last_part: String, + location_link_enabled: bool, location: Option, } @@ -193,6 +195,9 @@ impl fmt::Write for InlayHintLabelBuilder<'_> { impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { + if !self.location_link_enabled { + return; + } if self.location.is_some() { never!("location link is already started"); } @@ -204,6 +209,9 @@ impl HirWrite for InlayHintLabelBuilder<'_> { } fn end_location_link(&mut self) { + if !self.location_link_enabled { + return; + } self.make_new_part(); } } @@ -260,6 +268,7 @@ fn label_of_ty( db: sema.db, last_part: String::new(), location: None, + location_link_enabled: config.location_links, result: InlayHintLabel::default(), }; rec(sema, &famous_defs, config.max_length, ty, &mut label_builder); @@ -416,6 +425,7 @@ mod tests { use super::ClosureReturnTypeHints; pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { + location_links: false, render_colons: false, type_hints: false, parameter_hints: false, @@ -430,6 +440,8 @@ mod tests { max_length: None, closing_brace_hints_min_lines: None, }; + pub(super) const DISABLED_CONFIG_WITH_LINKS: InlayHintsConfig = + InlayHintsConfig { location_links: true, ..DISABLED_CONFIG }; pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { type_hints: true, parameter_hints: true, @@ -437,7 +449,7 @@ mod tests { closure_return_type_hints: ClosureReturnTypeHints::WithBlock, binding_mode_hints: true, lifetime_elision_hints: LifetimeElisionHints::Always, - ..DISABLED_CONFIG + ..DISABLED_CONFIG_WITH_LINKS }; #[track_caller] diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index c9f787e07488c..7766d497918df 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -194,7 +194,8 @@ mod tests { use crate::{fixture, inlay_hints::InlayHintsConfig}; use crate::inlay_hints::tests::{ - check, check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG, + check, check_expect, check_with_config, DISABLED_CONFIG, DISABLED_CONFIG_WITH_LINKS, + TEST_CONFIG, }; use crate::ClosureReturnTypeHints; @@ -290,7 +291,7 @@ fn main() { fn iterator_hint_regression_issue_12674() { // Ensure we don't crash while solving the projection type of iterators. check_expect( - InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG_WITH_LINKS }, r#" //- minicore: iterators struct S(T); diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index c9aabcbb044a3..efeb2b7925502 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -74,7 +74,10 @@ mod tests { use expect_test::expect; use crate::{ - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::tests::{ + check_expect, check_with_config, DISABLED_CONFIG, DISABLED_CONFIG_WITH_LINKS, + TEST_CONFIG, + }, InlayHintsConfig, }; @@ -86,7 +89,11 @@ mod tests { #[test] fn chaining_hints_ignore_comments() { check_expect( - InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG }, + InlayHintsConfig { + type_hints: false, + chaining_hints: true, + ..DISABLED_CONFIG_WITH_LINKS + }, r#" struct A(B); impl A { fn into_b(self) -> B { self.0 } } @@ -179,10 +186,69 @@ fn main() { } #[test] - fn struct_access_chaining_hints() { + fn disabled_location_links() { check_expect( InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, r#" + struct A { pub b: B } + struct B { pub c: C } + struct C(pub bool); + struct D; + + impl D { + fn foo(&self) -> i32 { 42 } + } + + fn main() { + let x = A { b: B { c: C(true) } } + .b + .c + .0; + let x = D + .foo(); + }"#, + expect![[r#" + [ + InlayHint { + range: 143..190, + kind: ChainingHint, + label: [ + "C", + ], + tooltip: Some( + HoverRanged( + FileId( + 0, + ), + 143..190, + ), + ), + }, + InlayHint { + range: 143..179, + kind: ChainingHint, + label: [ + "B", + ], + tooltip: Some( + HoverRanged( + FileId( + 0, + ), + 143..179, + ), + ), + }, + ] + "#]], + ); + } + + #[test] + fn struct_access_chaining_hints() { + check_expect( + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG_WITH_LINKS }, + r#" struct A { pub b: B } struct B { pub c: C } struct C(pub bool); @@ -264,7 +330,7 @@ fn main() { #[test] fn generic_chaining_hints() { check_expect( - InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG_WITH_LINKS }, r#" struct A(T); struct B(T); @@ -372,7 +438,7 @@ fn main() { #[test] fn shorten_iterator_chaining_hints() { check_expect( - InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, + InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG_WITH_LINKS }, r#" //- minicore: iterators use core::iter; diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 57605b392a810..e340c64c54b55 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,7 +109,10 @@ pub(super) fn hints( return None; } - let linked_location = name_range.map(|range| FileRange { file_id, range }); + let linked_location = config + .location_links + .then(|| name_range.map(|range| FileRange { file_id, range })) + .flatten(); acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBraceHint, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 2380cf7381c1c..42b5951c842f2 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -106,6 +106,7 @@ impl StaticIndex<'_> { .analysis .inlay_hints( &InlayHintsConfig { + location_links: true, render_colons: true, type_hints: true, parameter_hints: true, diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 7bf595d2a45f5..ec5053e991d3d 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -183,6 +183,8 @@ fn run_server() -> Result<()> { } } + config.client_specific_adjustments(&initialize_params.client_info); + let server_capabilities = rust_analyzer::server_capabilities(&config); let initialize_result = lsp_types::InitializeResult { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 835b37c98e2c5..0bcc91eb411e9 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -20,7 +20,7 @@ use ide_db::{ SnippetCap, }; use itertools::Itertools; -use lsp_types::{ClientCapabilities, MarkupKind}; +use lsp_types::{ClientCapabilities, ClientInfo, MarkupKind}; use project_model::{ CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource, UnsetTestCrates, @@ -333,6 +333,8 @@ config_data! { inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. inlayHints_lifetimeElisionHints_useParameterNames: bool = "false", + /// Whether to use location links for parts of type mentioned in inlay hints. + inlayHints_locationLinks: bool = "true", /// Maximum length for inlay hints. Set to null to have an unlimited length. inlayHints_maxLength: Option = "25", /// Whether to show function parameter name inlay hints at the call @@ -714,6 +716,19 @@ impl Config { } } + pub fn client_specific_adjustments(&mut self, client_info: &Option) { + // FIXME: remove this when we drop support for vscode 1.65 and below + if let Some(client) = client_info { + if client.name.contains("Code") || client.name.contains("Codium") { + if let Some(version) = &client.version { + if version.as_str() < "1.76" { + self.data.inlayHints_locationLinks = false; + } + } + } + } + } + pub fn update(&mut self, mut json: serde_json::Value) -> Result<(), ConfigUpdateError> { tracing::info!("updating config from JSON: {:#}", json); if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { @@ -1196,6 +1211,7 @@ impl Config { pub fn inlay_hints(&self) -> InlayHintsConfig { InlayHintsConfig { + location_links: self.data.inlayHints_locationLinks, render_colons: self.data.inlayHints_renderColons, type_hints: self.data.inlayHints_typeHints_enable, parameter_hints: self.data.inlayHints_parameterHints_enable, diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index db41c7bf109e2..47511aad0fea2 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -469,6 +469,11 @@ Whether to show inlay type hints for elided lifetimes in function signatures. -- Whether to prefer using parameter names as the name for elided lifetime hints if possible. -- +[[rust-analyzer.inlayHints.locationLinks]]rust-analyzer.inlayHints.locationLinks (default: `true`):: ++ +-- +Whether to use location links for parts of type mentioned in inlay hints. +-- [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `25`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index f9b0e28dadb7d..5b09ee6f7dab5 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -995,6 +995,11 @@ "default": false, "type": "boolean" }, + "rust-analyzer.inlayHints.locationLinks": { + "markdownDescription": "Whether to use location links for parts of type mentioned in inlay hints.", + "default": true, + "type": "boolean" + }, "rust-analyzer.inlayHints.maxLength": { "markdownDescription": "Maximum length for inlay hints. Set to null to have an unlimited length.", "default": 25,