Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Make inlay hint resolving work better for inlays targetting the same position #16822

Merged
merged 2 commits into from Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
96 changes: 60 additions & 36 deletions crates/ide/src/inlay_hints.rs
@@ -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
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
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
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
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
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
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
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
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
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
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
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
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
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