Unnamed repository; edit this file 'description' to name the repository.
Resolve inlay hint data
Skip every propery set in inlay hint client resolve capabilities, reducing overall json footprint.
Kirill Bulatov 2023-09-02
parent 0e002fe · commit e07fbab
-rw-r--r--crates/ide/src/inlay_hints.rs54
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/ide/src/static_index.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs6
-rw-r--r--crates/rust-analyzer/src/config.rs18
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs68
-rw-r--r--crates/rust-analyzer/src/lsp/ext.rs4
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs87
-rw-r--r--docs/dev/lsp-extensions.md6
9 files changed, 200 insertions, 51 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 2925916741..f61252d84d 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -52,6 +52,42 @@ pub struct InlayHintsConfig {
pub closure_style: ClosureStyle,
pub max_length: Option<usize>,
pub closing_brace_hints_min_lines: Option<usize>,
+ pub fields_to_resolve: InlayFieldsToResolve,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct InlayFieldsToResolve {
+ pub client_capability_fields: Vec<String>,
+}
+
+impl InlayFieldsToResolve {
+ pub const fn empty() -> Self {
+ Self { client_capability_fields: Vec::new() }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.client_capability_fields.is_empty()
+ }
+
+ pub fn resolve_text_edits(&self) -> bool {
+ self.client_capability_fields.iter().find(|s| s.as_str() == "textEdits").is_some()
+ }
+
+ pub fn resolve_hint_tooltip(&self) -> bool {
+ self.client_capability_fields.iter().find(|s| s.as_str() == "tooltip").is_some()
+ }
+
+ pub fn resolve_label_tooltip(&self) -> bool {
+ self.client_capability_fields.iter().find(|s| s.as_str() == "label.tooltip").is_some()
+ }
+
+ pub fn resolve_label_location(&self) -> bool {
+ self.client_capability_fields.iter().find(|s| s.as_str() == "label.location").is_some()
+ }
+
+ pub fn resolve_label_command(&self) -> bool {
+ self.client_capability_fields.iter().find(|s| s.as_str() == "label.command").is_some()
+ }
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -529,6 +565,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 +575,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 +596,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,
@@ -567,7 +605,19 @@ mod tests {
closure_return_type_hints: ClosureReturnTypeHints::WithBlock,
binding_mode_hints: true,
lifetime_elision_hints: LifetimeElisionHints::Always,
- ..DISABLED_CONFIG
+ discriminant_hints: DiscriminantHints::Never,
+ render_colons: false,
+ closure_capture_hints: false,
+ adjustment_hints: AdjustmentHints::Never,
+ adjustment_hints_mode: AdjustmentHintsMode::Prefix,
+ adjustment_hints_hide_outside_unsafe: false,
+ hide_named_constructor_hints: false,
+ hide_closure_initialization_hints: false,
+ closure_style: ClosureStyle::ImplFn,
+ param_names_for_lifetime_elision_hints: false,
+ max_length: None,
+ closing_brace_hints_min_lines: None,
+ fields_to_resolve: InlayFieldsToResolve::empty(),
};
#[track_caller]
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index c9cdbff7d7..2b51a81596 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 d8696198d3..aabd26da28 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 4a03be1893..1d99cd9534 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 40c50f6d17..f58de03ec4 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::<Vec<_>>();
+
InlayHintsConfig {
render_colons: self.data.inlayHints_renderColons,
type_hints: self.data.inlayHints_typeHints_enable,
@@ -1395,6 +1408,7 @@ impl Config {
} else {
None
},
+ fields_to_resolve: InlayFieldsToResolve { client_capability_fields },
}
}
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 36158e8115..dbf3a4792d 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,71 @@ 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::<Cancellable<Vec<_>>>()?,
))
}
pub(crate) fn handle_inlay_hints_resolve(
- _snap: GlobalStateSnapshot,
- hint: InlayHint,
+ snap: GlobalStateSnapshot,
+ mut original_hint: InlayHint,
) -> anyhow::Result<InlayHint> {
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);
+ 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 d0989b3230..ad56899163 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 daa7f5fe19..758dc66b43 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,22 @@ 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<lsp_types::InlayHint> {
- let (label, tooltip) = inlay_hint_label(snap, inlay_hint.label)?;
+ let (label, tooltip) = inlay_hint_label(snap, fields_to_resolve, inlay_hint.label)?;
+ let data = if fields_to_resolve.is_empty() {
+ None
+ } else {
+ Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap())
+ };
+ let text_edits = if fields_to_resolve.resolve_text_edits() {
+ None
+ } else {
+ inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it))
+ };
Ok(lsp_types::InlayHint {
position: match inlay_hint.position {
@@ -454,8 +466,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 +475,15 @@ pub(crate) fn inlay_hint(
fn inlay_hint_label(
snap: &GlobalStateSnapshot,
+ fields_to_resolve: &InlayFieldsToResolve,
mut label: InlayHintLabel,
) -> Cancellable<(lsp_types::InlayHintLabel, Option<lsp_types::InlayHintTooltip>)> {
let res = match &*label.parts {
[InlayHintLabelPart { linked_location: None, .. }] => {
let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
- (
- lsp_types::InlayHintLabel::String(text),
+ let hint_tooltip = if fields_to_resolve.resolve_hint_tooltip() {
+ None
+ } else {
match tooltip {
Some(ide::InlayTooltip::String(s)) => {
Some(lsp_types::InlayHintTooltip::String(s))
@@ -481,35 +495,44 @@ 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 fields_to_resolve.resolve_label_tooltip() {
+ 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 fields_to_resolve.resolve_label_location() {
+ None
+ } else {
+ part.linked_location.map(|range| location(snap, range)).transpose()?
+ };
+ Ok(lsp_types::InlayHintLabelPart {
+ value: part.text,
+ tooltip,
+ location,
+ command: None,
+ })
})
.collect::<Cancellable<_>>()?;
(lsp_types::InlayHintLabel::LabelParts(parts), None)
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 67d82a6854..0801e988f5 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp/ext.rs hash: 149a5be3c5e469d1
+lsp/ext.rs hash: 121482ee911854da
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
@@ -322,7 +322,7 @@ Position[]
```rust
fn main() {
- let x: Vec<()>/*cursor here*/ = vec![]
+ let x: Vec<()>/*cursor here*/ = vec![];
}
```
@@ -362,7 +362,7 @@ interface RunnablesParams {
```typescript
interface Runnable {
label: string;
- /// If this Runnable is associated with a specific function/module, etc, the location of this item
+ /// If this Runnable is associated with a specific function/module, etc., the location of this item
location?: LocationLink;
/// Running things is necessary technology specific, `kind` needs to be advertised via server capabilities,
// the type of `args` is specific to `kind`. The actual running is handled by the client.