mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
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:
commit
1cb426654a
20 changed files with 282 additions and 216 deletions
|
@ -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(_)))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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() => {
|
||||
|
|
|
@ -67,6 +67,7 @@ pub(super) fn hints(
|
|||
position: InlayHintPosition::After,
|
||||
pad_left: true,
|
||||
pad_right: false,
|
||||
resolve_parent: Some(expr.syntax().text_range()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -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(())
|
||||
|
|
|
@ -92,6 +92,7 @@ pub(crate) fn hints(
|
|||
kind: InlayKind::GenericParameter,
|
||||
label,
|
||||
text_edit: None,
|
||||
resolve_parent: Some(node.syntax().text_range()),
|
||||
})
|
||||
});
|
||||
|
||||
|
|
|
@ -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()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
),
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue