Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #13699 - HKalbasi:inlaylink, r=Veykril
Implement location link for type inlay hints fix #11701 This actually doesn't work due a problem in vscode: https://github.com/microsoft/vscode/issues/167564
bors 2022-12-22
parent 113f17b · parent e1aa73e · commit 271f7b4
-rw-r--r--crates/hir-ty/src/display.rs54
-rw-r--r--crates/hir/src/lib.rs10
-rw-r--r--crates/ide/src/inlay_hints.rs142
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs63
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs255
-rw-r--r--crates/ide/src/inlay_hints/closing_brace.rs5
-rw-r--r--crates/ide/src/inlay_hints/closure_ret.rs15
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/rust-analyzer/src/bin/main.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs18
-rw-r--r--docs/user/generated_config.adoc5
-rw-r--r--editors/code/package.json5
12 files changed, 483 insertions, 92 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 9d453eef71..57a15d114f 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<usize>,
@@ -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<T: HirDisplay> HirDisplayWrapper<'_, T> {
+ pub fn write_to<F: HirWrite>(&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 cc983ee014..7e0e024526 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 152f31b3a5..9aef78143d 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;
@@ -23,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,
@@ -89,6 +94,7 @@ pub enum InlayTooltip {
HoverOffset(FileId, TextSize),
}
+#[derive(Default)]
pub struct InlayHintLabel {
pub parts: Vec<InlayHintLabelPart>,
}
@@ -172,6 +178,104 @@ impl fmt::Debug for InlayHintLabelPart {
}
}
+#[derive(Debug)]
+struct InlayHintLabelBuilder<'a> {
+ db: &'a RootDatabase,
+ result: InlayHintLabel,
+ last_part: String,
+ location_link_enabled: bool,
+ location: Option<FileRange>,
+}
+
+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_link_enabled {
+ return;
+ }
+ 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) {
+ if !self.location_link_enabled {
+ return;
+ }
+ 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<InlayHintLabel> {
+ fn rec(
+ sema: &Semantics<'_, RootDatabase>,
+ famous_defs: &FamousDefs<'_, '_>,
+ mut max_length: Option<usize>,
+ 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<Item = ";
+ const LABEL_END: &str = ">";
+
+ max_length =
+ max_length.map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len()));
+
+ label_builder.write_str(LABEL_START).unwrap();
+ rec(sema, famous_defs, max_length, ty, label_builder);
+ label_builder.write_str(LABEL_END).unwrap();
+ }
+ None => {
+ 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,
+ location_link_enabled: config.location_links,
+ 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 +328,7 @@ pub(crate) fn inlay_hints(
fn hints(
hints: &mut Vec<InlayHint>,
- famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: FileId,
node: SyntaxNode,
@@ -233,14 +337,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 +374,12 @@ fn hints(
};
}
-/// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
+/// 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<String> {
+) -> Option<hir::Type> {
let db = sema.db;
let strukt = ty.strip_references().as_adt()?;
let krate = strukt.module(db).krate();
@@ -299,21 +402,7 @@ fn hint_iterator(
_ => None,
})?;
if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) {
- const LABEL_START: &str = "impl Iterator<Item = ";
- const LABEL_END: &str = ">";
-
- let ty_display = hint_iterator(sema, famous_defs, config, &ty)
- .map(|assoc_type_impl| assoc_type_impl.to_string())
- .unwrap_or_else(|| {
- ty.display_truncated(
- db,
- config
- .max_length
- .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
- )
- .to_string()
- });
- return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END));
+ return Some(ty);
}
}
@@ -336,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,
@@ -350,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,
@@ -357,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 754df09df1..7766d49791 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -3,8 +3,8 @@
//! fn f(a: i32, b: i32) -> 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<InlayHint>,
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())
@@ -202,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;
@@ -298,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>(T);
@@ -346,7 +339,31 @@ fn main(a: SliceIter<'_, Container>) {
range: 484..485,
kind: ChainingHint,
label: [
- "SliceIter<Container>",
+ "",
+ 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 32421afd39..efeb2b7925 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<InlayHint>,
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())),
});
}
@@ -77,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,
};
@@ -89,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 } }
@@ -110,7 +114,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 +141,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(
@@ -158,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);
@@ -185,7 +272,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 +299,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(
@@ -219,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>(T);
struct B<T>(T);
@@ -245,7 +356,31 @@ fn main() {
range: 246..283,
kind: ChainingHint,
label: [
- "B<X<i32, bool>>",
+ "",
+ 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,
+ },
+ ),
+ },
+ "<i32, bool>>",
],
tooltip: Some(
HoverRanged(
@@ -260,7 +395,31 @@ fn main() {
range: 246..265,
kind: ChainingHint,
label: [
- "A<X<i32, bool>>",
+ "",
+ 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,
+ },
+ ),
+ },
+ "<i32, bool>>",
],
tooltip: Some(
HoverRanged(
@@ -279,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;
@@ -352,7 +511,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 +567,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 +594,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 +621,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/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index 57605b392a..e340c64c54 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/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs
index de04f3ac75..247a4abcc5 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<InlayHint>,
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, &param_list, config, ty)?,
tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
});
Some(())
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 2380cf7381..42b5951c84 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 7bf595d2a4..ec5053e991 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 835b37c98e..0bcc91eb41 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<usize> = "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<ClientInfo>) {
+ // 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 db41c7bf10..47511aad0f 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 f9b0e28dad..5b09ee6f7d 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,