diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index b4331fc09f4..cb23fec3c7c 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -309,6 +309,7 @@ fn traverse( match (token.kind(), parent.kind()) { (T![ident], NAME | NAME_REF) => parent.into(), (T![self] | T![super] | T![crate], NAME_REF) => parent.into(), + (INT_NUMBER, NAME_REF) => parent.into(), _ => token.into(), } } diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 5113ab84d53..65705d303c6 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -8,7 +8,7 @@ use ide_db::{ }; use rustc_hash::FxHashMap; use syntax::{ - ast, AstNode, AstToken, NodeOrToken, SyntaxElement, + ast, match_ast, AstNode, AstToken, NodeOrToken, SyntaxElement, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, T, }; @@ -25,202 +25,220 @@ pub(super) fn element( syntactic_name_ref_highlighting: bool, element: SyntaxElement, ) -> Option<(Highlight, Option)> { - let mut binding_hash = None; - let highlight: Highlight = match element.kind() { - FN => { - bindings_shadow_count.clear(); - return None; + match element { + NodeOrToken::Node(it) => { + node(sema, krate, bindings_shadow_count, syntactic_name_ref_highlighting, it) } - // Highlight definitions depending on the "type" of the definition. - NAME => { - let name = element.into_node().and_then(ast::Name::cast).unwrap(); - highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name) - } - // Highlight references like the definitions they resolve to - NAME_REF if element.ancestors().any(|it| it.kind() == ATTR) => { - // FIXME: We highlight paths in attributes slightly differently to work around this module - // currently not knowing about tool attributes and rustc builtin attributes as - // we do not want to resolve those to functions that may be defined in scope. - let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); - highlight_name_ref_in_attr(sema, name_ref) - } - NAME_REF => { - let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); - highlight_name_ref( - sema, - krate, - bindings_shadow_count, - &mut binding_hash, - syntactic_name_ref_highlighting, - name_ref, - ) - } - // Simple token-based highlighting - COMMENT => { - let comment = element.into_token().and_then(ast::Comment::cast)?; - let h = HlTag::Comment; - match comment.kind().doc { - Some(_) => h | HlMod::Documentation, - None => h.into(), - } - } - STRING | BYTE_STRING => HlTag::StringLiteral.into(), - ATTR => HlTag::Attribute.into(), - INT_NUMBER if element.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => { - SymbolKind::Field.into() - } - INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), - BYTE => HlTag::ByteLiteral.into(), - CHAR => HlTag::CharLiteral.into(), - QUESTION => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow, - LIFETIME => { - let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap(); + NodeOrToken::Token(it) => Some((token(sema, krate, it)?, None)), + } +} - match NameClass::classify_lifetime(sema, &lifetime) { - Some(NameClass::Definition(def)) => { - highlight_def(sema, krate, def) | HlMod::Definition - } - None => match NameRefClass::classify_lifetime(sema, &lifetime) { - Some(NameRefClass::Definition(def)) => highlight_def(sema, krate, def), - _ => SymbolKind::LifetimeParam.into(), - }, - _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition, - } +fn token( + sema: &Semantics, + krate: Option, + token: SyntaxToken, +) -> Option { + let highlight: Highlight = if let Some(comment) = ast::Comment::cast(token.clone()) { + let h = HlTag::Comment; + match comment.kind().doc { + Some(_) => h | HlMod::Documentation, + None => h.into(), } - IDENT if parent_matches::(&element) => { - if let Some((attr, token)) = - element.ancestors().nth(2).and_then(ast::Attr::cast).zip(element.as_token()) - { - match try_resolve_derive_input_at(sema, &attr, token) { - Some(makro) => highlight_def(sema, krate, Definition::Macro(makro)), - None => HlTag::None.into(), - } - } else { - HlTag::None.into() + } else { + match token.kind() { + STRING | BYTE_STRING => HlTag::StringLiteral.into(), + INT_NUMBER if token.ancestors().nth(1).map_or(false, |it| it.kind() == FIELD_EXPR) => { + SymbolKind::Field.into() } - } - p if p.is_punct() => match p { - T![&] if parent_matches::(&element) => HlOperator::Bitwise.into(), - T![&] => { - let h = HlTag::Operator(HlOperator::Other).into(); - let is_unsafe = element - .parent() - .and_then(ast::RefExpr::cast) - .map_or(false, |ref_expr| sema.is_unsafe_ref_expr(&ref_expr)); - if is_unsafe { - h | HlMod::Unsafe + INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), + BYTE => HlTag::ByteLiteral.into(), + CHAR => HlTag::CharLiteral.into(), + T![?] => HlTag::Operator(HlOperator::Other) | HlMod::ControlFlow, + IDENT if parent_matches::(&token) => { + if let Some(attr) = token.ancestors().nth(2).and_then(ast::Attr::cast) { + match try_resolve_derive_input_at(sema, &attr, &token) { + Some(makro) => highlight_def(sema, krate, Definition::Macro(makro)), + None => HlTag::None.into(), + } } else { - h + HlTag::None.into() } } - T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlOperator::Other.into(), - T![!] if parent_matches::(&element) => SymbolKind::Macro.into(), - T![!] if parent_matches::(&element) => HlTag::BuiltinType.into(), - T![!] if parent_matches::(&element) => HlOperator::Logical.into(), - T![*] if parent_matches::(&element) => HlTag::Keyword.into(), - T![*] if parent_matches::(&element) => { - let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; - - let expr = prefix_expr.expr()?; - let ty = sema.type_of_expr(&expr)?.original; - if ty.is_raw_ptr() { - HlTag::Operator(HlOperator::Other) | HlMod::Unsafe - } else if let Some(ast::UnaryOp::Deref) = prefix_expr.op_kind() { + p if p.is_punct() => match p { + T![&] if parent_matches::(&token) => HlOperator::Bitwise.into(), + T![&] => { + let h = HlTag::Operator(HlOperator::Other).into(); + let is_unsafe = token + .parent() + .and_then(ast::RefExpr::cast) + .map_or(false, |ref_expr| sema.is_unsafe_ref_expr(&ref_expr)); + if is_unsafe { + h | HlMod::Unsafe + } else { + h + } + } + T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => { HlOperator::Other.into() - } else { - HlPunct::Other.into() + } + T![!] if parent_matches::(&token) => SymbolKind::Macro.into(), + T![!] if parent_matches::(&token) => HlTag::BuiltinType.into(), + T![!] if parent_matches::(&token) => HlOperator::Logical.into(), + T![*] if parent_matches::(&token) => HlTag::Keyword.into(), + T![*] if parent_matches::(&token) => { + let prefix_expr = token.parent().and_then(ast::PrefixExpr::cast)?; + + let expr = prefix_expr.expr()?; + let ty = sema.type_of_expr(&expr)?.original; + if ty.is_raw_ptr() { + HlTag::Operator(HlOperator::Other) | HlMod::Unsafe + } else if let Some(ast::UnaryOp::Deref) = prefix_expr.op_kind() { + HlOperator::Other.into() + } else { + HlPunct::Other.into() + } + } + T![-] if parent_matches::(&token) => { + let prefix_expr = token.parent().and_then(ast::PrefixExpr::cast)?; + + let expr = prefix_expr.expr()?; + match expr { + ast::Expr::Literal(_) => HlTag::NumericLiteral, + _ => HlTag::Operator(HlOperator::Other), + } + .into() + } + _ if parent_matches::(&token) => HlOperator::Other.into(), + T![+] | T![-] | T![*] | T![/] if parent_matches::(&token) => { + HlOperator::Arithmetic.into() + } + T![+=] | T![-=] | T![*=] | T![/=] if parent_matches::(&token) => { + Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable + } + T![|] | T![&] | T![!] | T![^] if parent_matches::(&token) => { + HlOperator::Bitwise.into() + } + T![|=] | T![&=] | T![^=] if parent_matches::(&token) => { + Highlight::from(HlOperator::Bitwise) | HlMod::Mutable + } + T![&&] | T![||] if parent_matches::(&token) => { + HlOperator::Logical.into() + } + T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=] + if parent_matches::(&token) => + { + HlOperator::Comparison.into() + } + _ if parent_matches::(&token) => HlOperator::Other.into(), + _ if parent_matches::(&token) => HlOperator::Other.into(), + _ if parent_matches::(&token) => HlOperator::Other.into(), + _ if parent_matches::(&token) => HlOperator::Other.into(), + _ if parent_matches::(&token) => HlTag::Attribute.into(), + kind => match kind { + T!['['] | T![']'] => HlPunct::Bracket, + T!['{'] | T!['}'] => HlPunct::Brace, + T!['('] | T![')'] => HlPunct::Parenthesis, + T![<] | T![>] => HlPunct::Angle, + T![,] => HlPunct::Comma, + T![:] => HlPunct::Colon, + T![;] => HlPunct::Semi, + T![.] => HlPunct::Dot, + _ => HlPunct::Other, + } + .into(), + }, + k if k.is_keyword() => { + let h = Highlight::new(HlTag::Keyword); + match k { + T![await] => h | HlMod::Async | HlMod::ControlFlow, + T![break] + | T![continue] + | T![else] + | T![if] + | T![in] + | T![loop] + | T![match] + | T![return] + | T![while] + | T![yield] => h | HlMod::ControlFlow, + T![for] if !is_child_of_impl(&token) => h | HlMod::ControlFlow, + T![unsafe] => h | HlMod::Unsafe, + T![true] | T![false] => HlTag::BoolLiteral.into(), + // self is handled as either a Name or NameRef already + T![self] => return None, + T![ref] => token + .parent() + .and_then(ast::IdentPat::cast) + .and_then(|ident_pat| { + (sema.is_unsafe_ident_pat(&ident_pat)).then(|| HlMod::Unsafe) + }) + .map_or(h, |modifier| h | modifier), + T![async] => h | HlMod::Async, + _ => h, } } - T![-] if parent_matches::(&element) => { - let prefix_expr = element.parent().and_then(ast::PrefixExpr::cast)?; - - let expr = prefix_expr.expr()?; - match expr { - ast::Expr::Literal(_) => HlTag::NumericLiteral, - _ => HlTag::Operator(HlOperator::Other), - } - .into() - } - _ if parent_matches::(&element) => HlOperator::Other.into(), - T![+] | T![-] | T![*] | T![/] if parent_matches::(&element) => { - HlOperator::Arithmetic.into() - } - T![+=] | T![-=] | T![*=] | T![/=] if parent_matches::(&element) => { - Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable - } - T![|] | T![&] | T![!] | T![^] if parent_matches::(&element) => { - HlOperator::Bitwise.into() - } - T![|=] | T![&=] | T![^=] if parent_matches::(&element) => { - Highlight::from(HlOperator::Bitwise) | HlMod::Mutable - } - T![&&] | T![||] if parent_matches::(&element) => { - HlOperator::Logical.into() - } - T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=] - if parent_matches::(&element) => - { - HlOperator::Comparison.into() - } - _ if parent_matches::(&element) => HlOperator::Other.into(), - _ if parent_matches::(&element) => HlOperator::Other.into(), - _ if parent_matches::(&element) => HlOperator::Other.into(), - _ if parent_matches::(&element) => HlOperator::Other.into(), - _ if parent_matches::(&element) => HlTag::Attribute.into(), - kind => match kind { - T!['['] | T![']'] => HlPunct::Bracket, - T!['{'] | T!['}'] => HlPunct::Brace, - T!['('] | T![')'] => HlPunct::Parenthesis, - T![<] | T![>] => HlPunct::Angle, - T![,] => HlPunct::Comma, - T![:] => HlPunct::Colon, - T![;] => HlPunct::Semi, - T![.] => HlPunct::Dot, - _ => HlPunct::Other, - } - .into(), - }, - - k if k.is_keyword() => { - let h = Highlight::new(HlTag::Keyword); - match k { - T![await] => h | HlMod::Async | HlMod::ControlFlow, - T![break] - | T![continue] - | T![else] - | T![if] - | T![in] - | T![loop] - | T![match] - | T![return] - | T![while] - | T![yield] => h | HlMod::ControlFlow, - T![for] if !is_child_of_impl(&element) => h | HlMod::ControlFlow, - T![unsafe] => h | HlMod::Unsafe, - T![true] | T![false] => HlTag::BoolLiteral.into(), - // self is handled as either a Name or NameRef already - T![self] => return None, - T![ref] => element - .parent() - .and_then(ast::IdentPat::cast) - .and_then(|ident_pat| { - if sema.is_unsafe_ident_pat(&ident_pat) { - Some(HlMod::Unsafe) - } else { - None - } - }) - .map(|modifier| h | modifier) - .unwrap_or(h), - T![async] => h | HlMod::Async, - _ => h, - } + _ => return None, } - - _ => return None, }; + Some(highlight) +} - return Some((highlight, binding_hash)); +fn node( + sema: &Semantics, + krate: Option, + bindings_shadow_count: &mut FxHashMap, + syntactic_name_ref_highlighting: bool, + node: SyntaxNode, +) -> Option<(Highlight, Option)> { + let mut binding_hash = None; + let highlight = match_ast! { + match node { + ast::Fn(__) => { + bindings_shadow_count.clear(); + return None; + }, + ast::Attr(__) => { + HlTag::Attribute.into() + }, + // Highlight definitions depending on the "type" of the definition. + ast::Name(name) => { + highlight_name(sema, bindings_shadow_count, &mut binding_hash, krate, name) + }, + // Highlight references like the definitions they resolve to + ast::NameRef(name_ref) => { + if node.ancestors().any(|it| it.kind() == ATTR) { + + // FIXME: We highlight paths in attributes slightly differently to work around this module + // currently not knowing about tool attributes and rustc builtin attributes as + // we do not want to resolve those to functions that may be defined in scope. + highlight_name_ref_in_attr(sema, name_ref) + } else { + highlight_name_ref( + sema, + krate, + bindings_shadow_count, + &mut binding_hash, + syntactic_name_ref_highlighting, + name_ref, + ) + } + }, + ast::Lifetime(lifetime) => { + match NameClass::classify_lifetime(sema, &lifetime) { + Some(NameClass::Definition(def)) => { + highlight_def(sema, krate, def) | HlMod::Definition + } + None => match NameRefClass::classify_lifetime(sema, &lifetime) { + Some(NameRefClass::Definition(def)) => highlight_def(sema, krate, def), + _ => SymbolKind::LifetimeParam.into(), + }, + _ => Highlight::from(SymbolKind::LifetimeParam) | HlMod::Definition, + } + }, + _ => return None, + } + }; + Some((highlight, binding_hash)) } fn highlight_name_ref_in_attr(sema: &Semantics, name_ref: ast::NameRef) -> Highlight { @@ -715,12 +733,12 @@ fn parents_match(mut node: NodeOrToken, mut kinds: &[Sy } #[inline] -fn parent_matches(element: &SyntaxElement) -> bool { - element.parent().map_or(false, |it| N::can_cast(it.kind())) +fn parent_matches(token: &SyntaxToken) -> bool { + token.parent().map_or(false, |it| N::can_cast(it.kind())) } -fn is_child_of_impl(element: &SyntaxElement) -> bool { - match element.parent() { +fn is_child_of_impl(token: &SyntaxToken) -> bool { + match token.parent() { Some(e) => e.kind() == IMPL, _ => false, } diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 7553852659b..91587b11f53 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -12,11 +12,10 @@ use syntax::{ use crate::{ doc_links::{doc_attributes, extract_definitions_from_docs, resolve_doc_path_for_def}, + syntax_highlighting::{highlights::Highlights, injector::Injector}, Analysis, HlMod, HlRange, HlTag, RootDatabase, }; -use super::{highlights::Highlights, injector::Injector}; - pub(super) fn ra_fixture( hl: &mut Highlights, sema: &Semantics, diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs index 24ff473ecdf..211dc54f2b6 100644 --- a/crates/ide/src/syntax_highlighting/injector.rs +++ b/crates/ide/src/syntax_highlighting/injector.rs @@ -17,9 +17,11 @@ impl Injector { assert_eq!(len, source_range.len()); self.add_impl(text, Some(source_range.start())); } + pub(super) fn add_unmapped(&mut self, text: &str) { self.add_impl(text, None); } + fn add_impl(&mut self, text: &str, source: Option) { let len = TextSize::of(text); let target_range = TextRange::at(TextSize::of(&self.buf), len); @@ -30,6 +32,7 @@ impl Injector { pub(super) fn text(&self) -> &str { &self.buf } + pub(super) fn map_range_up(&self, range: TextRange) -> impl Iterator + '_ { equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| { let (target_range, delta) = self.ranges[i];