about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-09-08 16:38:40 +0000
committerbors <bors@rust-lang.org>2022-09-08 16:38:40 +0000
commit4e1a3da8f26ff72754618149078fd69a17080a95 (patch)
treecec7a423227fcffd85b3d766fe3c21f421c08a60
parent690955643504b0dea0d49ab5c9d5fd45ce96230f (diff)
parentc4eadab016cf16bd32b90ba7ecc71361592a4f28 (diff)
downloadrust-4e1a3da8f26ff72754618149078fd69a17080a95.tar.gz
rust-4e1a3da8f26ff72754618149078fd69a17080a95.zip
Auto merge of #13158 - jonas-schievink:inlayhint-links, r=jonas-schievink
feat: make clicking a closing brace inlay hint go to the opening brace
-rw-r--r--crates/ide/src/inlay_hints.rs182
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/rust-analyzer/src/handlers.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs57
4 files changed, 192 insertions, 53 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index e9034daefa8..d1b1d2c331a 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 use either::Either;
 use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
 use ide_db::{
@@ -69,7 +71,7 @@ pub enum InlayKind {
 pub struct InlayHint {
     pub range: TextRange,
     pub kind: InlayKind,
-    pub label: String,
+    pub label: InlayHintLabel,
     pub tooltip: Option<InlayTooltip>,
 }
 
@@ -80,6 +82,83 @@ pub enum InlayTooltip {
     HoverOffset(FileId, TextSize),
 }
 
+pub struct InlayHintLabel {
+    pub parts: Vec<InlayHintLabelPart>,
+}
+
+impl InlayHintLabel {
+    pub fn as_simple_str(&self) -> Option<&str> {
+        match &*self.parts {
+            [part] => part.as_simple_str(),
+            _ => None,
+        }
+    }
+
+    pub fn prepend_str(&mut self, s: &str) {
+        match &mut *self.parts {
+            [part, ..] if part.as_simple_str().is_some() => part.text = format!("{s}{}", part.text),
+            _ => self.parts.insert(0, InlayHintLabelPart { text: s.into(), linked_location: None }),
+        }
+    }
+
+    pub fn append_str(&mut self, s: &str) {
+        match &mut *self.parts {
+            [.., part] if part.as_simple_str().is_some() => part.text.push_str(s),
+            _ => self.parts.push(InlayHintLabelPart { text: s.into(), linked_location: None }),
+        }
+    }
+}
+
+impl From<String> for InlayHintLabel {
+    fn from(s: String) -> Self {
+        Self { parts: vec![InlayHintLabelPart { text: s, linked_location: None }] }
+    }
+}
+
+impl fmt::Display for InlayHintLabel {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.parts.iter().map(|part| &part.text).format(""))
+    }
+}
+
+impl fmt::Debug for InlayHintLabel {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(&self.parts).finish()
+    }
+}
+
+pub struct InlayHintLabelPart {
+    pub text: String,
+    /// Source location represented by this label part. The client will use this to fetch the part's
+    /// hover tooltip, and Ctrl+Clicking the label part will navigate to the definition the location
+    /// refers to (not necessarily the location itself).
+    /// When setting this, no tooltip must be set on the containing hint, or VS Code will display
+    /// them both.
+    pub linked_location: Option<FileRange>,
+}
+
+impl InlayHintLabelPart {
+    pub fn as_simple_str(&self) -> Option<&str> {
+        match self {
+            Self { text, linked_location: None } => Some(text),
+            _ => None,
+        }
+    }
+}
+
+impl fmt::Debug for InlayHintLabelPart {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self.as_simple_str() {
+            Some(string) => string.fmt(f),
+            None => f
+                .debug_struct("InlayHintLabelPart")
+                .field("text", &self.text)
+                .field("linked_location", &self.linked_location)
+                .finish(),
+        }
+    }
+}
+
 // Feature: Inlay Hints
 //
 // rust-analyzer shows additional information inline with the source code.
