Improve inlay hint resolution reliability
This commit is contained in:
parent
e77de38415
commit
be33a2e1bd
@ -15,7 +15,7 @@
|
||||
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 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 @@ fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint {
|
||||
position: InlayHintPosition::After,
|
||||
pad_left: false,
|
||||
pad_right: false,
|
||||
resolve_parent: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,11 +388,12 @@ fn opening_paren_before(kind: InlayKind, range: TextRange) -> 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 rec(
|
||||
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 @@
|
||||
|
||||
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 @@
|
||||
|
||||
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 @@
|
||||
|
||||
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 @@
|
||||
|
||||
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 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 @@
|
||||
|
||||
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 @@ pub fn inlay_hints_resolve(
|
||||
&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 New Issue
Block a user