From 87658c81731cd99b80bcd32d2188890f2ca22291 Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Sat, 19 Nov 2022 21:22:10 +0530 Subject: [PATCH] refactor hover change struct_rest_pat to guarentee a HoverResult Move token and node matching out of struct_rest_pat into hover --- crates/ide/src/hover.rs | 104 +++++++++++++++++++-------------- crates/ide/src/hover/render.rs | 23 +++----- 2 files changed, 66 insertions(+), 61 deletions(-) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index cc64b0c3db39..1e07e7dbac9c 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -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) diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index b66c029257dd..fb00a40f9619 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -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, -) -> Option { - 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::( - &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 {