Auto merge of #13158 - jonas-schievink:inlayhint-links, r=jonas-schievink

feat: make clicking a closing brace inlay hint go to the opening brace
This commit is contained in:
bors 2022-09-08 16:38:40 +00:00
commit 4e1a3da8f2
4 changed files with 192 additions and 53 deletions

View file

@ -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(

View file

@ -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,

View file

@ -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<_>>>()?,
))
}

View file

@ -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);