Auto merge of #18008 - Veykril:inlay-hints-resolve, r=Veykril

internal: Improve inlay hint resolution reliability

The payload now ships the range the inlay hint ought to be triggered for instead of trying to estimate it from its position which is somewhat brittle
This commit is contained in:
bors 2024-08-30 13:59:05 +00:00
commit 1cb426654a
20 changed files with 282 additions and 216 deletions

View file

@ -15,7 +15,7 @@ use span::{Edition, EditionedFileId};
use stdx::never;
use syntax::{
ast::{self, AstNode},
match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, WalkEvent,
};
use text_edit::TextEdit;
@ -36,6 +36,192 @@ mod implicit_static;
mod param_name;
mod range_exclusive;
// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
// Editors usually render this using read-only virtual text snippets interspersed with code.
//
// rust-analyzer by default shows hints for
//
// * types of local variables
// * names of function arguments
// * names of const generic parameters
// * types of chained expressions
//
// Optionally, one can enable additional hints for
//
// * return types of closure expressions
// * elided lifetimes
// * compiler inserted reborrows
// * names of generic type and lifetime parameters
//
// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
// any of the
// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria]
// are met:
//
// * the parameter name is a suffix of the function's name
// * the argument is a qualified constructing or call expression where the qualifier is an ADT
// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
// of argument with _ splitting it off
// * the parameter name starts with `ra_fixture`
// * the parameter name is a
// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name]
// in a unary function
// * the parameter name is a
// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character]
// in a unary function
//
// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
pub(crate) fn inlay_hints(
db: &RootDatabase,
file_id: FileId,
range_limit: Option<TextRange>,
config: &InlayHintsConfig,
) -> Vec<InlayHint> {
let _p = tracing::info_span!("inlay_hints").entered();
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
let file = sema.parse(file_id);
let file = file.syntax();
let mut acc = Vec::new();
let Some(scope) = sema.scope(file) else {
return acc;
};
let famous_defs = FamousDefs(&sema, scope.krate());
let parent_impl = &mut None;
let hints = |node| hints(&mut acc, parent_impl, &famous_defs, config, file_id, node);
match range_limit {
// FIXME: This can miss some hints that require the parent of the range to calculate
Some(range) => match file.covering_element(range) {
NodeOrToken::Token(_) => return acc,
NodeOrToken::Node(n) => n
.preorder()
.filter(|event| matches!(event, WalkEvent::Enter(node) if range.intersect(node.text_range()).is_some()))
.for_each(hints),
},
None => file.preorder().for_each(hints),
};
acc
}
pub(crate) fn inlay_hints_resolve(
db: &RootDatabase,
file_id: FileId,
resolve_range: TextRange,
hash: u64,
config: &InlayHintsConfig,
hasher: impl Fn(&InlayHint) -> u64,
) -> Option<InlayHint> {
let _p = tracing::info_span!("inlay_hints_resolve").entered();
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
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 parent_impl = &mut None;
let hints = |node| hints(&mut acc, parent_impl, &famous_defs, config, file_id, node);
let mut res = file.clone();
let res = loop {
res = match res.child_or_token_at_range(resolve_range) {
Some(NodeOrToken::Node(n)) if n.text_range() == resolve_range => break n,
Some(NodeOrToken::Node(n)) => n,
_ => break res,
};
};
res.preorder().for_each(hints);
acc.into_iter().find(|hint| hasher(hint) == hash)
}
fn hints(
hints: &mut Vec<InlayHint>,
parent_impl: &mut Option<ast::Impl>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
node: WalkEvent<SyntaxNode>,
) {
let node = match node {
WalkEvent::Enter(node) => node,
WalkEvent::Leave(n) => {
if ast::Impl::can_cast(n.kind()) {
parent_impl.take();
}
return;
}
};
closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
generic_param::hints(hints, sema, config, any_has_generic_args);
}
match_ast! {
match node {
ast::Expr(expr) => {
chaining::hints(hints, famous_defs, config, file_id, &expr);
adjustment::hints(hints, famous_defs, config, file_id, &expr);
match expr {
ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)),
ast::Expr::MethodCallExpr(it) => {
param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it))
}
ast::Expr::ClosureExpr(it) => {
closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
closure_ret::hints(hints, famous_defs, config, file_id, it)
},
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it),
_ => None,
}
},
ast::Pat(it) => {
binding_mode::hints(hints, famous_defs, config, file_id, &it);
match it {
ast::Pat::IdentPat(it) => {
bind_pat::hints(hints, famous_defs, config, file_id, &it);
}
ast::Pat::RangePat(it) => {
range_exclusive::hints(hints, famous_defs, config, file_id, it);
}
_ => {}
}
Some(())
},
ast::Item(it) => match it {
// FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
ast::Item::Impl(impl_) => {
*parent_impl = Some(impl_);
None
},
ast::Item::Fn(it) => {
implicit_drop::hints(hints, famous_defs, config, file_id, &it);
fn_lifetime_fn::hints(hints, famous_defs, config, file_id, it)
},
// static type elisions
ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)),
ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)),
ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
_ => None,
},
// FIXME: fn-ptr type, dyn fn type, and trait object type elisions
ast::Type(_) => None,
_ => None,
}
};
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig {
pub render_colons: bool,
@ -162,6 +348,9 @@ pub struct InlayHint {
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<TextEdit>,
/// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the
/// hint does not support resolving.
pub resolve_parent: Option<TextRange>,
}
impl std::hash::Hash for InlayHint {
@ -186,6 +375,7 @@ impl InlayHint {
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: None,
}
}
@ -198,11 +388,12 @@ impl InlayHint {
position: InlayHintPosition::Before,
pad_left: false,
pad_right: false,
resolve_parent: None,
}
}
pub fn needs_resolve(&self) -> bool {
self.text_edit.is_some() || self.label.needs_resolve()
pub fn needs_resolve(&self) -> Option<TextRange> {
self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
}
}
@ -434,190 +625,6 @@ fn label_of_ty(
Some(r)
}
fn ty_to_text_edit(
sema: &Semantics<'_, RootDatabase>,
node_for_hint: &SyntaxNode,
ty: &hir::Type,
offset_to_insert: TextSize,
prefix: String,
) -> Option<TextEdit> {
let scope = sema.scope(node_for_hint)?;
// FIXME: Limit the length and bail out on excess somehow?
let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
let mut builder = TextEdit::builder();
builder.insert(offset_to_insert, prefix);
builder.insert(offset_to_insert, rendered);
Some(builder.finish())
}
// Feature: Inlay Hints
//
// rust-analyzer shows additional information inline with the source code.
// Editors usually render this using read-only virtual text snippets interspersed with code.
//
// rust-analyzer by default shows hints for
//
// * types of local variables
// * names of function arguments
// * names of const generic parameters
// * types of chained expressions
//
// Optionally, one can enable additional hints for
//
// * return types of closure expressions
// * elided lifetimes
// * compiler inserted reborrows
// * names of generic type and lifetime parameters
//
// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
// any of the
// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria]
// are met:
//
// * the parameter name is a suffix of the function's name
// * the argument is a qualified constructing or call expression where the qualifier is an ADT
// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
// of argument with _ splitting it off
// * the parameter name starts with `ra_fixture`
// * the parameter name is a
// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name]
// in a unary function
// * the parameter name is a
// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character]
// in a unary function
//
// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
pub(crate) fn inlay_hints(
db: &RootDatabase,
file_id: FileId,
range_limit: Option<TextRange>,
config: &InlayHintsConfig,
) -> Vec<InlayHint> {
let _p = tracing::info_span!("inlay_hints").entered();
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
let file = sema.parse(file_id);
let file = file.syntax();
let mut acc = Vec::new();
if let Some(scope) = sema.scope(file) {
let famous_defs = FamousDefs(&sema, scope.krate());
let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
match range_limit {
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),
},
None => file.descendants().for_each(hints),
};
}
acc
}
pub(crate) fn inlay_hints_resolve(
db: &RootDatabase,
file_id: FileId,
position: TextSize,
hash: u64,
config: &InlayHintsConfig,
hasher: impl Fn(&InlayHint) -> u64,
) -> Option<InlayHint> {
let _p = tracing::info_span!("inlay_hints_resolve").entered();
let sema = Semantics::new(db);
let file_id = sema
.attach_first_edition(file_id)
.unwrap_or_else(|| EditionedFileId::current_edition(file_id));
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);
let token = file.token_at_offset(position).left_biased()?;
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;
}
acc.into_iter().find(|hint| hasher(hint) == hash)
}
fn hints(
hints: &mut Vec<InlayHint>,
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
node: SyntaxNode,
) {
closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
generic_param::hints(hints, sema, config, any_has_generic_args);
}
match_ast! {
match node {
ast::Expr(expr) => {
chaining::hints(hints, famous_defs, config, file_id, &expr);
adjustment::hints(hints, sema, config, file_id, &expr);
match expr {
ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)),
ast::Expr::MethodCallExpr(it) => {
param_name::hints(hints, sema, config, ast::Expr::from(it))
}
ast::Expr::ClosureExpr(it) => {
closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
closure_ret::hints(hints, famous_defs, config, file_id, it)
},
ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, config, it),
_ => None,
}
},
ast::Pat(it) => {
binding_mode::hints(hints, sema, config, &it);
match it {
ast::Pat::IdentPat(it) => {
bind_pat::hints(hints, famous_defs, config, file_id, &it);
}
ast::Pat::RangePat(it) => {
range_exclusive::hints(hints, config, it);
}
_ => {}
}
Some(())
},
ast::Item(it) => match it {
// FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
ast::Item::Impl(_) => None,
ast::Item::Fn(it) => {
implicit_drop::hints(hints, sema, config, file_id, &it);
fn_lifetime_fn::hints(hints, config, it)
},
// static type elisions
ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)),
ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)),
ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
_ => None,
},
// FIXME: fn-ptr type, dyn fn type, and trait object type elisions
ast::Type(_) => None,
_ => None,
}
};
}
/// Checks if the type is an Iterator from std::iter and returns the iterator trait and the item type of the concrete iterator.
fn hint_iterator(
sema: &Semantics<'_, RootDatabase>,
@ -653,6 +660,23 @@ fn hint_iterator(
None
}
fn ty_to_text_edit(
sema: &Semantics<'_, RootDatabase>,
node_for_hint: &SyntaxNode,
ty: &hir::Type,
offset_to_insert: TextSize,
prefix: String,
) -> Option<TextEdit> {
let scope = sema.scope(node_for_hint)?;
// FIXME: Limit the length and bail out on excess somehow?
let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
let mut builder = TextEdit::builder();
builder.insert(offset_to_insert, prefix);
builder.insert(offset_to_insert, rendered);
Some(builder.finish())
}
fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
matches!(closure.body(), Some(ast::Expr::BlockExpr(_)))
}

