Skip to content

Commit d79999a

Browse files
committed
Thread file id through descension API for semantic highlighting
1 parent 4d61444 commit d79999a

File tree

3 files changed

+117
-75
lines changed

3 files changed

+117
-75
lines changed

src/tools/rust-analyzer/crates/hir-expand/src/files.rs

+9
Original file line numberDiff line numberDiff line change
@@ -461,3 +461,12 @@ impl<N: AstNode> InFile<N> {
461461
Some(InRealFile::new(file_id, value))
462462
}
463463
}
464+
465+
impl<T> InFile<T> {
466+
pub fn into_real_file(self) -> Result<InRealFile<T>, InFile<T>> {
467+
match self.file_id.repr() {
468+
HirFileIdRepr::FileId(file_id) => Ok(InRealFile { file_id, value: self.value }),
469+
HirFileIdRepr::MacroFile(_) => Err(self),
470+
}
471+
}
472+
}

src/tools/rust-analyzer/crates/hir/src/semantics.rs

+68-39
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,9 @@ impl<'db> SemanticsImpl<'db> {
550550
string: &ast::String,
551551
) -> Option<Vec<(TextRange, Option<PathResolution>)>> {
552552
let quote = string.open_quote_text_range()?;
553-
self.descend_into_macros_breakable(string.syntax().clone(), |token| {
553+
554+
let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
555+
self.descend_into_macros_breakable(token, |token| {
554556
(|| {
555557
let token = token.value;
556558
let string = ast::String::cast(token)?;
@@ -576,8 +578,9 @@ impl<'db> SemanticsImpl<'db> {
576578
offset: TextSize,
577579
) -> Option<(TextRange, Option<PathResolution>)> {
578580
let original_string = ast::String::cast(original_token.clone())?;
581+
let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
579582
let quote = original_string.open_quote_text_range()?;
580-
self.descend_into_macros_breakable(original_token.clone(), |token| {
583+
self.descend_into_macros_breakable(original_token, |token| {
581584
(|| {
582585
let token = token.value;
583586
self.resolve_offset_in_format_args(
@@ -617,30 +620,37 @@ impl<'db> SemanticsImpl<'db> {
617620
Some(it) => it,
618621
None => return res,
619622
};
623+
let file = self.find_file(node.syntax());
624+
let Some(file_id) = file.file_id.file_id() else {
625+
return res;
626+
};
620627

621628
if first == last {
622629
// node is just the token, so descend the token
623-
self.descend_into_macros_impl(first, &mut |InFile { value, .. }| {
624-
if let Some(node) = value
625-
.parent_ancestors()
626-
.take_while(|it| it.text_range() == value.text_range())
627-
.find_map(N::cast)
628-
{
629-
res.push(node)
630-
}
631-
CONTINUE_NO_BREAKS
632-
});
630+
self.descend_into_macros_impl(
631+
InRealFile::new(file_id, first),
632+
&mut |InFile { value, .. }| {
633+
if let Some(node) = value
634+
.parent_ancestors()
635+
.take_while(|it| it.text_range() == value.text_range())
636+
.find_map(N::cast)
637+
{
638+
res.push(node)
639+
}
640+
CONTINUE_NO_BREAKS
641+
},
642+
);
633643
} else {
634644
// Descend first and last token, then zip them to look for the node they belong to
635645
let mut scratch: SmallVec<[_; 1]> = smallvec![];
636-
self.descend_into_macros_impl(first, &mut |token| {
646+
self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token| {
637647
scratch.push(token);
638648
CONTINUE_NO_BREAKS
639649
});
640650

641651
let mut scratch = scratch.into_iter();
642652
self.descend_into_macros_impl(
643-
last,
653+
InRealFile::new(file_id, last),
644654
&mut |InFile { value: last, file_id: last_fid }| {
645655
if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
646656
if first_fid == last_fid {
@@ -669,18 +679,22 @@ impl<'db> SemanticsImpl<'db> {
669679
token: SyntaxToken,
670680
mut cb: impl FnMut(InFile<SyntaxToken>),
671681
) {
672-
self.descend_into_macros_impl(token.clone(), &mut |t| {
673-
cb(t);
674-
CONTINUE_NO_BREAKS
675-
});
682+
if let Ok(token) = self.wrap_token_infile(token).into_real_file() {
683+
self.descend_into_macros_impl(token, &mut |t| {
684+
cb(t);
685+
CONTINUE_NO_BREAKS
686+
});
687+
}
676688
}
677689

678690
pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
679691
let mut res = smallvec![];
680-
self.descend_into_macros_impl(token.clone(), &mut |t| {
681-
res.push(t.value);
682-
CONTINUE_NO_BREAKS
683-
});
692+
if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
693+
self.descend_into_macros_impl(token, &mut |t| {
694+
res.push(t.value);
695+
CONTINUE_NO_BREAKS
696+
});
697+
}
684698
if res.is_empty() {
685699
res.push(token);
686700
}
@@ -689,7 +703,7 @@ impl<'db> SemanticsImpl<'db> {
689703

690704
pub fn descend_into_macros_breakable<T>(
691705
&self,
692-
token: SyntaxToken,
706+
token: InRealFile<SyntaxToken>,
693707
mut cb: impl FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
694708
) -> Option<T> {
695709
self.descend_into_macros_impl(token.clone(), &mut cb)
@@ -721,28 +735,36 @@ impl<'db> SemanticsImpl<'db> {
721735
pub fn descend_into_macros_single_exact(&self, token: SyntaxToken) -> SyntaxToken {
722736
let text = token.text();
723737
let kind = token.kind();
724-
725-
self.descend_into_macros_breakable(token.clone(), |InFile { value, file_id: _ }| {
726-
let mapped_kind = value.kind();
727-
let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
728-
let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
729-
if matches {
730-
ControlFlow::Break(value)
731-
} else {
732-
ControlFlow::Continue(())
733-
}
734-
})
738+
if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
739+
self.descend_into_macros_breakable(token.clone(), |InFile { value, file_id: _ }| {
740+
let mapped_kind = value.kind();
741+
let any_ident_match =
742+
|| kind.is_any_identifier() && value.kind().is_any_identifier();
743+
let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
744+
if matches {
745+
ControlFlow::Break(value)
746+
} else {
747+
ControlFlow::Continue(())
748+
}
749+
})
750+
} else {
751+
None
752+
}
735753
.unwrap_or(token)
736754
}
737755

738756
fn descend_into_macros_impl<T>(
739757
&self,
740-
token: SyntaxToken,
758+
InRealFile { value: token, file_id }: InRealFile<SyntaxToken>,
741759
f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
742760
) -> Option<T> {
743761
let _p = tracing::info_span!("descend_into_macros_impl").entered();
744-
let (sa, span, file_id) =
745-
token.parent().and_then(|parent| self.analyze_no_infer(&parent)).and_then(|sa| {
762+
let (sa, span, file_id) = token
763+
.parent()
764+
.and_then(|parent| {
765+
self.analyze_impl(InRealFile::new(file_id, &parent).into(), None, false)
766+
})
767+
.and_then(|sa| {
746768
let file_id = sa.file_id.file_id()?;
747769
Some((
748770
sa,
@@ -1400,11 +1422,13 @@ impl<'db> SemanticsImpl<'db> {
14001422

14011423
/// Returns none if the file of the node is not part of a crate.
14021424
fn analyze(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
1425+
let node = self.find_file(node);
14031426
self.analyze_impl(node, None, true)
14041427
}
14051428

14061429
/// Returns none if the file of the node is not part of a crate.
14071430
fn analyze_no_infer(&self, node: &SyntaxNode) -> Option<SourceAnalyzer> {
1431+
let node = self.find_file(node);
14081432
self.analyze_impl(node, None, false)
14091433
}
14101434

@@ -1413,17 +1437,17 @@ impl<'db> SemanticsImpl<'db> {
14131437
node: &SyntaxNode,
14141438
offset: TextSize,
14151439
) -> Option<SourceAnalyzer> {
1440+
let node = self.find_file(node);
14161441
self.analyze_impl(node, Some(offset), false)
14171442
}
14181443

14191444
fn analyze_impl(
14201445
&self,
1421-
node: &SyntaxNode,
1446+
node: InFile<&SyntaxNode>,
14221447
offset: Option<TextSize>,
14231448
infer_body: bool,
14241449
) -> Option<SourceAnalyzer> {
14251450
let _p = tracing::info_span!("SemanticsImpl::analyze_impl").entered();
1426-
let node = self.find_file(node);
14271451

14281452
let container = self.with_ctx(|ctx| ctx.find_container(node))?;
14291453

@@ -1468,6 +1492,11 @@ impl<'db> SemanticsImpl<'db> {
14681492
InFile::new(file_id, node)
14691493
}
14701494

1495+
fn wrap_token_infile(&self, token: SyntaxToken) -> InFile<SyntaxToken> {
1496+
let InFile { file_id, .. } = self.find_file(&token.parent().unwrap());
1497+
InFile::new(file_id, token)
1498+
}
1499+
14711500
/// Wraps the node in a [`InFile`] with the file id it belongs to.
14721501
fn find_file<'node>(&self, node: &'node SyntaxNode) -> InFile<&'node SyntaxNode> {
14731502
let root_node = find_root(node);

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

+40-36
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ mod tests;
1515

1616
use std::ops::ControlFlow;
1717

18-
use hir::{Name, Semantics};
18+
use hir::{InRealFile, Name, Semantics};
1919
use ide_db::{FxHashMap, RootDatabase, SymbolKind};
2020
use span::EditionedFileId;
2121
use syntax::{
@@ -409,43 +409,47 @@ fn traverse(
409409
let mut r = 0;
410410
// FIXME: Add an extra API that takes the file id of this. That is a simple way
411411
// to prevent us constantly walking up the tree to fetch the file
412-
sema.descend_into_macros_breakable(token.clone(), |tok| {
413-
let tok = tok.value;
414-
let tok_kind = tok.kind();
415-
416-
let exact_same_kind = tok_kind == kind;
417-
let both_idents =
418-
exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
419-
let same_text = tok.text() == text;
420-
// anything that mapped into a token tree has likely no semantic information
421-
let no_tt_parent = tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
422-
let my_rank = (both_idents as usize)
423-
| ((exact_same_kind as usize) << 1)
424-
| ((same_text as usize) << 2)
425-
| ((no_tt_parent as usize) << 3);
426-
427-
if my_rank > 0b1110 {
428-
// a rank of 0b1110 means that we have found a maximally interesting
429-
// token so stop early.
430-
t = Some(tok);
431-
return ControlFlow::Break(());
432-
}
433-
434-
// r = r.max(my_rank);
435-
// t = Some(t.take_if(|_| r < my_rank).unwrap_or(tok));
436-
match &mut t {
437-
Some(prev) if r < my_rank => {
438-
*prev = tok;
439-
r = my_rank;
412+
sema.descend_into_macros_breakable(
413+
InRealFile::new(file_id, token.clone()),
414+
|tok| {
415+
let tok = tok.value;
416+
let tok_kind = tok.kind();
417+
418+
let exact_same_kind = tok_kind == kind;
419+
let both_idents =
420+
exact_same_kind || (tok_kind.is_any_identifier() && ident_kind);
421+
let same_text = tok.text() == text;
422+
// anything that mapped into a token tree has likely no semantic information
423+
let no_tt_parent =
424+
tok.parent().map_or(false, |it| it.kind() != TOKEN_TREE);
425+
let my_rank = (both_idents as usize)
426+
| ((exact_same_kind as usize) << 1)
427+
| ((same_text as usize) << 2)
428+
| ((no_tt_parent as usize) << 3);
429+
430+
if my_rank > 0b1110 {
431+
// a rank of 0b1110 means that we have found a maximally interesting
432+
// token so stop early.
433+
t = Some(tok);
434+
return ControlFlow::Break(());
440435
}
441-
Some(_) => (),
442-
None => {
443-
r = my_rank;
444-
t = Some(tok)
436+
437+
// r = r.max(my_rank);
438+
// t = Some(t.take_if(|_| r < my_rank).unwrap_or(tok));
439+
match &mut t {
440+
Some(prev) if r < my_rank => {
441+
*prev = tok;
442+
r = my_rank;
443+
}
444+
Some(_) => (),
445+
None => {
446+
r = my_rank;
447+
t = Some(tok)
448+
}
445449
}
446-
}
447-
ControlFlow::Continue(())
448-
});
450+
ControlFlow::Continue(())
451+
},
452+
);
449453

450454
let token = t.unwrap_or(token);
451455
match token.parent().and_then(ast::NameLike::cast) {

0 commit comments

Comments
 (0)