refactor hover

change struct_rest_pat to guarentee a HoverResult

Move token and node matching out of struct_rest_pat into hover
This commit is contained in:
Kartavya Vashishtha 2022-11-19 21:22:10 +05:30
parent 8b17681058
commit 87658c8173
No known key found for this signature in database
GPG Key ID: A50012C2324E5DF0
2 changed files with 66 additions and 61 deletions

View File

@ -127,6 +127,7 @@ pub(crate) fn hover(
original_token.parent().and_then(ast::TokenTree::cast),
Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))
);
// prefer descending the same token kind in attribute expansions, in normal macros text
// equivalency is more important
let descended = if in_attr {
@ -135,54 +136,68 @@ pub(crate) fn hover(
sema.descend_into_macros_with_same_text(original_token.clone())
};
// FIXME: Definition should include known lints and the like instead of having this special case here
let hovered_lint = descended.iter().find_map(|token| {
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
render::try_for_lint(&attr, token)
});
if let Some(res) = hovered_lint {
return Some(RangeInfo::new(original_token.text_range(), res));
}
// try lint hover
let result = descended
.iter()
.filter_map(|token| {
let node = token.parent()?;
let class = IdentClass::classify_token(sema, token)?;
if let IdentClass::Operator(OperatorClass::Await(_)) = class {
// It's better for us to fall back to the keyword hover here,
// rendering poll is very confusing
return None;
}
Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
.find_map(|token| {
// FIXME: Definition should include known lints and the like instead of having this special case here
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
render::try_for_lint(&attr, token)
})
.flatten()
.unique_by(|&(def, _)| def)
.filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
.reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
acc.actions.extend(actions);
acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
acc
// try item definitions
.or_else(|| {
descended
.iter()
.filter_map(|token| {
let node = token.parent()?;
let class = IdentClass::classify_token(sema, token)?;
if let IdentClass::Operator(OperatorClass::Await(_)) = class {
// It's better for us to fall back to the keyword hover here,
// rendering poll is very confusing
return None;
}
Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
})
.flatten()
.unique_by(|&(def, _)| def)
.filter_map(|(def, node)| hover_for_definition(sema, file_id, def, &node, config))
.reduce(|mut acc: HoverResult, HoverResult { markup, actions }| {
acc.actions.extend(actions);
acc.markup = Markup::from(format!("{}\n---\n{}", acc.markup, markup));
acc
})
})
// try keywords
.or_else(|| {
descended.iter().find_map(|token| render::keyword(sema, config, token))
})
// try rest item hover
.or_else(|| {
descended.iter().find_map(|token| {
if token.kind() != DOT2 {
return None;
}
let record_pat_field_list =
token.parent_ancestors().find_map(ast::RecordPatFieldList::cast)?;
let record_pat =
record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?;
Some(render::struct_rest_pat(sema, config, &record_pat))
})
});
if result.is_none() {
// fallbacks, show keywords or types
let res = descended.iter().find_map(|token| render::keyword(sema, config, token));
if let Some(res) = res {
return Some(RangeInfo::new(original_token.text_range(), res));
}
let res = descended
.iter()
.find_map(|token| hover_type_fallback(sema, config, token, &original_token));
if let Some(_) = res {
return res;
}
}
result.map(|mut res: HoverResult| {
res.actions = dedupe_or_merge_hover_actions(res.actions);
RangeInfo::new(original_token.text_range(), res)
})
result
.map(|mut res: HoverResult| {
res.actions = dedupe_or_merge_hover_actions(res.actions);
RangeInfo::new(original_token.text_range(), res)
})
// fallback to type hover if there aren't any other suggestions
// this finds its own range instead of using the closest token's range
.or_else(|| {
descended.iter().find_map(|token| hover_type_fallback(sema, config, token, &token))
})
}
pub(crate) fn hover_for_definition(
@ -268,8 +283,7 @@ fn hover_type_fallback(
}
};
let res = render::type_info(sema, config, &expr_or_pat)
.or_else(|| render::struct_rest_pat(sema, config, &expr_or_pat))?;
let res = render::type_info(sema, config, &expr_or_pat)?;
let range = sema
.original_range_opt(&node)

View File

@ -252,24 +252,15 @@ pub(super) fn keyword(
Some(HoverResult { markup, actions })
}
/// Returns missing types in a record pattern.
/// Only makes sense when there's a rest pattern in the record pattern.
/// i.e. `let S {a, ..} = S {a: 1, b: 2}`
pub(super) fn struct_rest_pat(
sema: &Semantics<'_, RootDatabase>,
config: &HoverConfig,
expr_or_pat: &Either<ast::Expr, ast::Pat>,
) -> Option<HoverResult> {
let pat = expr_or_pat.as_ref().right()?;
let mut ancestors = pat.syntax().ancestors();
let _record_pat_field_list = ancestors.next()?;
let record_pat = ancestors.next()?;
let pattern = sema
.find_nodes_at_offset_with_descend::<RecordPat>(
&record_pat,
record_pat.text_range().start(),
)
.next()?;
let missing_fields = sema.record_pattern_missing_fields(&pattern);
pattern: &RecordPat,
) -> HoverResult {
let missing_fields = sema.record_pattern_missing_fields(pattern);
// if there are no missing fields, the end result is a hover that shows ".."
// should be left in to indicate that there are no more fields in the pattern
@ -302,7 +293,7 @@ pub(super) fn struct_rest_pat(
}
};
res.actions.push(HoverAction::goto_type_from_targets(sema.db, targets));
Some(res)
res
}
pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option<HoverResult> {