fix: Fix lifetime elision inlay hints breaking for ranged requests

This commit is contained in:
Lukas Wirth 2024-08-31 10:51:13 +02:00
parent 16077975fa
commit 82f96b5e6a
2 changed files with 59 additions and 57 deletions

View file

@ -15,7 +15,7 @@ use span::{Edition, EditionedFileId};
use stdx::never; use stdx::never;
use syntax::{ use syntax::{
ast::{self, AstNode, HasGenericParams}, ast::{self, AstNode, HasGenericParams},
format_smolstr, match_ast, NodeOrToken, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent,
}; };
use text_edit::TextEdit; use text_edit::TextEdit;
@ -95,26 +95,27 @@ pub(crate) fn inlay_hints(
let famous_defs = FamousDefs(&sema, scope.krate()); let famous_defs = FamousDefs(&sema, scope.krate());
let ctx = &mut InlayHintCtx::default(); let ctx = &mut InlayHintCtx::default();
let hints = |node| hints(&mut acc, ctx, &famous_defs, config, file_id, node); let mut hints = |event| {
match range_limit { if let Some(node) = handle_event(ctx, event) {
// FIXME: This can miss some hints that require the parent of the range to calculate hints(&mut acc, ctx, &famous_defs, config, file_id, node);
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),
}; };
let mut preorder = file.preorder();
while let Some(event) = preorder.next() {
// FIXME: This can miss some hints that require the parent of the range to calculate
if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none())
{
preorder.skip_subtree();
continue;
}
hints(event);
}
acc acc
} }
#[derive(Default)] #[derive(Default)]
struct InlayHintCtx { struct InlayHintCtx {
lifetime_stacks: Vec<Vec<SmolStr>>, lifetime_stacks: Vec<Vec<SmolStr>>,
is_param_list: bool,
} }
pub(crate) fn inlay_hints_resolve( pub(crate) fn inlay_hints_resolve(
@ -138,20 +139,52 @@ pub(crate) fn inlay_hints_resolve(
let mut acc = Vec::new(); let mut acc = Vec::new();
let ctx = &mut InlayHintCtx::default(); let ctx = &mut InlayHintCtx::default();
let hints = |node| hints(&mut acc, ctx, &famous_defs, config, file_id, node); let mut hints = |event| {
if let Some(node) = handle_event(ctx, event) {
let mut res = file.clone(); hints(&mut acc, ctx, &famous_defs, config, file_id, node);
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);
let mut preorder = file.preorder();
while let Some(event) = preorder.next() {
// FIXME: This can miss some hints that require the parent of the range to calculate
if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none())
{
preorder.skip_subtree();
continue;
}
hints(event);
}
acc.into_iter().find(|hint| hasher(hint) == hash) acc.into_iter().find(|hint| hasher(hint) == hash)
} }
fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent<SyntaxNode>) -> Option<SyntaxNode> {
match node {
WalkEvent::Enter(node) => {
if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) {
let params = node
.generic_param_list()
.map(|it| {
it.lifetime_params()
.filter_map(|it| {
it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..]))
})
.collect()
})
.unwrap_or_default();
ctx.lifetime_stacks.push(params);
}
Some(node)
}
WalkEvent::Leave(n) => {
if ast::AnyHasGenericParams::can_cast(n.kind()) {
ctx.lifetime_stacks.pop();
}
None
}
}
}
// FIXME: At some point when our hir infra is fleshed out enough we should flip this and traverse the // FIXME: At some point when our hir infra is fleshed out enough we should flip this and traverse the
// HIR instead of the syntax tree. // HIR instead of the syntax tree.
fn hints( fn hints(
@ -160,35 +193,8 @@ fn hints(
famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig, config: &InlayHintsConfig,
file_id: EditionedFileId, file_id: EditionedFileId,
node: WalkEvent<SyntaxNode>, node: SyntaxNode,
) { ) {
let node = match node {
WalkEvent::Enter(node) => node,
WalkEvent::Leave(n) => {
if ast::AnyHasGenericParams::can_cast(n.kind()) {
ctx.lifetime_stacks.pop();
// pop
}
if ast::ParamList::can_cast(n.kind()) {
ctx.is_param_list = false;
// pop
}
return;
}
};
if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) {
let params = node
.generic_param_list()
.map(|it| {
it.lifetime_params()
.filter_map(|it| it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..])))
.collect()
})
.unwrap_or_default();
ctx.lifetime_stacks.push(params);
}
closing_brace::hints(hints, sema, config, file_id, node.clone()); closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
generic_param::hints(hints, sema, config, any_has_generic_args); generic_param::hints(hints, sema, config, any_has_generic_args);
@ -242,10 +248,6 @@ fn hints(
ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path), ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
_ => Some(()), _ => Some(()),
}, },
ast::ParamList(_) => {
ctx.is_param_list = true;
Some(())
},
_ => Some(()), _ => Some(()),
} }
}; };

View file

@ -532,7 +532,7 @@ fn fn_ptr(a: fn(&()) -> &fn(&()) -> &()) {}
//^^ for<'1> //^^ for<'1>
//^'1 //^'1
//^'1 //^'1
fn fn_ptr2(a: for<'a> fn(&()) -> &())) {} fn fn_ptr2(a: for<'a> fn(&()) -> &()) {}
//^'0, $ //^'0, $
//^'0 //^'0
//^'0 //^'0