about summary refs log tree commit diff
diff options
context:
space:
mode:
authorKirill Bulatov <mail4score@gmail.com>2023-08-28 00:11:26 +0300
committerKirill Bulatov <mail4score@gmail.com>2023-09-02 18:28:35 +0300
commite07fbabcfef2069e29ae053d92fbeaa0329492fc (patch)
treecc5f75aa62d93c1251bafccc2c9bcebe31c8598a
parent0e002fe5c6324d61cc8f8100fd7c8bd7e451572a (diff)
downloadrust-e07fbabcfef2069e29ae053d92fbeaa0329492fc.tar.gz
rust-e07fbabcfef2069e29ae053d92fbeaa0329492fc.zip
Resolve inlay hint data
Skip every propery set in inlay hint client resolve capabilities,
reducing overall json footprint.
-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 29259167419..f61252d84da 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 c9cdbff7d7d..2b51a815966 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 d8696198d3b..aabd26da289 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 4a03be1893c..1d99cd9534b 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 40c50f6d176..f58de03ec46 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 36158e8115e..dbf3a4792d6 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 d0989b3230d..ad56899163d 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 daa7f5fe19d..758dc66b43c 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 67d82a68548..0801e988f5c 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.