@@ -192,10 +271,10 @@ fn closing_brace_hints(
 ) -> Option<()> {
     let min_lines = config.closing_brace_hints_min_lines?;
 
-    let name = |it: ast::Name| it.syntax().text_range().start();
+    let name = |it: ast::Name| it.syntax().text_range();
 
     let mut closing_token;
-    let (label, name_offset) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
+    let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
         closing_token = item_list.r_curly_token()?;
 
         let parent = item_list.syntax().parent()?;
@@ -205,11 +284,11 @@ fn closing_brace_hints(
                     let imp = sema.to_def(&imp)?;
                     let ty = imp.self_ty(sema.db);
                     let trait_ = imp.trait_(sema.db);
-
-                    (match trait_ {
+                    let hint_text = match trait_ {
                         Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
                         None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
-                    }, None)
+                    };
+                    (hint_text, None)
                 },
                 ast::Trait(tr) => {
                     (format!("trait {}", tr.name()?), tr.name().map(name))
@@ -253,7 +332,7 @@ fn closing_brace_hints(
 
         (
             format!("{}!", mac.path()?),
-            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range().start()),
+            mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
         )
     } else {
         return None;
@@ -278,11 +357,12 @@ fn closing_brace_hints(
         return None;
     }
 
+    let linked_location = name_range.map(|range| FileRange { file_id, range });
     acc.push(InlayHint {
         range: closing_token.text_range(),
         kind: InlayKind::ClosingBraceHint,
-        label,
-        tooltip: name_offset.map(|it| InlayTooltip::HoverOffset(file_id, it)),
+        label: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }] },
+        tooltip: None, // provided by label part location
     });
 
     None
@@ -311,7 +391,7 @@ fn implicit_static_hints(
             acc.push(InlayHint {
                 range: t.text_range(),
                 kind: InlayKind::LifetimeHint,
-                label: "'static".to_owned(),
+                label: "'static".to_owned().into(),
                 tooltip: Some(InlayTooltip::String("Elided static lifetime".into())),
             });
         }
@@ -329,10 +409,10 @@ fn fn_lifetime_fn_hints(
         return None;
     }
 
-    let mk_lt_hint = |t: SyntaxToken, label| InlayHint {
+    let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint {
         range: t.text_range(),
         kind: InlayKind::LifetimeHint,
-        label,
+        label: label.into(),
         tooltip: Some(InlayTooltip::String("Elided lifetime".into())),
     };
 
@@ -486,7 +566,8 @@ fn fn_lifetime_fn_hints(
                     "{}{}",
                     allocated_lifetimes.iter().format(", "),
                     if is_empty { "" } else { ", " }
-                ),
+                )
+                .into(),
                 tooltip: Some(InlayTooltip::String("Elided lifetimes".into())),
             });
         }
@@ -535,7 +616,8 @@ fn closure_ret_hints(
         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()),
+            .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
+            .into(),
         tooltip: Some(InlayTooltip::HoverRanged(file_id, param_list.syntax().text_range())),
     });
     Some(())
@@ -562,7 +644,7 @@ fn reborrow_hints(
     acc.push(InlayHint {
         range: expr.syntax().text_range(),
         kind: InlayKind::ImplicitReborrowHint,
-        label: label.to_string(),
+        label: label.to_string().into(),
         tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())),
     });
     Some(())
@@ -620,9 +702,9 @@ fn chaining_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()
-                }),
+                label: hint_iterator(sema, &famous_defs, config, &ty)
+                    .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
+                    .into(),
                 tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
             });
         }
@@ -674,7 +756,7 @@ fn param_name_hints(
             InlayHint {
                 range,
                 kind: InlayKind::ParameterHint,
-                label: param_name,
+                label: param_name.into(),
                 tooltip: tooltip.map(|it| InlayTooltip::HoverOffset(it.file_id, it.range.start())),
             }
         });
@@ -705,7 +787,7 @@ fn binding_mode_hints(
         acc.push(InlayHint {
             range,
             kind: InlayKind::BindingModeHint,
-            label: r.to_string(),
+            label: r.to_string().into(),
             tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
         });
     });
