Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/syntax_highlighting.rs')
| -rw-r--r-- | crates/ide/src/syntax_highlighting.rs | 125 |
1 files changed, 72 insertions, 53 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index 968aa33116..0119ba5cb7 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -241,19 +241,26 @@ fn traverse( current_macro = Some(mac.into()); continue; } - Some(item) if sema.is_attr_macro_call(&item) => current_attr_call = Some(item), - Some(item) if current_attr_call.is_none() => { - let adt = match item { - ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), - ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), - ast::Item::Union(it) => Some(ast::Adt::Union(it)), - _ => None, - }; - match adt { - Some(adt) if sema.is_derive_annotated(&adt) => { - current_derive_call = Some(ast::Item::from(adt)); + Some(item) => { + if matches!(node.kind(), FN | CONST | STATIC) { + bindings_shadow_count.clear(); + } + + if sema.is_attr_macro_call(&item) { + current_attr_call = Some(item); + } else if current_attr_call.is_none() { + let adt = match item { + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + }; + match adt { + Some(adt) if sema.is_derive_annotated(&adt) => { + current_derive_call = Some(ast::Item::from(adt)); + } + _ => (), } - _ => (), } } None if ast::Attr::can_cast(node.kind()) => inside_attribute = true, @@ -291,6 +298,8 @@ fn traverse( WalkEvent::Enter(it) => it, WalkEvent::Leave(NodeOrToken::Token(_)) => continue, WalkEvent::Leave(NodeOrToken::Node(node)) => { + // Doc comment highlighting injection, we do this when leaving the node + // so that we overwrite the highlighting of the doc comment itself. inject::doc_comment(hl, sema, InFile::new(file_id.into(), &node)); continue; } @@ -302,57 +311,68 @@ fn traverse( } } - // only attempt to descend if we are inside a macro call or attribute - // as calling `descend_into_macros_single` gets rather expensive if done for every single token - // additionally, do not descend into comments, descending maps down to doc attributes which get - // tagged as string literals. - let descend_token = (current_macro_call.is_some() - || current_attr_call.is_some() - || current_derive_call.is_some()) - && element.kind() != COMMENT; - let element_to_highlight = if descend_token { - let token = match &element { - NodeOrToken::Node(_) => continue, - NodeOrToken::Token(tok) => tok.clone(), - }; - let in_mcall_outside_tt = current_attr_call.is_none() - && token.parent().as_ref().map(SyntaxNode::kind) != Some(TOKEN_TREE); - let token = match in_mcall_outside_tt { - // not in the macros/derives token tree, don't attempt to descend - true => token, - false => sema.descend_into_macros_single(token), - }; - match token.parent() { - Some(parent) => { - // Names and NameRefs have special semantics, use them instead of the tokens - // as otherwise we won't ever visit them - match (token.kind(), parent.kind()) { - (T![ident], NAME | NAME_REF) => parent.into(), - (T![self] | T![super] | T![crate] | T![Self], NAME_REF) => parent.into(), - (INT_NUMBER, NAME_REF) => parent.into(), - (LIFETIME_IDENT, LIFETIME) => parent.into(), - _ => token.into(), + let element = match element.clone() { + NodeOrToken::Node(n) => match ast::NameLike::cast(n) { + Some(n) => NodeOrToken::Node(n), + None => continue, + }, + NodeOrToken::Token(t) => NodeOrToken::Token(t), + }; + let token = element.as_token().cloned(); + + // Descending tokens into macros is expensive even if no descending occurs, so make sure + // that we actually are in a position where descending is possible. + let in_macro = current_macro_call.is_some() + || current_derive_call.is_some() + || current_attr_call.is_some(); + let descended_element = if in_macro { + // Attempt to descend tokens into macro-calls. + match element { + NodeOrToken::Token(token) if token.kind() != COMMENT => { + // For function-like macro calls and derive attributes, only attempt to descend if + // we are inside their token-trees. + let in_tt = current_attr_call.is_some() + || token.parent().as_ref().map(SyntaxNode::kind) == Some(TOKEN_TREE); + + if in_tt { + let token = sema.descend_into_macros_single(token); + match token.parent().and_then(ast::NameLike::cast) { + // Remap the token into the wrapping single token nodes + // FIXME: if the node doesn't resolve, we also won't do token based highlighting! + Some(parent) => match (token.kind(), parent.syntax().kind()) { + (T![self] | T![ident], NAME | NAME_REF) => { + NodeOrToken::Node(parent) + } + (T![self] | T![super] | T![crate] | T![Self], NAME_REF) => { + NodeOrToken::Node(parent) + } + (INT_NUMBER, NAME_REF) => NodeOrToken::Node(parent), + (LIFETIME_IDENT, LIFETIME) => NodeOrToken::Node(parent), + _ => NodeOrToken::Token(token), + }, + None => NodeOrToken::Token(token), + } + } else { + NodeOrToken::Token(token) } } - None => token.into(), + e => e, } } else { - element.clone() + element }; // FIXME: do proper macro def highlighting https://github.com/rust-analyzer/rust-analyzer/issues/6232 // Skip metavariables from being highlighted to prevent keyword highlighting in them - if macro_highlighter.highlight(&element_to_highlight).is_some() { + if descended_element.as_token().and_then(|t| macro_highlighter.highlight(t)).is_some() { continue; } // string highlight injections, note this does not use the descended element as proc-macros // can rewrite string literals which invalidates our indices - if let (Some(token), Some(token_to_highlight)) = - (element.into_token(), element_to_highlight.as_token()) - { + if let (Some(token), Some(descended_token)) = (token, descended_element.as_token()) { let string = ast::String::cast(token); - let string_to_highlight = ast::String::cast(token_to_highlight.clone()); + let string_to_highlight = ast::String::cast(descended_token.clone()); if let Some((string, expanded_string)) = string.zip(string_to_highlight) { if string.is_raw() { if inject::ra_fixture(hl, sema, &string, &expanded_string).is_some() { @@ -377,14 +397,13 @@ fn traverse( } } - // do the normal highlighting - let element = match element_to_highlight { - NodeOrToken::Node(node) => highlight::node( + let element = match descended_element { + NodeOrToken::Node(name_like) => highlight::name_like( sema, krate, &mut bindings_shadow_count, syntactic_name_ref_highlighting, - node, + name_like, ), NodeOrToken::Token(token) => highlight::token(sema, token).zip(Some(None)), }; |