Unnamed repository; edit this file 'description' to name the repository.
Merge #10406
10406: internal: Restructure syntax element highlighting into node and token branches r=Veykril a=Veykril Gets rid of all the unseemly unwraps bors r+ Co-authored-by: Lukas Wirth <[email protected]>
bors[bot] 2021-10-01
parent 8bf2d4f · parent c5ceaef · commit c30a6a2
-rw-r--r--crates/ide/src/syntax_highlighting.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs394
-rw-r--r--crates/ide/src/syntax_highlighting/inject.rs3
-rw-r--r--crates/ide/src/syntax_highlighting/injector.rs3
4 files changed, 211 insertions, 190 deletions
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index b4331fc09f..cb23fec3c7 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 5113ab84d5..65705d303c 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<u64>)> {
- let mut binding_hash = None;
- let highlight: Highlight = match element.kind() {
- FN => {
- bindings_shadow_count.clear();
- return None;
- }
- // 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)
+ match element {
+ NodeOrToken::Node(it) => {
+ node(sema, krate, bindings_shadow_count, syntactic_name_ref_highlighting, it)
}
- 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();
-
- 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,
- }
- }
- IDENT if parent_matches::<ast::TokenTree>(&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()
- }
+ NodeOrToken::Token(it) => Some((token(sema, krate, it)?, None)),
+ }
+}
+
+fn token(
+ sema: &Semantics<RootDatabase>,
+ krate: Option<hir::Crate>,
+ token: SyntaxToken,
+) -> Option<Highlight> {
+ 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(),
}
- p if p.is_punct() => match p {
- T![&] if parent_matches::<ast::BinExpr>(&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
+ } 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()
+ }
+ 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::<ast::TokenTree>(&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::<ast::MacroCall>(&element) => SymbolKind::Macro.into(),
- T![!] if parent_matches::<ast::NeverType>(&element) => HlTag::BuiltinType.into(),
- T![!] if parent_matches::<ast::PrefixExpr>(&element) => HlOperator::Logical.into(),
- T![*] if parent_matches::<ast::PtrType>(&element) => HlTag::Keyword.into(),
- T![*] if parent_matches::<ast::PrefixExpr>(&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::<ast::BinExpr>(&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::<ast::PrefixExpr>(&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),
+ T![!] if parent_matches::<ast::MacroCall>(&token) => SymbolKind::Macro.into(),
+ T![!] if parent_matches::<ast::NeverType>(&token) => HlTag::BuiltinType.into(),
+ T![!] if parent_matches::<ast::PrefixExpr>(&token) => HlOperator::Logical.into(),
+ T![*] if parent_matches::<ast::PtrType>(&token) => HlTag::Keyword.into(),
+ T![*] if parent_matches::<ast::PrefixExpr>(&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()
+ }
}
- .into()
- }
- _ if parent_matches::<ast::PrefixExpr>(&element) => HlOperator::Other.into(),
- T![+] | T![-] | T![*] | T![/] if parent_matches::<ast::BinExpr>(&element) => {
- HlOperator::Arithmetic.into()
- }
- T![+=] | T![-=] | T![*=] | T![/=] if parent_matches::<ast::BinExpr>(&element) => {
- Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
- }
- T![|] | T![&] | T![!] | T![^] if parent_matches::<ast::BinExpr>(&element) => {
- HlOperator::Bitwise.into()
- }
- T![|=] | T![&=] | T![^=] if parent_matches::<ast::BinExpr>(&element) => {
- Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
- }
- T![&&] | T![||] if parent_matches::<ast::BinExpr>(&element) => {
- HlOperator::Logical.into()
- }
- T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
- if parent_matches::<ast::BinExpr>(&element) =>
- {
- HlOperator::Comparison.into()
- }
- _ if parent_matches::<ast::BinExpr>(&element) => HlOperator::Other.into(),
- _ if parent_matches::<ast::RangeExpr>(&element) => HlOperator::Other.into(),
- _ if parent_matches::<ast::RangePat>(&element) => HlOperator::Other.into(),
- _ if parent_matches::<ast::RestPat>(&element) => HlOperator::Other.into(),
- _ if parent_matches::<ast::Attr>(&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(),
- },
+ T![-] if parent_matches::<ast::PrefixExpr>(&token) => {
+ let prefix_expr = token.parent().and_then(ast::PrefixExpr::cast)?;
- 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,
+ let expr = prefix_expr.expr()?;
+ match expr {
+ ast::Expr::Literal(_) => HlTag::NumericLiteral,
+ _ => HlTag::Operator(HlOperator::Other),
+ }
+ .into()
+ }
+ _ if parent_matches::<ast::PrefixExpr>(&token) => HlOperator::Other.into(),
+ T![+] | T![-] | T![*] | T![/] if parent_matches::<ast::BinExpr>(&token) => {
+ HlOperator::Arithmetic.into()
+ }
+ T![+=] | T![-=] | T![*=] | T![/=] if parent_matches::<ast::BinExpr>(&token) => {
+ Highlight::from(HlOperator::Arithmetic) | HlMod::Mutable
+ }
+ T![|] | T![&] | T![!] | T![^] if parent_matches::<ast::BinExpr>(&token) => {
+ HlOperator::Bitwise.into()
+ }
+ T![|=] | T![&=] | T![^=] if parent_matches::<ast::BinExpr>(&token) => {
+ Highlight::from(HlOperator::Bitwise) | HlMod::Mutable
+ }
+ T![&&] | T![||] if parent_matches::<ast::BinExpr>(&token) => {
+ HlOperator::Logical.into()
+ }
+ T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
+ if parent_matches::<ast::BinExpr>(&token) =>
+ {
+ HlOperator::Comparison.into()
+ }
+ _ if parent_matches::<ast::BinExpr>(&token) => HlOperator::Other.into(),
+ _ if parent_matches::<ast::RangeExpr>(&token) => HlOperator::Other.into(),
+ _ if parent_matches::<ast::RangePat>(&token) => HlOperator::Other.into(),
+ _ if parent_matches::<ast::RestPat>(&token) => HlOperator::Other.into(),
+ _ if parent_matches::<ast::Attr>(&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,
+ }
}
+ _ => return None,
}
-
- _ => return None,
};
+ Some(highlight)
+}
- return Some((highlight, binding_hash));
+fn node(
+ sema: &Semantics<RootDatabase>,
+ krate: Option<hir::Crate>,
+ bindings_shadow_count: &mut FxHashMap<hir::Name, u32>,
+ syntactic_name_ref_highlighting: bool,
+ node: SyntaxNode,
+) -> Option<(Highlight, Option<u64>)> {
+ 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<RootDatabase>, name_ref: ast::NameRef) -> Highlight {
@@ -715,12 +733,12 @@ fn parents_match(mut node: NodeOrToken<SyntaxNode, SyntaxToken>, mut kinds: &[Sy
}
#[inline]
-fn parent_matches<N: AstNode>(element: &SyntaxElement) -> bool {
- element.parent().map_or(false, |it| N::can_cast(it.kind()))
+fn parent_matches<N: AstNode>(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 7553852659..91587b11f5 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<RootDatabase>,
diff --git a/crates/ide/src/syntax_highlighting/injector.rs b/crates/ide/src/syntax_highlighting/injector.rs
index 24ff473ecd..211dc54f2b 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<TextSize>) {
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<Item = TextRange> + '_ {
equal_range_by(&self.ranges, |&(r, _)| TextRange::ordering(r, range)).filter_map(move |i| {
let (target_range, delta) = self.ranges[i];