Skip to content

Commit 5aa60cf

Browse files
committed
fix: handle highlightings inside macro calls & only highlight kws in current file
1 parent c590cfb commit 5aa60cf

File tree

4 files changed

+278
-220
lines changed

4 files changed

+278
-220
lines changed

src/tools/rust-analyzer/crates/ide/src/goto_definition.rs

Lines changed: 138 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use std::{iter, mem::discriminant};
22

33
use crate::{
44
doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget,
5-
RangeInfo, TryToNav, UpmappingResult,
5+
RangeInfo, TryToNav,
66
};
77
use hir::{
8-
AsAssocItem, AssocItem, DescendPreference, InFile, MacroFileIdExt, ModuleDef, Semantics,
8+
AsAssocItem, AssocItem, DescendPreference, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics
99
};
1010
use ide_db::{
1111
base_db::{AnchoredPath, FileLoader},
@@ -14,11 +14,12 @@ use ide_db::{
1414
FileId, RootDatabase,
1515
};
1616
use itertools::Itertools;
17+
1718
use syntax::{
18-
ast::{self, HasLoopBody, Label},
19+
ast::{self, HasLoopBody},
1920
match_ast, AstNode, AstToken,
2021
SyntaxKind::*,
21-
SyntaxToken, TextRange, T,
22+
SyntaxNode, SyntaxToken, TextRange, T,
2223
};
2324

2425
// Feature: Go to Definition
@@ -208,136 +209,127 @@ fn handle_control_flow_keywords(
208209
match token.kind() {
209210
// For `fn` / `loop` / `while` / `for` / `async`, return the keyword it self,
210211
// so that VSCode will find the references when using `ctrl + click`
211-
T![fn] | T![async] | T![try] | T![return] => try_find_fn_or_closure(sema, token),
212-
T![loop] | T![while] | T![break] | T![continue] => try_find_loop(sema, token),
212+
T![fn] | T![async] | T![try] | T![return] => nav_for_exit_points(sema, token),
213+
T![loop] | T![while] | T![break] | T![continue] => nav_for_break_points(sema, token),
213214
T![for] if token.parent().and_then(ast::ForExpr::cast).is_some() => {
214-
try_find_loop(sema, token)
215+
nav_for_break_points(sema, token)
215216
}
216217
_ => None,
217218
}
218219
}
219220

220-
fn try_find_fn_or_closure(
221+
pub(crate) fn find_fn_or_blocks(
222+
sema: &Semantics<'_, RootDatabase>,
223+
token: &SyntaxToken,
224+
) -> Vec<SyntaxNode> {
225+
let find_ancestors = |token: SyntaxToken| {
226+
let token_kind = token.kind();
227+
228+
for anc in sema.token_ancestors_with_macros(token) {
229+
let node = match_ast! {
230+
match anc {
231+
ast::Fn(fn_) => fn_.syntax().clone(),
232+
ast::ClosureExpr(c) => c.syntax().clone(),
233+
ast::BlockExpr(blk) => {
234+
match blk.modifier() {
235+
Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),
236+
Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => blk.syntax().clone(),
237+
_ => continue,
238+
}
239+
},
240+
_ => continue,
241+
}
242+
};
243+
244+
return Some(node);
245+
}
246+
None
247+
};
248+
249+
sema.descend_into_macros(DescendPreference::None, token.clone())
250+
.into_iter()
251+
.filter_map(find_ancestors)
252+
.collect_vec()
253+
}
254+
255+
fn nav_for_exit_points(
221256
sema: &Semantics<'_, RootDatabase>,
222257
token: &SyntaxToken,
223258
) -> Option<Vec<NavigationTarget>> {
224-
fn find_exit_point(
225-
sema: &Semantics<'_, RootDatabase>,
226-
token: SyntaxToken,
227-
) -> Option<UpmappingResult<NavigationTarget>> {
228-
let db = sema.db;
229-
230-
for anc in sema.token_ancestors_with_macros(token.clone()) {
231-
let file_id = sema.hir_file_for(&anc);
259+
let db = sema.db;
260+
let token_kind = token.kind();
261+
262+
let navs = find_fn_or_blocks(sema, token)
263+
.into_iter()
264+
.filter_map(|node| {
265+
let file_id = sema.hir_file_for(&node);
266+
232267
match_ast! {
233-
match anc {
268+
match node {
234269
ast::Fn(fn_) => {
235-
let fn_: ast::Fn = fn_;
236-
let nav = sema.to_def(&fn_)?.try_to_nav(db)?;
270+
let mut nav = sema.to_def(&fn_)?.try_to_nav(db)?;
237271
// For async token, we navigate to itself, which triggers
238272
// VSCode to find the references
239-
let focus_token = if matches!(token.kind(), T![async]) {
273+
let focus_token = if matches!(token_kind, T![async]) {
240274
fn_.async_token()?
241275
} else {
242276
fn_.fn_token()?
243277
};
244278

245-
let focus_range = InFile::new(file_id, focus_token.text_range())
246-
.original_node_file_range_opt(db)
247-
.map(|(frange, _)| frange.range);
248-
return Some(nav.map(|it| {
249-
if focus_range.is_some_and(|range| it.full_range.contains_range(range)) {
250-
NavigationTarget { focus_range, ..it }
251-
} else {
252-
it
279+
let focus_frange = InFile::new(file_id, focus_token.text_range())
280+
.original_node_file_range_opt(db)
281+
.map(|(frange, _)| frange);
282+
283+
if let Some(FileRange { file_id, range }) = focus_frange {
284+
let contains_frange = |nav: &NavigationTarget| {
285+
nav.file_id == file_id && nav.full_range.contains_range(range)
286+
};
287+
288+
if let Some(def_site) = nav.def_site.as_mut() {
289+
if contains_frange(def_site) {
290+
def_site.focus_range = Some(range);
291+
}
292+
} else if contains_frange(&nav.call_site) {
293+
nav.call_site.focus_range = Some(range);
253294
}
254-
}));
295+
}
296+
297+
Some(nav)
255298
},
256299
ast::ClosureExpr(c) => {
257-
let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.into();
258-
let c_infile = InFile::new(file_id, c.into());
259-
let nav = NavigationTarget::from_expr(db, c_infile, pipe_tok);
260-
return Some(nav);
300+
let pipe_tok = c.param_list().and_then(|it| it.pipe_token())?.text_range();
301+
let closure_in_file = InFile::new(file_id, c.into());
302+
Some(NavigationTarget::from_expr(db, closure_in_file, Some(pipe_tok)))
261303
},
262304
ast::BlockExpr(blk) => {
263305
match blk.modifier() {
264306
Some(ast::BlockModifier::Async(_)) => {
265-
let async_tok = blk.async_token()?.into();
266-
let blk_infile = InFile::new(file_id, blk.into());
267-
let nav = NavigationTarget::from_expr(db, blk_infile, async_tok);
268-
return Some(nav);
307+
let async_tok = blk.async_token()?.text_range();
308+
let blk_in_file = InFile::new(file_id, blk.into());
309+
Some(NavigationTarget::from_expr(db, blk_in_file, Some(async_tok)))
269310
},
270-
Some(ast::BlockModifier::Try(_)) if token.kind() != T![return] => {
271-
let try_tok = blk.try_token()?.into();
272-
let blk_infile = InFile::new(file_id, blk.into());
273-
let nav = NavigationTarget::from_expr(db, blk_infile, try_tok);
274-
return Some(nav);
311+
Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => {
312+
let try_tok = blk.try_token()?.text_range();
313+
let blk_in_file = InFile::new(file_id, blk.into());
314+
Some(NavigationTarget::from_expr(db, blk_in_file, Some(try_tok)))
275315
},
276-
_ => {}
316+
_ => None,
277317
}
278318
},
279-
_ => {}
319+
_ => None,
280320
}
281321
}
282-
}
283-
None
284-
}
285-
286-
sema.descend_into_macros(DescendPreference::None, token.clone())
287-
.into_iter()
288-
.filter_map(|descended| find_exit_point(sema, descended))
322+
})
289323
.flatten()
290-
.collect_vec()
291-
.into()
324+
.collect_vec();
325+
326+
Some(navs)
292327
}
293328

294-
fn try_find_loop(
329+
pub(crate) fn find_loops(
295330
sema: &Semantics<'_, RootDatabase>,
296331
token: &SyntaxToken,
297-
) -> Option<Vec<NavigationTarget>> {
298-
fn find_break_point(
299-
sema: &Semantics<'_, RootDatabase>,
300-
token: SyntaxToken,
301-
label_matches: impl Fn(Option<Label>) -> bool,
302-
) -> Option<UpmappingResult<NavigationTarget>> {
303-
let db = sema.db;
304-
let file_id = sema.hir_file_for(&token.parent()?);
305-
306-
for anc in sema.token_ancestors_with_macros(token.clone()).filter_map(ast::Expr::cast) {
307-
match anc {
308-
ast::Expr::LoopExpr(loop_) if label_matches(loop_.label()) => {
309-
let expr = ast::Expr::LoopExpr(loop_.clone());
310-
let loop_tok = loop_.loop_token()?.into();
311-
let nav = NavigationTarget::from_expr(db, InFile::new(file_id, expr), loop_tok);
312-
return Some(nav);
313-
}
314-
ast::Expr::WhileExpr(while_) if label_matches(while_.label()) => {
315-
let expr = ast::Expr::WhileExpr(while_.clone());
316-
let while_tok = while_.while_token()?.into();
317-
let nav =
318-
NavigationTarget::from_expr(db, InFile::new(file_id, expr), while_tok);
319-
return Some(nav);
320-
}
321-
ast::Expr::ForExpr(for_) if label_matches(for_.label()) => {
322-
let expr = ast::Expr::ForExpr(for_.clone());
323-
let for_tok = for_.for_token()?.into();
324-
let nav = NavigationTarget::from_expr(db, InFile::new(file_id, expr), for_tok);
325-
return Some(nav);
326-
}
327-
ast::Expr::BlockExpr(blk)
328-
if blk.label().is_some() && label_matches(blk.label()) =>
329-
{
330-
let expr = ast::Expr::BlockExpr(blk.clone());
331-
let lbl = blk.label().unwrap().syntax().clone().into();
332-
let nav = NavigationTarget::from_expr(db, InFile::new(file_id, expr), lbl);
333-
return Some(nav);
334-
}
335-
_ => {}
336-
}
337-
}
338-
None
339-
}
340-
332+
) -> Option<Vec<ast::Expr>> {
341333
let parent = token.parent()?;
342334
let lbl = match_ast! {
343335
match parent {
@@ -353,14 +345,60 @@ fn try_find_loop(
353345
(Some(_), None) => false,
354346
};
355347

348+
let find_ancestors = |token: SyntaxToken| {
349+
for anc in sema.token_ancestors_with_macros(token).filter_map(ast::Expr::cast) {
350+
let node = match &anc {
351+
ast::Expr::LoopExpr(loop_) if label_matches(loop_.label()) => anc,
352+
ast::Expr::WhileExpr(while_) if label_matches(while_.label()) => anc,
353+
ast::Expr::ForExpr(for_) if label_matches(for_.label()) => anc,
354+
ast::Expr::BlockExpr(blk)
355+
if blk.label().is_some() && label_matches(blk.label()) =>
356+
{
357+
anc
358+
}
359+
_ => continue,
360+
};
361+
362+
return Some(node);
363+
}
364+
None
365+
};
366+
356367
sema.descend_into_macros(DescendPreference::None, token.clone())
357368
.into_iter()
358-
.filter_map(|descended| find_break_point(sema, descended, label_matches))
359-
.flatten()
369+
.filter_map(find_ancestors)
360370
.collect_vec()
361371
.into()
362372
}
363373

374+
fn nav_for_break_points(
375+
sema: &Semantics<'_, RootDatabase>,
376+
token: &SyntaxToken,
377+
) -> Option<Vec<NavigationTarget>> {
378+
let db = sema.db;
379+
380+
let navs = find_loops(sema, token)?
381+
.into_iter()
382+
.filter_map(|expr| {
383+
let file_id = sema.hir_file_for(expr.syntax());
384+
let expr_in_file = InFile::new(file_id, expr.clone());
385+
let focus_range = match expr {
386+
ast::Expr::LoopExpr(loop_) => loop_.loop_token()?.text_range(),
387+
ast::Expr::WhileExpr(while_) => while_.while_token()?.text_range(),
388+
ast::Expr::ForExpr(for_) => for_.for_token()?.text_range(),
389+
// We garentee that the label exists
390+
ast::Expr::BlockExpr(blk) => blk.label().unwrap().syntax().text_range(),
391+
_ => return None,
392+
};
393+
let nav = NavigationTarget::from_expr(db, expr_in_file, Some(focus_range));
394+
Some(nav)
395+
})
396+
.flatten()
397+
.collect_vec();
398+
399+
Some(navs)
400+
}
401+
364402
fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
365403
def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default()
366404
}

0 commit comments

Comments
 (0)