diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index edf14a6f4b..dde98d40dc 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -58,7 +58,7 @@ pub enum HoverDocFormat { PlainText, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum HoverAction { Runnable(Runnable), Implementation(FilePosition), @@ -97,7 +97,7 @@ pub struct HoverGotoTypeData { } /// Contains the results when hovering over an item -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default, Hash, PartialEq, Eq)] pub struct HoverResult { pub markup: Markup, pub actions: Vec, @@ -200,21 +200,24 @@ fn hover_simple( | ((no_tt_parent as usize) << 3) }); - // TODO: WE should not try these step by step, instead to accommodate for macros we should run - // all of these in "parallel" and rank their results - let result = None - // try lint hover - .or_else(|| { - descended().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) - }) - }) - // try definitions - .or_else(|| { - descended() - .filter_map(|token| { + let mut res = vec![]; + // let mut merge_result = |next: HoverResult| { + // res.markup = Markup::from(format!("{}\n---\n{}", res.markup, next.markup)); + // res.actions.extend(next.actions); + // }; + for token in descended { + let lint_hover = (|| { + // 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) + })(); + if let Some(lint_hover) = lint_hover { + res.push(lint_hover); + continue; + } + let definitions = (|| { + Some( + 'a: { let node = token.parent()?; // special case macro calls, we wanna render the invoked arm index @@ -229,11 +232,11 @@ fn hover_simple( .and_then(ast::MacroCall::cast) { if let Some(macro_) = sema.resolve_macro_call(¯o_call) { - return Some(vec![( + break 'a vec![( Definition::Macro(macro_), sema.resolve_macro_call_arm(¯o_call), node, - )]); + )]; } } } @@ -242,88 +245,96 @@ fn hover_simple( match IdentClass::classify_node(sema, &node)? { // It's better for us to fall back to the keyword hover here, // rendering poll is very confusing - IdentClass::Operator(OperatorClass::Await(_)) => None, + IdentClass::Operator(OperatorClass::Await(_)) => return None, IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, .. - }) => Some(vec![(Definition::ExternCrateDecl(decl), None, node)]), + }) => { + vec![(Definition::ExternCrateDecl(decl), None, node)] + } - class => Some( + class => { multizip((class.definitions(), iter::repeat(None), iter::repeat(node))) - .collect::>(), - ), + .collect::>() + } } - }) - .flatten() + } + .into_iter() .unique_by(|&(def, _, _)| def) .map(|(def, macro_arm, node)| { hover_for_definition(sema, file_id, def, &node, macro_arm, config, edition) }) - .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { - acc.actions.extend(actions); - acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup)); - acc - }) - }) - // try keywords - .or_else(|| descended().find_map(|token| render::keyword(sema, config, token, edition))) - // try _ hovers - .or_else(|| descended().find_map(|token| render::underscore(sema, config, token, edition))) - // try rest pattern hover - .or_else(|| { - descended().find_map(|token| { - if token.kind() != DOT2 { - return None; - } + .collect::>(), + ) + })(); + if let Some(definitions) = definitions { + res.extend(definitions); + continue; + } + let keywords = || render::keyword(sema, config, &token, edition); + let underscore = || render::underscore(sema, config, &token, edition); + let rest_pat = || { + if token.kind() != DOT2 { + return None; + } - let rest_pat = token.parent().and_then(ast::RestPat::cast)?; - let record_pat_field_list = - rest_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast)?; + let rest_pat = token.parent().and_then(ast::RestPat::cast)?; + let record_pat_field_list = + rest_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast)?; - let record_pat = - record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?; + let record_pat = + record_pat_field_list.syntax().parent().and_then(ast::RecordPat::cast)?; - Some(render::struct_rest_pat(sema, config, &record_pat, edition)) - }) - }) - // try () call hovers - .or_else(|| { - descended().find_map(|token| { - if token.kind() != T!['('] && token.kind() != T![')'] { - return None; + Some(render::struct_rest_pat(sema, config, &record_pat, edition)) + }; + let call = || { + if token.kind() != T!['('] && token.kind() != T![')'] { + return None; + } + let arg_list = token.parent().and_then(ast::ArgList::cast)?.syntax().parent()?; + let call_expr = syntax::match_ast! { + match arg_list { + ast::CallExpr(expr) => expr.into(), + ast::MethodCallExpr(expr) => expr.into(), + _ => return None, } - let arg_list = token.parent().and_then(ast::ArgList::cast)?.syntax().parent()?; - let call_expr = syntax::match_ast! { - match arg_list { - ast::CallExpr(expr) => expr.into(), - ast::MethodCallExpr(expr) => expr.into(), - _ => return None, - } - }; - render::type_info_of(sema, config, &Either::Left(call_expr), edition) - }) - }) - // try closure - .or_else(|| { - descended().find_map(|token| { - if token.kind() != T![|] { - return None; - } - let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?; - render::closure_expr(sema, config, c, edition) - }) - }) - // tokens - .or_else(|| { + }; + render::type_info_of(sema, config, &Either::Left(call_expr), edition) + }; + let closure = || { + if token.kind() != T![|] { + return None; + } + let c = token.parent().and_then(|x| x.parent()).and_then(ast::ClosureExpr::cast)?; + render::closure_expr(sema, config, c, edition) + }; + let literal = || { render::literal(sema, original_token.clone(), edition) .map(|markup| HoverResult { markup, actions: vec![] }) - }); + }; + if let Some(result) = keywords() + .or_else(underscore) + .or_else(rest_pat) + .or_else(call) + .or_else(closure) + .or_else(literal) + { + res.push(result) + } + } - result.map(|mut res: HoverResult| { - res.actions = dedupe_or_merge_hover_actions(res.actions); - RangeInfo::new(original_token.text_range(), res) - }) + res.into_iter() + .unique() + .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { + acc.actions.extend(actions); + acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup)); + acc + }) + .map(|mut res: HoverResult| { + res.actions = dedupe_or_merge_hover_actions(res.actions); + RangeInfo::new(original_token.text_range(), res) + }) } fn hover_ranged( diff --git a/crates/ide/src/markup.rs b/crates/ide/src/markup.rs index 4a4e29fa33..750d125426 100644 --- a/crates/ide/src/markup.rs +++ b/crates/ide/src/markup.rs @@ -5,7 +5,7 @@ //! what is used by LSP, so let's keep it simple. use std::fmt; -#[derive(Default, Debug)] +#[derive(Clone, Default, Debug, Hash, PartialEq, Eq)] pub struct Markup { text: String, }