View file

@ -6,9 +6,8 @@
use either::Either;
use hir::{
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
Semantics,
};
use ide_db::RootDatabase;
use ide_db::famous_defs::FamousDefs;
use span::EditionedFileId;
use stdx::never;
@ -24,7 +23,7 @@ use crate::{
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
expr: &ast::Expr,
@ -156,6 +155,7 @@ pub(super) fn hints(
kind: InlayKind::Adjustment,
label,
text_edit: None,
resolve_parent: Some(expr.syntax().text_range()),
});
}
if !postfix && needs_inner_parens {

View file

@ -110,6 +110,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: !render_colons,
pad_right: false,
resolve_parent: Some(pat.syntax().text_range()),
});
Some(())

View file

@ -2,17 +2,19 @@
//! ```no_run
//! let /* & */ (/* ref */ x,) = &(0,);
//! ```
use hir::{Mutability, Semantics};
use ide_db::RootDatabase;
use hir::Mutability;
use ide_db::famous_defs::FamousDefs;
use span::EditionedFileId;
use syntax::ast::{self, AstNode};
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
pat: &ast::Pat,
) -> Option<()> {
if !config.binding_mode_hints {
@ -57,6 +59,7 @@ pub(super) fn hints(
position: InlayHintPosition::Before,
pad_left: false,
pad_right: mut_reference,
resolve_parent: Some(pat.syntax().text_range()),
});
});
match pat {
@ -75,6 +78,7 @@ pub(super) fn hints(
position: InlayHintPosition::Before,
pad_left: false,
pad_right: true,
resolve_parent: Some(pat.syntax().text_range()),
});
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {

View file

@ -67,6 +67,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: true,
pad_right: false,
resolve_parent: Some(expr.syntax().text_range()),
});
}
}

