use token_ancestors_with_macros to simplify goto-def on kw

This commit is contained in:
roife 2024-07-08 04:18:32 +08:00
parent 22c5924080
commit ae6e8d56d4
2 changed files with 45 additions and 83 deletions

View file

@ -5,8 +5,7 @@ use crate::{
RangeInfo, TryToNav, UpmappingResult, RangeInfo, TryToNav, UpmappingResult,
}; };
use hir::{ use hir::{
AsAssocItem, AssocItem, DescendPreference, HirFileId, InFile, MacroFileIdExt, ModuleDef, AsAssocItem, AssocItem, DescendPreference, InFile, MacroFileIdExt, ModuleDef, Semantics,
Semantics,
}; };
use ide_db::{ use ide_db::{
base_db::{AnchoredPath, FileLoader}, base_db::{AnchoredPath, FileLoader},
@ -15,11 +14,12 @@ use ide_db::{
FileId, RootDatabase, FileId, RootDatabase,
}; };
use itertools::Itertools; use itertools::Itertools;
use span::FileRange;
use syntax::{ use syntax::{
ast::{self, HasLoopBody}, ast::{self, HasLoopBody, Label},
match_ast, AstNode, AstToken, match_ast, AstNode, AstToken,
SyntaxKind::{self, *}, SyntaxKind::*,
SyntaxNode, SyntaxToken, TextRange, T, SyntaxToken, TextRange, T,
}; };
// Feature: Go to Definition // Feature: Go to Definition
@ -224,30 +224,28 @@ fn try_find_fn_or_closure(
) -> Option<Vec<NavigationTarget>> { ) -> Option<Vec<NavigationTarget>> {
fn find_exit_point( fn find_exit_point(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
file_id: HirFileId, token: SyntaxToken,
ancestors: impl Iterator<Item = SyntaxNode>,
cursor_token_kind: SyntaxKind,
) -> Option<UpmappingResult<NavigationTarget>> { ) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db; let db = sema.db;
for anc in ancestors { for anc in sema.token_ancestors_with_macros(token.clone()) {
let file_id = sema.hir_file_for(&anc);
match_ast! { match_ast! {
match anc { match anc {
ast::Fn(fn_) => { ast::Fn(fn_) => {
let hir_fn: hir::Function = sema.to_def(&fn_)?; let fn_: ast::Fn = fn_;
let nav = hir_fn.try_to_nav(db)?; let nav = sema.to_def(&fn_)?.try_to_nav(db)?;
// For async token, we navigate to itself, which triggers // For async token, we navigate to itself, which triggers
// VSCode to find the references // VSCode to find the references
let focus_token = if matches!(cursor_token_kind, T![async]) { let focus_token = if matches!(token.kind(), T![async]) {
fn_.async_token()? fn_.async_token()?
} else { } else {
fn_.fn_token()? fn_.fn_token()?
}; };
let focus_range = InFile::new(file_id, focus_token.text_range()) let focus_range = InFile::new(file_id, focus_token.text_range())
.original_node_file_range_opt(db) .original_node_file_range_opt(db)
.map(|(frange, _)| frange.range); .map(|(frange, _)| frange.range);
return Some(nav.map(|it| { return Some(nav.map(|it| {
if focus_range.is_some_and(|range| it.full_range.contains_range(range)) { if focus_range.is_some_and(|range| it.full_range.contains_range(range)) {
NavigationTarget { focus_range, ..it } NavigationTarget { focus_range, ..it }
@ -258,21 +256,26 @@ fn try_find_fn_or_closure(
}, },
ast::ClosureExpr(c) => { ast::ClosureExpr(c) => {
let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.into(); let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.into();
let nav = NavigationTarget::from_expr(db, InFile::new(file_id, c.into()), pipe_tok); let c_infile = InFile::new(file_id, c.into());
let nav = NavigationTarget::from_expr(db, c_infile, pipe_tok);
return Some(nav); return Some(nav);
}, },
ast::BlockExpr(blk) => match blk.modifier() { ast::BlockExpr(blk) => {
match blk.modifier() {
Some(ast::BlockModifier::Async(_)) => { Some(ast::BlockModifier::Async(_)) => {
let async_tok = blk.async_token()?.into(); let async_tok = blk.async_token()?.into();
let nav = NavigationTarget::from_expr(db, InFile::new(file_id, blk.into()), async_tok); let blk_infile = InFile::new(file_id, blk.into());
let nav = NavigationTarget::from_expr(db, blk_infile, async_tok);
return Some(nav); return Some(nav);
}, },
Some(ast::BlockModifier::Try(_)) if cursor_token_kind != T![return] => { Some(ast::BlockModifier::Try(_)) if token.kind() != T![return] => {
let try_tok = blk.try_token()?.into(); let try_tok = blk.try_token()?.into();
let nav = NavigationTarget::from_expr(db, InFile::new(file_id, blk.into()), try_tok); let blk_infile = InFile::new(file_id, blk.into());
let nav = NavigationTarget::from_expr(db, blk_infile, try_tok);
return Some(nav); return Some(nav);
}, },
_ => {} _ => {}
}
}, },
_ => {} _ => {}
} }
@ -281,28 +284,9 @@ fn try_find_fn_or_closure(
None None
} }
let token_kind = token.kind();
sema.descend_into_macros(DescendPreference::None, token.clone()) sema.descend_into_macros(DescendPreference::None, token.clone())
.into_iter() .into_iter()
.filter_map(|descended| { .filter_map(|descended| find_exit_point(sema, descended))
let file_id = sema.hir_file_for(&descended.parent()?);
// Try to find the function in the macro file
find_exit_point(sema, file_id, descended.parent_ancestors(), token_kind).or_else(|| {
// If not found, try to find it in the root file
if file_id.is_macro() {
token
.parent_ancestors()
.find(|it| ast::TokenTree::can_cast(it.kind()))
.and_then(|parent| {
let file_id = sema.hir_file_for(&parent);
find_exit_point(sema, file_id, parent.ancestors(), token_kind)
})
} else {
None
}
})
})
.flatten() .flatten()
.collect_vec() .collect_vec()
.into() .into()
@ -314,19 +298,13 @@ fn try_find_loop(
) -> Option<Vec<NavigationTarget>> { ) -> Option<Vec<NavigationTarget>> {
fn find_break_point( fn find_break_point(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
file_id: HirFileId, token: SyntaxToken,
ancestors: impl Iterator<Item = SyntaxNode>, label_matches: impl Fn(Option<Label>) -> bool,
lbl: &Option<ast::Lifetime>,
) -> Option<UpmappingResult<NavigationTarget>> { ) -> Option<UpmappingResult<NavigationTarget>> {
let db = sema.db; let db = sema.db;
let label_matches = |it: Option<ast::Label>| match lbl { let file_id = sema.hir_file_for(&token.parent()?);
Some(lbl) => {
Some(lbl.text()) == it.and_then(|it| it.lifetime()).as_ref().map(|it| it.text())
}
None => true,
};
for anc in ancestors.filter_map(ast::Expr::cast) { for anc in sema.token_ancestors_with_macros(token.clone()).filter_map(ast::Expr::cast) {
match anc { match anc {
ast::Expr::LoopExpr(loop_) if label_matches(loop_.label()) => { ast::Expr::LoopExpr(loop_) if label_matches(loop_.label()) => {
let expr = ast::Expr::LoopExpr(loop_.clone()); let expr = ast::Expr::LoopExpr(loop_.clone());
@ -369,28 +347,16 @@ fn try_find_loop(
_ => None, _ => None,
} }
}; };
let label_matches =
|it: Option<ast::Label>| match (lbl.as_ref(), it.and_then(|it| it.lifetime())) {
(Some(lbl), Some(it)) => lbl.text() == it.text(),
(None, _) => true,
(Some(_), None) => false,
};
sema.descend_into_macros(DescendPreference::None, token.clone()) sema.descend_into_macros(DescendPreference::None, token.clone())
.into_iter() .into_iter()
.filter_map(|descended| { .filter_map(|descended| find_break_point(sema, descended, label_matches))
let file_id = sema.hir_file_for(&descended.parent()?);
// Try to find the function in the macro file
find_break_point(sema, file_id, descended.parent_ancestors(), &lbl).or_else(|| {
// If not found, try to find it in the root file
if file_id.is_macro() {
token
.parent_ancestors()
.find(|it| ast::TokenTree::can_cast(it.kind()))
.and_then(|parent| {
let file_id = sema.hir_file_for(&parent);
find_break_point(sema, file_id, parent.ancestors(), &lbl)
})
} else {
None
}
})
})
.flatten() .flatten()
.collect_vec() .collect_vec()
.into() .into()

View file

@ -541,11 +541,7 @@ fn original_range(
file_id: HirFileId, file_id: HirFileId,
text_range: Option<TextRange>, text_range: Option<TextRange>,
) -> Option<TextRange> { ) -> Option<TextRange> {
if text_range.is_none() || !file_id.is_macro() { InFile::new(file_id, text_range?)
return text_range;
}
InFile::new(file_id, text_range.unwrap())
.original_node_file_range_opt(db) .original_node_file_range_opt(db)
.map(|(frange, _)| frange.range) .map(|(frange, _)| frange.range)
} }