Skip to content

Commit

Permalink
Auto merge of rust-lang#16822 - Veykril:inlays, r=Veykril
Browse files Browse the repository at this point in the history
fix: Make inlay hint resolving work better for inlays targetting the same position
  • Loading branch information
bors committed Mar 18, 2024
2 parents f6e2895 + 4a93368 commit f40c7d8
Show file tree
Hide file tree
Showing 19 changed files with 114 additions and 91 deletions.
96 changes: 60 additions & 36 deletions crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
fmt::{self, Write},
hash::{BuildHasher, BuildHasherDefault},
mem::take,
};

Expand All @@ -8,7 +9,7 @@ use hir::{
known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef,
ModuleDefId, Semantics,
};
use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase};
use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase};
use itertools::Itertools;
use smallvec::{smallvec, SmallVec};
use stdx::never;
Expand Down Expand Up @@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode {
PreferPostfix,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum InlayKind {
Adjustment,
BindingMode,
Expand All @@ -132,7 +133,7 @@ pub enum InlayKind {
RangeExclusive,
}

#[derive(Debug)]
#[derive(Debug, Hash)]
pub enum InlayHintPosition {
Before,
After,
Expand All @@ -151,13 +152,23 @@ pub struct InlayHint {
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<TextEdit>,
pub needs_resolve: bool,
}

impl std::hash::Hash for InlayHint {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.range.hash(state);
self.position.hash(state);
self.pad_left.hash(state);
self.pad_right.hash(state);
self.kind.hash(state);
self.label.hash(state);
self.text_edit.is_some().hash(state);
}
}

impl InlayHint {
fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
needs_resolve: false,
range,
kind,
label: InlayHintLabel::from(")"),
Expand All @@ -167,9 +178,9 @@ impl InlayHint {
pad_right: false,
}
}

fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint {
InlayHint {
needs_resolve: false,
range,
kind,
label: InlayHintLabel::from("("),
Expand All @@ -179,15 +190,19 @@ impl InlayHint {
pad_right: false,
}
}

pub fn needs_resolve(&self) -> bool {
self.text_edit.is_some() || self.label.needs_resolve()
}
}

#[derive(Debug)]
#[derive(Debug, Hash)]
pub enum InlayTooltip {
String(String),
Markdown(String),
}

#[derive(Default)]
#[derive(Default, Hash)]
pub struct InlayHintLabel {
pub parts: SmallVec<[InlayHintLabelPart; 1]>,
}
Expand Down Expand Up @@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel {
}
}

#[derive(Hash)]
pub struct InlayHintLabelPart {
pub text: String,
/// Source location represented by this label part. The client will use this to fetch the part's
Expand Down Expand Up @@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> {

impl HirWrite for InlayHintLabelBuilder<'_> {
fn start_location_link(&mut self, def: ModuleDefId) {
if self.location.is_some() {
never!("location link is already started");
}
never!(self.location.is_some(), "location link is already started");
self.make_new_part();
let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
let location = location.call_site();
Expand Down Expand Up @@ -425,11 +439,6 @@ fn ty_to_text_edit(
Some(builder.finish())
}

pub enum RangeLimit {
Fixed(TextRange),
NearestParent(TextSize),
}

// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
Expand All @@ -451,7 +460,7 @@ pub enum RangeLimit {
pub(crate) fn inlay_hints(
db: &RootDatabase,
file_id: FileId,
range_limit: Option<RangeLimit>,
range_limit: Option<TextRange>,
config: &InlayHintsConfig,
) -> Vec<InlayHint> {
let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered();
Expand All @@ -466,38 +475,53 @@ pub(crate) fn inlay_hints(

let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
match range_limit {
Some(RangeLimit::Fixed(range)) => match file.covering_element(range) {
Some(range) => match file.covering_element(range) {
NodeOrToken::Token(_) => return acc,
NodeOrToken::Node(n) => n
.descendants()
.filter(|descendant| range.intersect(descendant.text_range()).is_some())
.for_each(hints),
},
Some(RangeLimit::NearestParent(position)) => {
match file.token_at_offset(position).left_biased() {
Some(token) => {
if let Some(parent_block) =
token.parent_ancestors().find_map(ast::BlockExpr::cast)
{
parent_block.syntax().descendants().for_each(hints)
} else if let Some(parent_item) =
token.parent_ancestors().find_map(ast::Item::cast)
{
parent_item.syntax().descendants().for_each(hints)
} else {
return acc;
}
}
None => return acc,
}
}
None => file.descendants().for_each(hints),
};
}

acc
}

pub(crate) fn inlay_hints_resolve(
db: &RootDatabase,
file_id: FileId,
position: TextSize,
hash: u64,
config: &InlayHintsConfig,
) -> Option<InlayHint> {
let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered();
let sema = Semantics::new(db);
let file = sema.parse(file_id);
let file = file.syntax();

let scope = sema.scope(file)?;
let famous_defs = FamousDefs(&sema, scope.krate());
let mut acc = Vec::new();

let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
match file.token_at_offset(position).left_biased() {
Some(token) => {
if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) {
parent_block.syntax().descendants().for_each(hints)
} else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) {
parent_item.syntax().descendants().for_each(hints)
} else {
return None;
}
}
None => return None,
}

acc.into_iter().find(|hint| BuildHasherDefault::<FxHasher>::default().hash_one(hint) == hash)
}

fn hints(
hints: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ pub(super) fn hints(
None,
);
acc.push(InlayHint {
needs_resolve: label.needs_resolve(),
range: expr.syntax().text_range(),
pad_left: false,
pad_right: false,
Expand Down
9 changes: 2 additions & 7 deletions crates/ide/src/inlay_hints/bind_pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ 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,
Expand Down Expand Up @@ -177,11 +176,7 @@ mod tests {
use syntax::{TextRange, TextSize};
use test_utils::extract_annotations;

use crate::{
fixture,
inlay_hints::{InlayHintsConfig, RangeLimit},
ClosureReturnTypeHints,
};
use crate::{fixture, inlay_hints::InlayHintsConfig, ClosureReturnTypeHints};

use crate::inlay_hints::tests::{
check, check_edit, check_no_edit, check_with_config, DISABLED_CONFIG, TEST_CONFIG,
Expand Down Expand Up @@ -404,7 +399,7 @@ fn main() {
.inlay_hints(
&InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
file_id,
Some(RangeLimit::Fixed(TextRange::new(TextSize::from(500), TextSize::from(600)))),
Some(TextRange::new(TextSize::from(500), TextSize::from(600))),
)
.unwrap();
let actual =
Expand Down
2 changes: 0 additions & 2 deletions crates/ide/src/inlay_hints/binding_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ pub(super) fn hints(
_ => return,
};
acc.push(InlayHint {
needs_resolve: false,
range,
kind: InlayKind::BindingMode,
label: r.into(),
Expand All @@ -69,7 +68,6 @@ 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.into(),
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ 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,
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/closing_brace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ 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),
Expand Down
5 changes: 0 additions & 5 deletions crates/ide/src/inlay_hints/closure_captures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ 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::from("move"),
Expand All @@ -45,7 +44,6 @@ pub(super) fn hints(
}
};
acc.push(InlayHint {
needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from("("),
Expand Down Expand Up @@ -79,7 +77,6 @@ pub(super) fn hints(
}),
);
acc.push(InlayHint {
needs_resolve: label.needs_resolve(),
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label,
Expand All @@ -91,7 +88,6 @@ pub(super) fn hints(

if idx != last {
acc.push(InlayHint {
needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(", "),
Expand All @@ -103,7 +99,6 @@ pub(super) fn hints(
}
}
acc.push(InlayHint {
needs_resolve: false,
range: move_kw_range,
kind: InlayKind::ClosureCapture,
label: InlayHintLabel::from(")"),
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/closure_ret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ 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,
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/discriminant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ fn variant_hints(
None,
);
acc.push(InlayHint {
needs_resolve: label.needs_resolve(),
range: match eq_token {
Some(t) => range.cover(t.text_range()),
_ => range,
Expand Down
3 changes: 0 additions & 3 deletions crates/ide/src/inlay_hints/fn_lifetime_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ 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(),
Expand Down Expand Up @@ -184,7 +183,6 @@ 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!(
Expand All @@ -200,7 +198,6 @@ 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(),
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/implicit_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,6 @@ pub(super) fn hints(
pad_left: true,
pad_right: true,
kind: InlayKind::Drop,
needs_resolve: label.needs_resolve(),
label,
text_edit: None,
})
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/implicit_static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ 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".into(),
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/param_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ 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,
Expand Down
1 change: 0 additions & 1 deletion crates/ide/src/inlay_hints/range_exclusive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint {
kind: crate::InlayKind::RangeExclusive,
label: crate::InlayHintLabel::from("<"),
text_edit: None,
needs_resolve: false,
}
}

Expand Down
Loading

0 comments on commit f40c7d8

Please sign in to comment.