View file

@ -18,12 +18,13 @@ pub(super) fn hints(
sema: &Semantics<'_, RootDatabase>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
mut node: SyntaxNode,
original_node: SyntaxNode,
) -> Option<()> {
let min_lines = config.closing_brace_hints_min_lines?;
let name = |it: ast::Name| it.syntax().text_range();
let mut node = original_node.clone();
let mut closing_token;
let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
closing_token = item_list.r_curly_token()?;
@ -145,6 +146,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: true,
pad_right: false,
resolve_parent: Some(original_node.text_range()),
});
None

View file

@ -40,6 +40,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: Some(closure.syntax().text_range()),
});
range
}
@ -52,6 +53,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: None,
});
let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() {
@ -85,6 +87,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: Some(closure.syntax().text_range()),
});
if idx != last {
@ -96,6 +99,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: None,
});
}
}
@ -107,6 +111,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
resolve_parent: None,
});
Some(())

View file

@ -72,6 +72,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: Some(closure.syntax().text_range()),
});
Some(())
}

View file

@ -35,7 +35,7 @@ pub(super) fn enum_hints(
return None;
}
for variant in enum_.variant_list()?.variants() {
variant_hints(acc, sema, &variant);
variant_hints(acc, sema, &enum_, &variant);
}
Some(())
}
@ -43,6 +43,7 @@ pub(super) fn enum_hints(
fn variant_hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
enum_: &ast::Enum,
variant: &ast::Variant,
) -> Option<()> {
if variant.expr().is_some() {
@ -90,6 +91,7 @@ fn variant_hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: Some(enum_.syntax().text_range()),
});
Some(())

