Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/highlight_related.rs')
| -rw-r--r-- | crates/ide/src/highlight_related.rs | 230 |
1 files changed, 124 insertions, 106 deletions
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index b1efd90d4e..a10deed42a 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -2,14 +2,10 @@ use std::iter; use hir::{db, DescendPreference, FilePosition, FileRange, HirFileId, InFile, Semantics}; use ide_db::{ - defs::{Definition, IdentClass}, - helpers::pick_best_token, - search::{FileReference, ReferenceCategory, SearchScope}, - syntax_helpers::node_ext::{ + defs::{Definition, IdentClass}, helpers::pick_best_token, search::{FileReference, ReferenceCategory, SearchScope}, syntax_helpers::node_ext::{ eq_label_lt, for_each_tail_expr, full_path_of_name_ref, is_closure_or_blk_with_modif, preorder_expr_with_ctx_checker, - }, - FxHashSet, RootDatabase, + }, FxHashMap, FxHashSet, RootDatabase }; use span::EditionedFileId; use syntax::{ @@ -19,7 +15,7 @@ use syntax::{ SyntaxToken, TextRange, WalkEvent, T, }; -use crate::{navigation_target::ToNav, NavigationTarget, TryToNav}; +use crate::{goto_definition, navigation_target::ToNav, NavigationTarget, TryToNav}; #[derive(PartialEq, Eq, Hash)] pub struct HighlightedRange { @@ -73,15 +69,19 @@ pub(crate) fn highlight_related( // most if not all of these should be re-implemented with information seeded from hir match token.kind() { T![?] if config.exit_points && token.parent().and_then(ast::TryExpr::cast).is_some() => { - highlight_exit_points(sema, token) + highlight_exit_points(sema, token).remove(&file_id) + } + T![fn] | T![return] | T![->] if config.exit_points => { + highlight_exit_points(sema, token).remove(&file_id) + } + T![await] | T![async] if config.yield_points => { + highlight_yield_points(sema, token).remove(&file_id) } - T![fn] | T![return] | T![->] if config.exit_points => highlight_exit_points(sema, token), - T![await] | T![async] if config.yield_points => highlight_yield_points(sema, token), T![for] if config.break_points && token.parent().and_then(ast::ForExpr::cast).is_some() => { - highlight_break_points(sema, token) + highlight_break_points(sema, token).remove(&file_id) } T![break] | T![loop] | T![while] | T![continue] if config.break_points => { - highlight_break_points(sema, token) + highlight_break_points(sema, token).remove(&file_id) } T![|] if config.closure_captures => highlight_closure_captures(sema, token, file_id), T![move] if config.closure_captures => highlight_closure_captures(sema, token, file_id), @@ -277,24 +277,35 @@ fn highlight_references( } } +// If `file_id` is None, pub(crate) fn highlight_exit_points( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, -) -> Option<Vec<HighlightedRange>> { +) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> { fn hl( sema: &Semantics<'_, RootDatabase>, - def_range: Option<TextRange>, + def_token: Option<SyntaxToken>, body: ast::Expr, - ) -> Option<Vec<HighlightedRange>> { - let mut highlights = Vec::new(); - if let Some(range) = def_range { - highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }); + ) -> Option<FxHashMap<EditionedFileId, Vec<HighlightedRange>>> { + let mut highlights: FxHashMap<EditionedFileId, Vec<_>> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().push(hrange); + } + }; + + if let Some(tok) = def_token { + let file_id = sema.hir_file_for(&tok.parent()?); + let range = Some(tok.text_range()); + push_to_highlights(file_id, range); } WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| { let file_id = sema.hir_file_for(expr.syntax()); - let text_range = match &expr { + let range = match &expr { ast::Expr::TryExpr(try_) => { try_.question_mark_token().map(|token| token.text_range()) } @@ -306,29 +317,24 @@ pub(crate) fn highlight_exit_points( _ => None, }; - if let Some(range) = original_range(sema.db, file_id, text_range) { - highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }) - } + push_to_highlights(file_id, range); }); - // We should handle `return` separately because when it is used in `try` block - // it will exit the outside function instead of the block it self. + // We should handle `return` separately, because when it is used in a `try` block, + // it will exit the outside function instead of the block itself. WalkExpandedExprCtx::new(sema) .with_check_ctx(&WalkExpandedExprCtx::is_async_const_block_or_closure) .walk(&body, &mut |_, expr| { let file_id = sema.hir_file_for(expr.syntax()); - let text_range = match &expr { + let range = match &expr { ast::Expr::ReturnExpr(expr) => { expr.return_token().map(|token| token.text_range()) } _ => None, }; - if let Some(range) = original_range(sema.db, file_id, text_range) { - highlights - .push(HighlightedRange { category: ReferenceCategory::empty(), range }) - } + push_to_highlights(file_id, range); }); let tail = match body { @@ -338,59 +344,74 @@ pub(crate) fn highlight_exit_points( if let Some(tail) = tail { for_each_tail_expr(&tail, &mut |tail| { + let file_id = sema.hir_file_for(tail.syntax()); let range = match tail { ast::Expr::BreakExpr(b) => b .break_token() .map_or_else(|| tail.syntax().text_range(), |tok| tok.text_range()), _ => tail.syntax().text_range(), }; - highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }) + push_to_highlights(file_id, Some(range)); }); } Some(highlights) } - for anc in token.parent_ancestors() { - return match_ast! { - match anc { - ast::Fn(fn_) => hl(sema, fn_.fn_token().map(|it| it.text_range()), ast::Expr::BlockExpr(fn_.body()?)), - ast::ClosureExpr(closure) => hl( - sema, - closure.param_list().and_then(|p| p.pipe_token()).map(|tok| tok.text_range()), - closure.body()? - ), + let mut res = FxHashMap::default(); + for def in goto_definition::find_fn_or_blocks(sema, &token) { + let new_map = match_ast! { + match def { + ast::Fn(fn_) => fn_.body().and_then(|body| hl(sema, fn_.fn_token(), body.into())), + ast::ClosureExpr(closure) => { + let pipe_tok = closure.param_list().and_then(|p| p.pipe_token()); + closure.body().and_then(|body| hl(sema, pipe_tok, body)) + }, ast::BlockExpr(blk) => match blk.modifier() { - Some(ast::BlockModifier::Async(t)) => hl(sema, Some(t.text_range()), blk.into()), + Some(ast::BlockModifier::Async(t)) => hl(sema, Some(t), blk.into()), Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => { - hl(sema, Some(t.text_range()), blk.into()) + hl(sema, Some(t), blk.into()) }, _ => continue, }, _ => continue, } }; + merge_map(&mut res, new_map); } - None + + res } pub(crate) fn highlight_break_points( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, -) -> Option<Vec<HighlightedRange>> { - fn hl( +) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> { + pub(crate) fn hl( sema: &Semantics<'_, RootDatabase>, cursor_token_kind: SyntaxKind, loop_token: Option<SyntaxToken>, label: Option<ast::Label>, expr: ast::Expr, - ) -> Option<Vec<HighlightedRange>> { - let mut highlights = Vec::new(); + ) -> Option<FxHashMap<EditionedFileId, Vec<HighlightedRange>>> { + let mut highlights: FxHashMap<EditionedFileId, Vec<_>> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().push(hrange); + } + }; - let (label_range, label_lt) = label - .map_or((None, None), |label| (Some(label.syntax().text_range()), label.lifetime())); + let label_lt = label.as_ref().and_then(|it| it.lifetime()); - if let Some(range) = cover_range(loop_token.map(|tok| tok.text_range()), label_range) { - highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }) + if let Some(range) = cover_range( + loop_token.as_ref().map(|tok| tok.text_range()), + label.as_ref().map(|it| it.syntax().text_range()), + ) { + let file_id = loop_token + .and_then(|tok| Some(sema.hir_file_for(&tok.parent()?))) + .unwrap_or_else(|| sema.hir_file_for(label.unwrap().syntax())); + push_to_highlights(file_id, Some(range)); } WalkExpandedExprCtx::new(sema) @@ -418,68 +439,53 @@ pub(crate) fn highlight_break_points( token_lt.map(|it| it.syntax().text_range()), ); - if let Some(range) = original_range(sema.db, file_id, text_range) { - highlights - .push(HighlightedRange { category: ReferenceCategory::empty(), range }) - } + push_to_highlights(file_id, text_range); }); Some(highlights) } - let parent = token.parent()?; - let lbl = match_ast! { - match parent { - ast::BreakExpr(b) => b.lifetime(), - ast::ContinueExpr(c) => c.lifetime(), - ast::LoopExpr(l) => l.label().and_then(|it| it.lifetime()), - ast::ForExpr(f) => f.label().and_then(|it| it.lifetime()), - ast::WhileExpr(w) => w.label().and_then(|it| it.lifetime()), - ast::BlockExpr(b) => Some(b.label().and_then(|it| it.lifetime())?), - _ => return None, - } + let mut res = FxHashMap::default(); + let token_kind = token.kind(); + let Some(loops) = goto_definition::find_loops(sema, &token) else { + return res; }; - - let label_matches = |def_lbl: Option<ast::Label>| match lbl.as_ref() { - Some(lbl) => { - Some(lbl.text()) == def_lbl.and_then(|it| it.lifetime()).as_ref().map(|it| it.text()) - } - None => true, - }; - - for anc in token.parent_ancestors().flat_map(ast::Expr::cast) { - return match &anc { - ast::Expr::LoopExpr(l) if label_matches(l.label()) => { - hl(sema, token.kind(), l.loop_token(), l.label(), anc) - } - ast::Expr::ForExpr(f) if label_matches(f.label()) => { - hl(sema, token.kind(), f.for_token(), f.label(), anc) - } - ast::Expr::WhileExpr(w) if label_matches(w.label()) => { - hl(sema, token.kind(), w.while_token(), w.label(), anc) - } - ast::Expr::BlockExpr(e) if e.label().is_some() && label_matches(e.label()) => { - hl(sema, token.kind(), None, e.label(), anc) - } + for expr in loops { + let new_map = match &expr { + ast::Expr::LoopExpr(l) => hl(sema, token_kind, l.loop_token(), l.label(), expr), + ast::Expr::ForExpr(f) => hl(sema, token_kind, f.for_token(), f.label(), expr), + ast::Expr::WhileExpr(w) => hl(sema, token_kind, w.while_token(), w.label(), expr), + ast::Expr::BlockExpr(e) => hl(sema, token_kind, None, e.label(), expr), _ => continue, }; + merge_map(&mut res, new_map); } - None + + res } pub(crate) fn highlight_yield_points( sema: &Semantics<'_, RootDatabase>, token: SyntaxToken, -) -> Option<Vec<HighlightedRange>> { +) -> FxHashMap<EditionedFileId, Vec<HighlightedRange>> { fn hl( sema: &Semantics<'_, RootDatabase>, async_token: Option<SyntaxToken>, body: Option<ast::Expr>, - ) -> Option<Vec<HighlightedRange>> { - let mut highlights = vec![HighlightedRange { - category: ReferenceCategory::empty(), - range: async_token?.text_range(), - }]; + ) -> Option<FxHashMap<EditionedFileId, Vec<HighlightedRange>>> { + let mut highlights: FxHashMap<EditionedFileId, Vec<_>> = FxHashMap::default(); + + let mut push_to_highlights = |file_id, range| { + if let Some(FileRange { file_id, range }) = original_frange(sema.db, file_id, range) { + let hrange = HighlightedRange { category: ReferenceCategory::empty(), range }; + highlights.entry(file_id).or_default().push(hrange); + } + }; + + let async_token = async_token?; + let async_tok_file_id = sema.hir_file_for(&async_token.parent()?); + push_to_highlights(async_tok_file_id, Some(async_token.text_range())); + let Some(body) = body else { return Some(highlights); }; @@ -487,22 +493,22 @@ pub(crate) fn highlight_yield_points( WalkExpandedExprCtx::new(sema).walk(&body, &mut |_, expr| { let file_id = sema.hir_file_for(expr.syntax()); - let token_range = match expr { + let text_range = match expr { ast::Expr::AwaitExpr(expr) => expr.await_token(), ast::Expr::ReturnExpr(expr) => expr.return_token(), _ => None, } .map(|it| it.text_range()); - if let Some(range) = original_range(sema.db, file_id, token_range) { - highlights.push(HighlightedRange { category: ReferenceCategory::empty(), range }); - } + push_to_highlights(file_id, text_range); }); Some(highlights) } - for anc in token.parent_ancestors() { - return match_ast! { + + let mut res = FxHashMap::default(); + for anc in goto_definition::find_fn_or_blocks(sema, &token) { + let new_map = match_ast! { match anc { ast::Fn(fn_) => hl(sema, fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)), ast::BlockExpr(block_expr) => { @@ -515,8 +521,10 @@ pub(crate) fn highlight_yield_points( _ => continue, } }; + merge_map(&mut res, new_map); } - None + + res } fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange> { @@ -536,14 +544,24 @@ fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSe .collect() } -fn original_range( +fn original_frange( db: &dyn db::ExpandDatabase, file_id: HirFileId, text_range: Option<TextRange>, -) -> Option<TextRange> { - InFile::new(file_id, text_range?) - .original_node_file_range_opt(db) - .map(|(frange, _)| frange.range) +) -> Option<FileRange> { + InFile::new(file_id, text_range?).original_node_file_range_opt(db).map(|(frange, _)| frange) +} + +fn merge_map( + res: &mut FxHashMap<EditionedFileId, Vec<HighlightedRange>>, + new: Option<FxHashMap<EditionedFileId, Vec<HighlightedRange>>>, +) { + let Some(new) = new else { + return; + }; + new.into_iter().for_each(|(file_id, ranges)| { + res.entry(file_id).or_default().extend(ranges); + }); } /// Preorder walk all the expression's child expressions. |