From 3115fd8b41f2a920e0498a8bfe326e976d5978c6 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 12 Mar 2024 15:02:17 +0100 Subject: [PATCH 1/2] Simplify inlay hints `needs_resolve` --- crates/ide/src/inlay_hints.rs | 8 +++++--- crates/ide/src/inlay_hints/adjustment.rs | 1 - crates/ide/src/inlay_hints/bind_pat.rs | 1 - crates/ide/src/inlay_hints/binding_mode.rs | 2 -- crates/ide/src/inlay_hints/chaining.rs | 1 - crates/ide/src/inlay_hints/closing_brace.rs | 1 - crates/ide/src/inlay_hints/closure_captures.rs | 5 ----- crates/ide/src/inlay_hints/closure_ret.rs | 1 - crates/ide/src/inlay_hints/discriminant.rs | 1 - crates/ide/src/inlay_hints/fn_lifetime_fn.rs | 3 --- crates/ide/src/inlay_hints/implicit_drop.rs | 1 - crates/ide/src/inlay_hints/implicit_static.rs | 1 - crates/ide/src/inlay_hints/param_name.rs | 1 - crates/ide/src/inlay_hints/range_exclusive.rs | 1 - crates/rust-analyzer/src/lsp/to_proto.rs | 2 +- 15 files changed, 6 insertions(+), 24 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 8311e770b4b..a94bf8ebd11 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -151,13 +151,11 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, - pub needs_resolve: bool, } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -167,9 +165,9 @@ fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { pad_right: false, } } + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -179,6 +177,10 @@ fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { pad_right: false, } } + + pub fn needs_resolve(&self) -> bool { + self.text_edit.is_some() || self.label.needs_resolve() + } } #[derive(Debug)] diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 631807d99a7..20128a286f2 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -147,7 +147,6 @@ pub(super) fn hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 45b51e35570..703eb660c87 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,7 +99,6 @@ pub(super) fn hints( None => pat.syntax().text_range(), }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: match type_ascriptable { Some(Some(t)) => text_range.cover(t.text_range()), _ => text_range, diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs index 35504ffa785..f27390ee898 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/crates/ide/src/inlay_hints/binding_mode.rs @@ -50,7 +50,6 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::BindingMode, label: r.into(), @@ -69,7 +68,6 @@ pub(super) fn hints( hir::BindingMode::Ref(Mutability::Shared) => "ref", }; acc.push(InlayHint { - needs_resolve: false, range: pat.syntax().text_range(), kind: InlayKind::BindingMode, label: bm.into(), diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index b6063978e92..d86487d4b41 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -59,7 +59,6 @@ pub(super) fn hints( } let label = label_of_ty(famous_defs, config, &ty)?; acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), kind: InlayKind::Chaining, label, diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 2b68538c198..2cefd5acdc2 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,7 +109,6 @@ pub(super) fn hints( let linked_location = name_range.map(|range| FileRange { file_id, range }); acc.push(InlayHint { - needs_resolve: linked_location.is_some(), range: closing_token.text_range(), kind: InlayKind::ClosingBrace, label: InlayHintLabel::simple(label, None, linked_location), diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index 2f8b959516d..f1b524e0880 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -32,7 +32,6 @@ pub(super) fn hints( let range = closure.syntax().first_token()?.prev_token()?.text_range(); let range = TextRange::new(range.end() - TextSize::from(1), range.end()); acc.push(InlayHint { - needs_resolve: false, range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("move"), @@ -45,7 +44,6 @@ pub(super) fn hints( } }; acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -79,7 +77,6 @@ pub(super) fn hints( }), ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, label, @@ -91,7 +88,6 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(", "), @@ -103,7 +99,6 @@ pub(super) fn hints( } } acc.push(InlayHint { - needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from(")"), diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index 204967cd7ca..3b41db0f13d 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,7 +64,6 @@ pub(super) fn hints( }; acc.push(InlayHint { - needs_resolve: label.needs_resolve() || text_edit.is_some(), range: param_list.syntax().text_range(), kind: InlayKind::Type, label, diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs index 06cce147d2a..202954100fb 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/crates/ide/src/inlay_hints/discriminant.rs @@ -79,7 +79,6 @@ fn variant_hints( None, ); acc.push(InlayHint { - needs_resolve: label.needs_resolve(), range: match eq_token { Some(t) => range.cover(t.text_range()), _ => range, diff --git a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs index 6e5f23bed09..d3666754e2b 100644 --- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,7 +22,6 @@ pub(super) fn hints( } let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: label.into(), @@ -184,7 +183,6 @@ pub(super) fn hints( let angle_tok = gpl.l_angle_token()?; let is_empty = gpl.generic_params().next().is_none(); acc.push(InlayHint { - needs_resolve: false, range: angle_tok.text_range(), kind: InlayKind::Lifetime, label: format!( @@ -200,7 +198,6 @@ pub(super) fn hints( }); } (None, allocated_lifetimes) => acc.push(InlayHint { - needs_resolve: false, range: func.name()?.syntax().text_range(), kind: InlayKind::GenericParamList, label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs index 5ba4e514e1f..31f0c790374 100644 --- a/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/crates/ide/src/inlay_hints/implicit_drop.rs @@ -105,7 +105,6 @@ pub(super) fn hints( pad_left: true, pad_right: true, kind: InlayKind::Drop, - needs_resolve: label.needs_resolve(), label, text_edit: None, }) diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs index f18e6421cbc..42223ddf580 100644 --- a/crates/ide/src/inlay_hints/implicit_static.rs +++ b/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,7 +31,6 @@ pub(super) fn hints( if ty.lifetime().is_none() { let t = ty.amp_token()?; acc.push(InlayHint { - needs_resolve: false, range: t.text_range(), kind: InlayKind::Lifetime, label: "'static".into(), diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 418fc002a8b..96e845b2f32 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -57,7 +57,6 @@ pub(super) fn hints( let label = InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location); InlayHint { - needs_resolve: label.needs_resolve(), range, kind: InlayKind::Parameter, label, diff --git a/crates/ide/src/inlay_hints/range_exclusive.rs b/crates/ide/src/inlay_hints/range_exclusive.rs index c4b0c199fc2..bfb92838857 100644 --- a/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/crates/ide/src/inlay_hints/range_exclusive.rs @@ -30,7 +30,6 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, - needs_resolve: false, } } diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index e77d0c13bf2..cd6c3173643 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -446,7 +446,7 @@ pub(crate) fn inlay_hint( file_id: FileId, inlay_hint: InlayHint, ) -> Cancellable { - let needs_resolve = inlay_hint.needs_resolve; + let needs_resolve = inlay_hint.needs_resolve(); let (label, tooltip, mut something_to_resolve) = inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; From 4a93368590a71b1cb17d319aa0343d7def68737b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 12 Mar 2024 15:41:51 +0100 Subject: [PATCH 2/2] Use a hash to find the correct inlay hint when resolving --- crates/ide/src/inlay_hints.rs | 88 ++++++++++++-------- crates/ide/src/inlay_hints/bind_pat.rs | 8 +- crates/ide/src/lib.rs | 13 ++- crates/rust-analyzer/src/handlers/request.rs | 11 +-- crates/rust-analyzer/src/lsp/ext.rs | 1 + crates/rust-analyzer/src/lsp/to_proto.rs | 46 ++++++---- docs/dev/lsp-extensions.md | 8 +- 7 files changed, 108 insertions(+), 67 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index a94bf8ebd11..dda38ce77e0 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,5 +1,6 @@ use std::{ fmt::{self, Write}, + hash::{BuildHasher, BuildHasherDefault}, mem::take, }; @@ -8,7 +9,7 @@ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode { PreferPostfix, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InlayKind { Adjustment, BindingMode, @@ -132,7 +133,7 @@ pub enum InlayKind { RangeExclusive, } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayHintPosition { Before, After, @@ -153,6 +154,18 @@ pub struct InlayHint { pub text_edit: Option, } +impl std::hash::Hash for InlayHint { + fn hash(&self, state: &mut H) { + self.range.hash(state); + self.position.hash(state); + self.pad_left.hash(state); + self.pad_right.hash(state); + self.kind.hash(state); + self.label.hash(state); + self.text_edit.is_some().hash(state); + } +} + impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { @@ -183,13 +196,13 @@ pub fn needs_resolve(&self) -> bool { } } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayTooltip { String(String), Markdown(String), } -#[derive(Default)] +#[derive(Default, Hash)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, } @@ -267,6 +280,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } } +#[derive(Hash)] pub struct InlayHintLabelPart { pub text: String, /// Source location represented by this label part. The client will use this to fetch the part's @@ -315,9 +329,7 @@ fn write_str(&mut self, s: &str) -> fmt::Result { impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { - if self.location.is_some() { - never!("location link is already started"); - } + never!(self.location.is_some(), "location link is already started"); self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; let location = location.call_site(); @@ -427,11 +439,6 @@ fn ty_to_text_edit( Some(builder.finish()) } -pub enum RangeLimit { - Fixed(TextRange), - NearestParent(TextSize), -} - // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -453,7 +460,7 @@ pub enum RangeLimit { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option, + range_limit: Option, config: &InlayHintsConfig, ) -> Vec { let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); @@ -468,31 +475,13 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(RangeLimit::Fixed(range)) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() .filter(|descendant| range.intersect(descendant.text_range()).is_some()) .for_each(hints), }, - Some(RangeLimit::NearestParent(position)) => { - match file.token_at_offset(position).left_biased() { - Some(token) => { - if let Some(parent_block) = - token.parent_ancestors().find_map(ast::BlockExpr::cast) - { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = - token.parent_ancestors().find_map(ast::Item::cast) - { - parent_item.syntax().descendants().for_each(hints) - } else { - return acc; - } - } - None => return acc, - } - } None => file.descendants().for_each(hints), }; } @@ -500,6 +489,39 @@ pub(crate) fn inlay_hints( acc } +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + position: TextSize, + hash: u64, + config: &InlayHintsConfig, +) -> Option { + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let sema = Semantics::new(db); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); + match file.token_at_offset(position).left_biased() { + Some(token) => { + if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { + parent_block.syntax().descendants().for_each(hints) + } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { + parent_item.syntax().descendants().for_each(hints) + } else { + return None; + } + } + None => return None, + } + + acc.into_iter().find(|hint| BuildHasherDefault::::default().hash_one(hint) == hash) +} + fn hints( hints: &mut Vec, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 703eb660c87..07b9f9cc1ff 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -176,11 +176,7 @@ mod tests { use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::{ - fixture, - inlay_hints::{InlayHintsConfig, RangeLimit}, - ClosureReturnTypeHints, - }; + use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints}; use crate::inlay_hints::tests::{ check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG, @@ -403,7 +399,7 @@ fn main() { .inlay_hints( &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, - Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))), + Some(TextRange::new(TextSize::from(500), TextSize::from(600))), ) .unwrap(); let actual = diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 6955e14a10a..1c57f4f8f63 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -90,7 +90,7 @@ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, - InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, RangeLimit, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -415,10 +415,19 @@ pub fn inlay_hints( &self, config: &InlayHintsConfig, file_id: FileId, - range: Option, + range: Option, ) -> Cancellable> { self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config)) } + pub fn inlay_hints_resolve( + &self, + config: &InlayHintsConfig, + file_id: FileId, + position: TextSize, + hash: u64, + ) -> Cancellable> { + self.with_db(|db| inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config)) + } /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Cancellable> { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 1d98457add3..9d6eda3e5ec 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -12,8 +12,8 @@ use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, RangeLimit, - ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, + Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use itertools::Itertools; @@ -1465,7 +1465,7 @@ pub(crate) fn handle_inlay_hints( let inlay_hints_config = snap.config.inlay_hints(); Ok(Some( snap.analysis - .inlay_hints(&inlay_hints_config, file_id, Some(RangeLimit::Fixed(range)))? + .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() .map(|it| { to_proto::inlay_hint( @@ -1499,10 +1499,11 @@ pub(crate) fn handle_inlay_hints_resolve( let hint_position = from_proto::offset(&line_index, original_hint.position)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); - let resolve_hints = snap.analysis.inlay_hints( + let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - Some(RangeLimit::NearestParent(hint_position)), + hint_position, + resolve_data.hash, )?; let mut resolved_hints = resolve_hints diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index 710ce7f8acb..5d3d75efcba 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -800,6 +800,7 @@ pub struct CompletionResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct InlayHintResolveData { pub file_id: u32, + pub hash: u64, } #[derive(Debug, Serialize, Deserialize)] diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index cd6c3173643..dcff0ea7473 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -13,7 +13,7 @@ NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; -use ide_db::rust_doc::format_docs; +use ide_db::{rust_doc::format_docs, FxHasher}; use itertools::Itertools; use semver::VersionReq; use serde_json::to_value; @@ -444,30 +444,42 @@ pub(crate) fn inlay_hint( fields_to_resolve: &InlayFieldsToResolve, line_index: &LineIndex, file_id: FileId, - inlay_hint: InlayHint, + mut inlay_hint: InlayHint, ) -> Cancellable { - let needs_resolve = inlay_hint.needs_resolve(); - let (label, tooltip, mut something_to_resolve) = - inlay_hint_label(snap, fields_to_resolve, needs_resolve, inlay_hint.label)?; + let resolve_hash = inlay_hint.needs_resolve().then(|| { + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ) + }); + let mut something_to_resolve = false; let text_edits = if snap .config .visual_studio_code_version() // https://github.com/microsoft/vscode/issues/193124 .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && needs_resolve + && resolve_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); None } else { - inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) + inlay_hint.text_edit.take().map(|it| text_edit_vec(line_index, it)) }; + let (label, tooltip) = inlay_hint_label( + snap, + fields_to_resolve, + &mut something_to_resolve, + resolve_hash.is_some(), + inlay_hint.label, + )?; - let data = if needs_resolve && something_to_resolve { - Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) - } else { - None + let data = match resolve_hash { + Some(hash) if something_to_resolve => Some( + to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash }).unwrap(), + ), + _ => None, }; Ok(lsp_types::InlayHint { @@ -492,15 +504,15 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, fields_to_resolve: &InlayFieldsToResolve, + something_to_resolve: &mut bool, needs_resolve: bool, mut label: InlayHintLabel, -) -> Cancellable<(lsp_types::InlayHintLabel, Option, bool)> { - let mut something_to_resolve = false; +) -> Cancellable<(lsp_types::InlayHintLabel, Option)> { let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - something_to_resolve |= tooltip.is_some(); + *something_to_resolve |= tooltip.is_some(); None } else { match tooltip { @@ -524,7 +536,7 @@ fn inlay_hint_label( .into_iter() .map(|part| { let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - something_to_resolve |= part.tooltip.is_some(); + *something_to_resolve |= part.tooltip.is_some(); None } else { match part.tooltip { @@ -543,7 +555,7 @@ fn inlay_hint_label( } }; let location = if needs_resolve && fields_to_resolve.resolve_label_location { - something_to_resolve |= part.linked_location.is_some(); + *something_to_resolve |= part.linked_location.is_some(); None } else { part.linked_location.map(|range| location(snap, range)).transpose()? @@ -559,7 +571,7 @@ fn inlay_hint_label( (lsp_types::InlayHintLabel::LabelParts(parts), None) } }; - Ok((label, tooltip, something_to_resolve)) + Ok((label, tooltip)) } static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index cf9ad5fe04d..8db66687aae 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@