View file

@ -2,8 +2,9 @@
//! ```no_run
//! fn example/* <'0> */(a: &/* '0 */()) {}
//! ```
use ide_db::{syntax_helpers::node_ext::walk_ty, FxHashMap};
use ide_db::{famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap};
use itertools::Itertools;
use span::EditionedFileId;
use syntax::{
ast::{self, AstNode, HasGenericParams, HasName},
SyntaxToken,
@ -14,7 +15,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
func: ast::Fn,
) -> Option<()> {
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@ -29,6 +32,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
resolve_parent: None,
};
let param_list = func.param_list()?;
@ -195,6 +199,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
resolve_parent: None,
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
@ -205,6 +210,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
resolve_parent: None,
}),
}
Some(())

View file

@ -92,6 +92,7 @@ pub(crate) fn hints(
kind: InlayKind::GenericParameter,
label,
text_edit: None,
resolve_parent: Some(node.syntax().text_range()),
})
});

View file

@ -8,9 +8,9 @@
use hir::{
db::{DefDatabase as _, HirDatabase as _},
mir::{MirSpan, TerminatorKind},
ChalkTyInterner, DefWithBody, Semantics,
ChalkTyInterner, DefWithBody,
};
use ide_db::{FileRange, RootDatabase};
use ide_db::{famous_defs::FamousDefs, FileRange};
use span::EditionedFileId;
use syntax::{
@ -22,16 +22,16 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
def: &ast::Fn,
node: &ast::Fn,
) -> Option<()> {
if !config.implicit_drop_hints {
return None;
}
let def = sema.to_def(def)?;
let def = sema.to_def(node)?;
let def: DefWithBody = def.into();
let (hir, source_map) = sema.db.body_with_source_map(def.into());
@ -121,6 +121,7 @@ pub(super) fn hints(
kind: InlayKind::Drop,
label,
text_edit: None,
resolve_parent: Some(node.syntax().text_range()),
})
}
}

View file

@ -3,6 +3,8 @@
//! static S: &/* 'static */str = "";
//! ```
use either::Either;
use ide_db::famous_defs::FamousDefs;
use span::EditionedFileId;
use syntax::{
ast::{self, AstNode},
SyntaxKind,
@ -12,7 +14,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
statik_or_const: Either<ast::Static, ast::Const>,
) -> Option<()> {
if config.lifetime_elision_hints != LifetimeElisionHints::Always {
@ -38,6 +42,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
resolve_parent: None,
});
}
}

