diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 292591674191..a5d070fe7673 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -52,6 +52,28 @@ pub struct InlayHintsConfig { pub closure_style: ClosureStyle, pub max_length: Option, pub closing_brace_hints_min_lines: Option, + pub fields_to_resolve: InlayFieldsToResolve, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct InlayFieldsToResolve { + pub resolve_text_edits: bool, + pub resolve_hint_tooltip: bool, + pub resolve_label_tooltip: bool, + pub resolve_label_location: bool, + pub resolve_label_command: bool, +} + +impl InlayFieldsToResolve { + pub const fn empty() -> Self { + Self { + resolve_text_edits: false, + resolve_hint_tooltip: false, + resolve_label_tooltip: false, + resolve_label_location: false, + resolve_label_command: false, + } + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -123,11 +145,13 @@ 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(")"), @@ -139,6 +163,7 @@ impl InlayHint { } fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { + needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -196,6 +221,10 @@ impl InlayHintLabel { }), } } + + pub fn needs_resolve(&self) -> bool { + self.parts.iter().any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + } } impl From for InlayHintLabel { @@ -529,6 +558,7 @@ fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { #[cfg(test)] mod tests { + use expect_test::Expect; use hir::ClosureStyle; use itertools::Itertools; @@ -538,7 +568,7 @@ mod tests { use crate::DiscriminantHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; - use super::ClosureReturnTypeHints; + use super::{ClosureReturnTypeHints, InlayFieldsToResolve}; pub(super) const DISABLED_CONFIG: InlayHintsConfig = InlayHintsConfig { discriminant_hints: DiscriminantHints::Never, @@ -559,6 +589,7 @@ mod tests { param_names_for_lifetime_elision_hints: false, max_length: None, closing_brace_hints_min_lines: None, + fields_to_resolve: InlayFieldsToResolve::empty(), }; pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { type_hints: true, diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 6d6bd315ebb4..631807d99a7e 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/crates/ide/src/inlay_hints/adjustment.rs @@ -137,21 +137,23 @@ pub(super) fn hints( } _ => continue, }; + let label = InlayHintLabel::simple( + if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, + Some(InlayTooltip::Markdown(format!( + "`{}` → `{}` ({coercion} coercion)", + source.display(sema.db), + target.display(sema.db), + ))), + None, + ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: expr.syntax().text_range(), pad_left: false, pad_right: false, position: if postfix { InlayHintPosition::After } else { InlayHintPosition::Before }, kind: InlayKind::Adjustment, - label: InlayHintLabel::simple( - if postfix { format!(".{}", text.trim_end()) } else { text.to_owned() }, - Some(InlayTooltip::Markdown(format!( - "`{}` → `{}` ({coercion} coercion)", - source.display(sema.db), - target.display(sema.db), - ))), - None, - ), + label, text_edit: None, }); } diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 07b9f9cc1fff..680035c721b3 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -99,6 +99,7 @@ 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 343cf17e50e0..35504ffa7859 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/crates/ide/src/inlay_hints/binding_mode.rs @@ -50,9 +50,10 @@ pub(super) fn hints( _ => return, }; acc.push(InlayHint { + needs_resolve: false, range, kind: InlayKind::BindingMode, - label: r.to_string().into(), + label: r.into(), text_edit: None, position: InlayHintPosition::Before, pad_left: false, @@ -68,9 +69,10 @@ 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.to_string().into(), + label: bm.into(), text_edit: None, position: InlayHintPosition::Before, pad_left: false, diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index b621a8dda7ee..af5f5c327030 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -57,10 +57,12 @@ 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: label_of_ty(famous_defs, config, &ty)?, + label, text_edit: None, position: InlayHintPosition::After, pad_left: true, @@ -128,6 +130,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 147..154, @@ -152,6 +155,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -221,6 +225,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 143..179, @@ -245,6 +250,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -298,6 +304,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 143..179, @@ -322,6 +329,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -389,6 +397,7 @@ fn main() { ">", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 246..265, @@ -426,6 +435,7 @@ fn main() { ">", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -495,6 +505,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..224, @@ -532,6 +543,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..206, @@ -569,6 +581,7 @@ fn main() { " = ()>", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 174..189, @@ -593,6 +606,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, ] "#]], @@ -655,6 +669,7 @@ fn main() { ], }, ), + needs_resolve: true, }, InlayHint { range: 145..185, @@ -679,6 +694,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 145..168, @@ -703,6 +719,7 @@ fn main() { "", ], text_edit: None, + needs_resolve: true, }, InlayHint { range: 222..228, @@ -725,6 +742,7 @@ fn main() { }, ], text_edit: None, + needs_resolve: true, }, ] "#]], diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index 2cefd5acdc2e..2b68538c198c 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/crates/ide/src/inlay_hints/closing_brace.rs @@ -109,6 +109,7 @@ 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 9d5defcbb71a..d691303c18b8 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/crates/ide/src/inlay_hints/closure_captures.rs @@ -31,9 +31,10 @@ 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::simple("move", None, None), + label: InlayHintLabel::from("move"), text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -43,6 +44,7 @@ pub(super) fn hints( } }; acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, label: InlayHintLabel::from("("), @@ -59,23 +61,25 @@ pub(super) fn hints( // force cache the source file, otherwise sema lookup will potentially panic _ = sema.parse_or_expand(source.file()); + let label = InlayHintLabel::simple( + format!( + "{}{}", + match capture.kind() { + hir::CaptureKind::SharedRef => "&", + hir::CaptureKind::UniqueSharedRef => "&unique ", + hir::CaptureKind::MutableRef => "&mut ", + hir::CaptureKind::Move => "", + }, + capture.display_place(sema.db) + ), + None, + source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), + ); acc.push(InlayHint { + needs_resolve: label.needs_resolve(), range: move_kw_range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple( - format!( - "{}{}", - match capture.kind() { - hir::CaptureKind::SharedRef => "&", - hir::CaptureKind::UniqueSharedRef => "&unique ", - hir::CaptureKind::MutableRef => "&mut ", - hir::CaptureKind::Move => "", - }, - capture.display_place(sema.db) - ), - None, - source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)), - ), + label, text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -84,9 +88,10 @@ pub(super) fn hints( if idx != last { acc.push(InlayHint { + needs_resolve: false, range: move_kw_range, kind: InlayKind::ClosureCapture, - label: InlayHintLabel::simple(", ", None, None), + label: InlayHintLabel::from(", "), text_edit: None, position: InlayHintPosition::After, pad_left: false, @@ -95,6 +100,7 @@ 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 3b41db0f13d0..204967cd7ca8 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/crates/ide/src/inlay_hints/closure_ret.rs @@ -64,6 +64,7 @@ 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 c4d2ac75cfa9..26dc6fa8b9c7 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/crates/ide/src/inlay_hints/discriminant.rs @@ -79,6 +79,7 @@ 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 5fce11b785a7..7b05e32ad86f 100644 --- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs @@ -22,6 +22,7 @@ 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(), @@ -185,6 +186,7 @@ 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,6 +202,7 @@ 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_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs index fc297a8d824f..f18e6421cbcb 100644 --- a/crates/ide/src/inlay_hints/implicit_static.rs +++ b/crates/ide/src/inlay_hints/implicit_static.rs @@ -31,9 +31,10 @@ 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".to_owned().into(), + label: "'static".into(), text_edit: None, position: InlayHintPosition::After, pad_left: false, diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index c4f43f411753..b4260d82506a 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs @@ -57,6 +57,7 @@ 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/lib.rs b/crates/ide/src/lib.rs index c9cdbff7d7d3..2b51a8159669 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -91,9 +91,9 @@ pub use crate::{ MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, }, inlay_hints::{ - AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, - InlayTooltip, LifetimeElisionHints, + AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, + InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index d8696198d3ba..aabd26da289c 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -12,6 +12,7 @@ use ide_db::{ }; use syntax::{AstNode, SyntaxKind::*, TextRange, T}; +use crate::inlay_hints::InlayFieldsToResolve; use crate::{ hover::hover_for_definition, inlay_hints::AdjustmentHintsMode, @@ -125,6 +126,7 @@ impl StaticIndex<'_> { max_length: Some(25), closure_capture_hints: false, closing_brace_hints_min_lines: Some(25), + fields_to_resolve: InlayFieldsToResolve::empty(), }, file_id, None, diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 4a03be1893c7..1d99cd9534b7 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -15,7 +15,10 @@ use hir_def::{ hir::{ExprId, PatId}, }; use hir_ty::{Interner, Substitution, TyExt, TypeFlags}; -use ide::{Analysis, AnnotationConfig, DiagnosticsConfig, InlayHintsConfig, LineCol, RootDatabase}; +use ide::{ + Analysis, AnnotationConfig, DiagnosticsConfig, InlayFieldsToResolve, InlayHintsConfig, LineCol, + RootDatabase, +}; use ide_db::{ base_db::{ salsa::{self, debug::DebugQueryTable, ParallelDatabase}, @@ -782,6 +785,7 @@ impl flags::AnalysisStats { closure_style: hir::ClosureStyle::ImplFn, max_length: Some(25), closing_brace_hints_min_lines: Some(20), + fields_to_resolve: InlayFieldsToResolve::empty(), }, file_id, None, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 40c50f6d1768..ea3a21241cb6 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -13,8 +13,9 @@ use cfg::{CfgAtom, CfgDiff}; use flycheck::FlycheckConfig; use ide::{ AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, - HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayHintsConfig, - JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, + HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, InlayFieldsToResolve, + InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, + Snippet, SnippetScope, }; use ide_db::{ imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind}, @@ -1335,6 +1336,18 @@ impl Config { } pub fn inlay_hints(&self) -> InlayHintsConfig { + let client_capability_fields = self + .caps + .text_document + .as_ref() + .and_then(|text| text.inlay_hint.as_ref()) + .and_then(|inlay_hint_caps| inlay_hint_caps.resolve_support.as_ref()) + .map(|inlay_resolve| inlay_resolve.properties.iter()) + .into_iter() + .flatten() + .cloned() + .collect::>(); + InlayHintsConfig { render_colons: self.data.inlayHints_renderColons, type_hints: self.data.inlayHints_typeHints_enable, @@ -1395,6 +1408,13 @@ impl Config { } else { None }, + fields_to_resolve: InlayFieldsToResolve { + resolve_text_edits: client_capability_fields.contains("textEdits"), + resolve_hint_tooltip: client_capability_fields.contains("tooltip"), + resolve_label_tooltip: client_capability_fields.contains("label.tooltip"), + resolve_label_location: client_capability_fields.contains("label.location"), + resolve_label_command: client_capability_fields.contains("label.command"), + }, } } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 22a7dd1548f7..869afbe907db 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -489,6 +489,10 @@ impl GlobalStateSnapshot { pub(crate) fn vfs_memory_usage(&self) -> usize { self.vfs.read().0.memory_usage() } + + pub(crate) fn file_exists(&self, file_id: FileId) -> bool { + self.vfs.read().0.exists(file_id) + } } pub(crate) fn file_id_to_url(vfs: &vfs::Vfs, id: FileId) -> Url { diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 36158e8115e2..a21206b5b132 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -11,8 +11,8 @@ use anyhow::Context; use ide::{ AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, - SingleResolve, SourceChange, TextEdit, + HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory, + Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use lsp_server::ErrorCode; @@ -30,7 +30,7 @@ use serde_json::json; use stdx::{format_to, never}; use syntax::{algo, ast, AstNode, TextRange, TextSize}; use triomphe::Arc; -use vfs::{AbsPath, AbsPathBuf, VfsPath}; +use vfs::{AbsPath, AbsPathBuf, FileId, VfsPath}; use crate::{ cargo_target_spec::CargoTargetSpec, @@ -1412,17 +1412,73 @@ pub(crate) fn handle_inlay_hints( snap.analysis .inlay_hints(&inlay_hints_config, file_id, Some(range))? .into_iter() - .map(|it| to_proto::inlay_hint(&snap, &line_index, it)) + .map(|it| { + to_proto::inlay_hint( + &snap, + &inlay_hints_config.fields_to_resolve, + &line_index, + file_id, + it, + ) + }) .collect::>>()?, )) } pub(crate) fn handle_inlay_hints_resolve( - _snap: GlobalStateSnapshot, - hint: InlayHint, + snap: GlobalStateSnapshot, + mut original_hint: InlayHint, ) -> anyhow::Result { let _p = profile::span("handle_inlay_hints_resolve"); - Ok(hint) + + let data = match original_hint.data.take() { + Some(it) => it, + None => return Ok(original_hint), + }; + + let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?; + let file_id = FileId(resolve_data.file_id); + anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); + + let line_index = snap.file_line_index(file_id)?; + let range = from_proto::text_range( + &line_index, + lsp_types::Range { start: original_hint.position, end: original_hint.position }, + )?; + let range_start = range.start(); + let range_end = range.end(); + let large_range = TextRange::new( + range_start.checked_sub(1.into()).unwrap_or(range_start), + range_end.checked_add(1.into()).unwrap_or(range_end), + ); + 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( + &forced_resolve_inlay_hints_config, + file_id, + Some(large_range), + )?; + + let mut resolved_hints = resolve_hints + .into_iter() + .filter_map(|it| { + to_proto::inlay_hint( + &snap, + &forced_resolve_inlay_hints_config.fields_to_resolve, + &line_index, + file_id, + it, + ) + .ok() + }) + .filter(|hint| hint.position == original_hint.position) + .filter(|hint| hint.kind == original_hint.kind); + if let Some(resolved_hint) = resolved_hints.next() { + if resolved_hints.next().is_none() { + return Ok(resolved_hint); + } + } + Ok(original_hint) } pub(crate) fn handle_call_hierarchy_prepare( diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index d0989b3230dc..ad56899163d3 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -682,7 +682,9 @@ pub struct CompletionResolveData { } #[derive(Debug, Serialize, Deserialize)] -pub struct InlayHintResolveData {} +pub struct InlayHintResolveData { + pub file_id: u32, +} #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index daa7f5fe19d4..c81d72b5f14e 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -8,10 +8,10 @@ use std::{ use ide::{ Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, - Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, - InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, NavigationTarget, ReferenceCategory, - RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, - SymbolKind, TextEdit, TextRange, TextSize, + Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, + NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, + SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; use itertools::Itertools; use serde_json::to_value; @@ -437,10 +437,25 @@ pub(crate) fn signature_help( pub(crate) fn inlay_hint( snap: &GlobalStateSnapshot, + fields_to_resolve: &InlayFieldsToResolve, line_index: &LineIndex, + file_id: FileId, inlay_hint: InlayHint, ) -> Cancellable { - let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?; + 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 text_edits = if needs_resolve && 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)) + }; + let data = if needs_resolve && something_to_resolve { + Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap()) + } else { + None + }; Ok(lsp_types::InlayHint { position: match inlay_hint.position { @@ -454,8 +469,8 @@ pub(crate) fn inlay_hint( InlayKind::Type | InlayKind::Chaining => Some(lsp_types::InlayHintKind::TYPE), _ => None, }, - text_edits: inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)), - data: None, + text_edits, + data, tooltip, label, }) @@ -463,13 +478,18 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, + fields_to_resolve: &InlayFieldsToResolve, + needs_resolve: bool, mut label: InlayHintLabel, -) -> Cancellable<(lsp_types::InlayHintLabel, Option)> { - let res = match &*label.parts { +) -> Cancellable<(lsp_types::InlayHintLabel, Option, bool)> { + let mut something_to_resolve = false; + let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); - ( - lsp_types::InlayHintLabel::String(text), + let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { + something_to_resolve |= tooltip.is_some(); + None + } else { match tooltip { Some(ide::InlayTooltip::String(s)) => { Some(lsp_types::InlayHintTooltip::String(s)) @@ -481,41 +501,52 @@ fn inlay_hint_label( })) } None => None, - }, - ) + } + }; + (lsp_types::InlayHintLabel::String(text), hint_tooltip) } _ => { let parts = label .parts .into_iter() .map(|part| { - part.linked_location.map(|range| location(snap, range)).transpose().map( - |location| lsp_types::InlayHintLabelPart { - value: part.text, - tooltip: match part.tooltip { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( - lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - }, - )) - } - None => None, - }, - location, - command: None, - }, - ) + let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { + something_to_resolve |= part.tooltip.is_some(); + None + } else { + match part.tooltip { + Some(ide::InlayTooltip::String(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::String(s)) + } + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( + lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + }, + )) + } + None => None, + } + }; + let location = if needs_resolve && fields_to_resolve.resolve_label_location { + something_to_resolve |= part.linked_location.is_some(); + None + } else { + part.linked_location.map(|range| location(snap, range)).transpose()? + }; + Ok(lsp_types::InlayHintLabelPart { + value: part.text, + tooltip, + location, + command: None, + }) }) .collect::>()?; (lsp_types::InlayHintLabel::LabelParts(parts), None) } }; - Ok(res) + Ok((label, tooltip, something_to_resolve)) } static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1); diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index fe3dfe619686..06004adad34a 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -184,6 +184,11 @@ impl Vfs { mem::take(&mut self.changes) } + /// Provides a panic-less way to verify file_id validity. + pub fn exists(&self, file_id: FileId) -> bool { + self.get(file_id).is_some() + } + /// Returns the id associated with `path` /// /// - If `path` does not exists in the `Vfs`, allocate a new id for it, associated with a diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 67d82a685488..0801e988f5ce 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@