@@ -720,7 +802,7 @@ fn binding_mode_hints(
             acc.push(InlayHint {
                 range,
                 kind: InlayKind::BindingModeHint,
-                label: bm.to_string(),
+                label: bm.to_string().into(),
                 tooltip: Some(InlayTooltip::String("Inferred binding mode".into())),
             });
         }
@@ -772,7 +854,7 @@ fn bind_pat_hints(
             None => pat.syntax().text_range(),
         },
         kind: InlayKind::TypeHint,
-        label,
+        label: label.into(),
         tooltip: pat
             .name()
             .map(|it| it.syntax().text_range())
@@ -2223,7 +2305,9 @@ fn main() {
                     InlayHint {
                         range: 147..172,
                         kind: ChainingHint,
-                        label: "B",
+                        label: [
+                            "B",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2236,7 +2320,9 @@ fn main() {
                     InlayHint {
                         range: 147..154,
                         kind: ChainingHint,
-                        label: "A",
+                        label: [
+                            "A",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2294,7 +2380,9 @@ fn main() {
                     InlayHint {
                         range: 143..190,
                         kind: ChainingHint,
-                        label: "C",
+                        label: [
+                            "C",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2307,7 +2395,9 @@ fn main() {
                     InlayHint {
                         range: 143..179,
                         kind: ChainingHint,
-                        label: "B",
+                        label: [
+                            "B",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2350,7 +2440,9 @@ fn main() {
                     InlayHint {
                         range: 246..283,
                         kind: ChainingHint,
-                        label: "B<X<i32, bool>>",
+                        label: [
+                            "B<X<i32, bool>>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2363,7 +2455,9 @@ fn main() {
                     InlayHint {
                         range: 246..265,
                         kind: ChainingHint,
-                        label: "A<X<i32, bool>>",
+                        label: [
+                            "A<X<i32, bool>>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2408,7 +2502,9 @@ fn main() {
                     InlayHint {
                         range: 174..241,
                         kind: ChainingHint,
-                        label: "impl Iterator<Item = ()>",
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2421,7 +2517,9 @@ fn main() {
                     InlayHint {
                         range: 174..224,
                         kind: ChainingHint,
-                        label: "impl Iterator<Item = ()>",
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2434,7 +2532,9 @@ fn main() {
                     InlayHint {
                         range: 174..206,
                         kind: ChainingHint,
-                        label: "impl Iterator<Item = ()>",
+                        label: [
+                            "impl Iterator<Item = ()>",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2447,7 +2547,9 @@ fn main() {
                     InlayHint {
                         range: 174..189,
                         kind: ChainingHint,
-                        label: "&mut MyIter",
+                        label: [
+                            "&mut MyIter",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2489,7 +2591,9 @@ fn main() {
                     InlayHint {
                         range: 124..130,
                         kind: TypeHint,
-                        label: "Struct",
+                        label: [
+                            "Struct",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2502,7 +2606,9 @@ fn main() {
                     InlayHint {
                         range: 145..185,
                         kind: ChainingHint,
-                        label: "Struct",
+                        label: [
+                            "Struct",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2515,7 +2621,9 @@ fn main() {
                     InlayHint {
                         range: 145..168,
                         kind: ChainingHint,
-                        label: "Struct",
+                        label: [
+                            "Struct",
+                        ],
                         tooltip: Some(
                             HoverRanged(
                                 FileId(
@@ -2528,7 +2636,9 @@ fn main() {
                     InlayHint {
                         range: 222..228,
                         kind: ParameterHint,
-                        label: "self",
+                        label: [
+                            "self",
+                        ],
                         tooltip: Some(
                             HoverOffset(
                                 FileId(
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index d61d69a090b..0552330814a 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -82,8 +82,8 @@ pub use crate::{
     highlight_related::{HighlightRelatedConfig, HighlightedRange},
     hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult},
     inlay_hints::{
-        ClosureReturnTypeHints, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
-        LifetimeElisionHints, ReborrowHints,
+        ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
+        InlayTooltip, LifetimeElisionHints, ReborrowHints,
     },
     join_lines::JoinLinesConfig,
     markup::Markup,
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index d9b669afbe8..e79cf3d3fd6 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -1362,7 +1362,7 @@ pub(crate) fn handle_inlay_hints(
             .map(|it| {
                 to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it)
             })
-            .collect(),
+            .collect::<Result<Vec<_>>>()?,
     ))
 }
 
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 102cd602950..e083b9d0e33 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -9,8 +9,9 @@ use ide::{
     Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
     CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
     Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint,
-    InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity,
-    SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
+    InlayHintLabel, InlayKind, Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable,
+    Severity, SignatureHelp, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange,
+    TextSize,
 };
 use itertools::Itertools;
 use serde_json::to_value;
@@ -426,9 +427,16 @@ pub(crate) fn inlay_hint(
     snap: &GlobalStateSnapshot,
     line_index: &LineIndex,
     render_colons: bool,
-    inlay_hint: InlayHint,
-) -> lsp_types::InlayHint {
-    lsp_types::InlayHint {
+    mut inlay_hint: InlayHint,
+) -> Result<lsp_types::InlayHint> {
+    match inlay_hint.kind {
+        InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"),
+        InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "),
+        InlayKind::ClosureReturnTypeHint => inlay_hint.label.prepend_str(" -> "),
+        _ => {}
+    }
+
+    Ok(lsp_types::InlayHint {
         position: match inlay_hint.kind {
             // before annotated thing
             InlayKind::ParameterHint
@@ -459,15 +467,9 @@ pub(crate) fn inlay_hint(
             | InlayKind::ImplicitReborrowHint
             | InlayKind::TypeHint
             | InlayKind::ClosingBraceHint => false,
-            InlayKind::BindingModeHint => inlay_hint.label != "&",
+            InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"),
             InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
         }),
-        label: lsp_types::InlayHintLabel::String(match inlay_hint.kind {
-            InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
-            InlayKind::TypeHint if render_colons => format!(": {}", inlay_hint.label),
-            InlayKind::ClosureReturnTypeHint => format!(" -> {}", inlay_hint.label),
-            _ => inlay_hint.label.clone(),
-        }),
         kind: match inlay_hint.kind {
             InlayKind::ParameterHint => Some(lsp_types::InlayHintKind::PARAMETER),
             InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
@@ -506,9 +508,36 @@ pub(crate) fn inlay_hint(
         })(),
         tooltip: Some(match inlay_hint.tooltip {
             Some(ide::InlayTooltip::String(s)) => lsp_types::InlayHintTooltip::String(s),
-            _ => lsp_types::InlayHintTooltip::String(inlay_hint.label),
+            _ => lsp_types::InlayHintTooltip::String(inlay_hint.label.to_string()),
         }),
-    }
+        label: inlay_hint_label(snap, inlay_hint.label)?,
+    })
+}
+
+fn inlay_hint_label(
+    snap: &GlobalStateSnapshot,
+    label: InlayHintLabel,
+) -> Result<lsp_types::InlayHintLabel> {
+    Ok(match label.as_simple_str() {
+        Some(s) => lsp_types::InlayHintLabel::String(s.into()),
+        None => lsp_types::InlayHintLabel::LabelParts(
+            label
+                .parts
+                .into_iter()
+                .map(|part| {
+                    Ok(lsp_types::InlayHintLabelPart {
+                        value: part.text,
+                        tooltip: None,
+                        location: part
+                            .linked_location
+                            .map(|range| location(snap, range))
+                            .transpose()?,
+                        command: None,
+                    })
+                })
+                .collect::<Result<Vec<_>>>()?,
+        ),
+    })
 }
 
 static TOKEN_RESULT_COUNTER: AtomicU32 = AtomicU32::new(1);