View file

@ -7,8 +7,9 @@ use std::fmt::Display;
use either::Either;
use hir::{Callable, Semantics};
use ide_db::RootDatabase;
use ide_db::{famous_defs::FamousDefs, RootDatabase};
use span::EditionedFileId;
use stdx::to_lower_snake_case;
use syntax::{
ast::{self, AstNode, HasArgList, HasName, UnaryOp},
@ -19,8 +20,9 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
expr: ast::Expr,
) -> Option<()> {
if !config.parameter_hints {
@ -60,6 +62,7 @@ pub(super) fn hints(
position: InlayHintPosition::Before,
pad_left: false,
pad_right: true,
resolve_parent: Some(expr.syntax().text_range()),
}
});

View file

@ -3,13 +3,17 @@
//! for i in 0../* < */10 {}
//! if let ../* < */100 = 50 {}
//! ```
use ide_db::famous_defs::FamousDefs;
use span::EditionedFileId;
use syntax::{ast, SyntaxToken, T};
use crate::{InlayHint, InlayHintsConfig};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
FamousDefs(_sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
range: impl ast::RangeItem,
) -> Option<()> {
(config.range_exclusive_hints && range.end().is_some())
@ -30,6 +34,7 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint {
kind: crate::InlayKind::RangeExclusive,
label: crate::InlayHintLabel::from("<"),
text_edit: None,
resolve_parent: None,
}
}

View file

@ -439,12 +439,12 @@ impl Analysis {
&self,
config: &InlayHintsConfig,
file_id: FileId,
position: TextSize,
resolve_range: TextRange,
hash: u64,
hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe,
) -> Cancellable<Option<InlayHint>> {
self.with_db(|db| {
inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher)
inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher)
})
}

View file

@ -1602,14 +1602,14 @@ pub(crate) fn handle_inlay_hints_resolve(
anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
let line_index = snap.file_line_index(file_id)?;
let hint_position = from_proto::offset(&line_index, original_hint.position)?;
let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?;
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_resolve(
&forced_resolve_inlay_hints_config,
file_id,
hint_position,
range,
hash,
|hint| {
std::hash::BuildHasher::hash_one(

View file

@ -819,6 +819,7 @@ pub struct InlayHintResolveData {
pub file_id: u32,
// This is a string instead of a u64 as javascript can't represent u64 fully
pub hash: String,
pub resolve_range: lsp_types::Range,
pub version: Option<i32>,
}

View file

@ -452,10 +452,13 @@ pub(crate) fn inlay_hint(
file_id: FileId,
mut inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
let resolve_hash = inlay_hint.needs_resolve().then(|| {
std::hash::BuildHasher::hash_one(
&std::hash::BuildHasherDefault::<FxHasher>::default(),
&inlay_hint,
let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| {
(
range,
std::hash::BuildHasher::hash_one(
&std::hash::BuildHasherDefault::<FxHasher>::default(),
&inlay_hint,
),
)
});
@ -465,7 +468,7 @@ pub(crate) fn inlay_hint(
.visual_studio_code_version()
// https://github.com/microsoft/vscode/issues/193124
.map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version))
&& resolve_hash.is_some()
&& resolve_range_and_hash.is_some()
&& fields_to_resolve.resolve_text_edits
{
something_to_resolve |= inlay_hint.text_edit.is_some();
@ -477,16 +480,17 @@ pub(crate) fn inlay_hint(
snap,
fields_to_resolve,
&mut something_to_resolve,
resolve_hash.is_some(),
resolve_range_and_hash.is_some(),
inlay_hint.label,
)?;
let data = match resolve_hash {
Some(hash) if something_to_resolve => Some(
let data = match resolve_range_and_hash {
Some((resolve_range, hash)) if something_to_resolve => Some(
to_value(lsp_ext::InlayHintResolveData {
file_id: file_id.index(),
hash: hash.to_string(),
version: snap.file_version(file_id),
resolve_range: range(line_index, resolve_range),
})
.unwrap(),
),

View file

@ -1,5 +1,5 @@
<!---
lsp/ext.rs hash: 3429c08745984b3d
lsp/ext.rs hash: c6e83d3d08d993de
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: