Repository: rust-analyzer/rust-analyzer
Pull request #8354 (2021-04-05 19:23:07)

Distinguishing between different operators in semantic highlighting


Patch:
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 3f73c06322..b5ae51d28c 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -82,7 +82,7 @@ pub use crate::{
     references::{rename::RenameError, ReferenceSearchResult},
     runnables::{Runnable, RunnableKind, TestId},
     syntax_highlighting::{
-        tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
+        tags::{Highlight, HlMod, HlMods, HlOperator, HlPunct, HlTag},
         HlRange,
     },
 };
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 5ccb847149..8dd5d801b5 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -12,7 +12,10 @@ use syntax::{
     SyntaxNode, SyntaxToken, T,
 };
 
-use crate::{syntax_highlighting::tags::HlPunct, Highlight, HlMod, HlTag};
+use crate::{
+    syntax_highlighting::tags::{HlOperator, HlPunct},
+    Highlight, HlMod, HlTag,
+};
 
 pub(super) fn element(
     sema: &Semantics<RootDatabase>,
@@ -132,7 +135,7 @@ pub(super) fn element(
         INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(),
         BYTE => HlTag::ByteLiteral.into(),
         CHAR => HlTag::CharLiteral.into(),
-        QUESTION => Highlight::new(HlTag::Operator) | HlMod::ControlFlow,
+        QUESTION => Highlight::new(HlTag::Operator(HlOperator::Other)) | HlMod::ControlFlow,
         LIFETIME => {
             let lifetime = element.into_node().and_then(ast::Lifetime::cast).unwrap();
 
@@ -146,8 +149,11 @@ pub(super) fn element(
             }
         }
         p if p.is_punct() => match p {
+            T![&] if element.parent().and_then(ast::BinExpr::cast).is_some() => {
+                HlTag::Operator(HlOperator::Bitwise).into()
+            }
             T![&] => {
-                let h = HlTag::Operator.into();
+                let h = HlTag::Operator(HlOperator::Other).into();
                 let is_unsafe = element
                     .parent()
                     .and_then(ast::RefExpr::cast)
@@ -159,13 +165,18 @@ pub(super) fn element(
                     h
                 }
             }
-            T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => HlTag::Operator.into(),
+            T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] | T![.] => {
+                HlTag::Operator(HlOperator::Other).into()
+            }
             T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
                 HlTag::Symbol(SymbolKind::Macro).into()
             }
             T![!] if element.parent().and_then(ast::NeverType::cast).is_some() => {
                 HlTag::BuiltinType.into()
             }
+            T![!] if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
+                HlTag::Operator(HlOperator::Logical).into()
+            }
             T![*] if element.parent().and_then(ast::PtrType::cast).is_some() => {
                 HlTag::Keyword.into()
             }
@@ -175,9 +186,9 @@ pub(super) fn element(
                 let expr = prefix_expr.expr()?;
                 let ty = sema.type_of_expr(&expr)?;
                 if ty.is_raw_ptr() {
-                    HlTag::Operator | HlMod::Unsafe
+                    HlTag::Operator(HlOperator::Other) | HlMod::Unsafe
                 } else if let Some(ast::PrefixOp::Deref) = prefix_expr.op_kind() {
-                    HlTag::Operator.into()
+                    HlTag::Operator(HlOperator::Other).into()
                 } else {
                     HlTag::Punctuation(HlPunct::Other).into()
                 }
@@ -188,19 +199,43 @@ pub(super) fn element(
                 let expr = prefix_expr.expr()?;
                 match expr {
                     ast::Expr::Literal(_) => HlTag::NumericLiteral,
-                    _ => HlTag::Operator,
+                    _ => HlTag::Operator(HlOperator::Other),
                 }
                 .into()
             }
             _ if element.parent().and_then(ast::PrefixExpr::cast).is_some() => {
-                HlTag::Operator.into()
+                HlTag::Operator(HlOperator::Other).into()
+            }
+            T![+] | T![-] | T![*] | T![/] | T![+=] | T![-=] | T![*=] | T![/=]
+                if element.parent().and_then(ast::BinExpr::cast).is_some() =>
+            {
+                HlTag::Operator(HlOperator::Arithmetic).into()
+            }
+            T![|] | T![&] | T![!] | T![^] | T![|=] | T![&=] | T![^=]
+                if element.parent().and_then(ast::BinExpr::cast).is_some() =>
+            {
+                HlTag::Operator(HlOperator::Bitwise).into()
+            }
+            T![&&] | T![||] if element.parent().and_then(ast::BinExpr::cast).is_some() => {
+                HlTag::Operator(HlOperator::Logical).into()
+            }
+            T![>] | T![<] | T![==] | T![>=] | T![<=] | T![!=]
+                if element.parent().and_then(ast::BinExpr::cast).is_some() =>
+            {
+                HlTag::Operator(HlOperator::Comparision).into()
+            }
+            _ if element.parent().and_then(ast::BinExpr::cast).is_some() => {
+                HlTag::Operator(HlOperator::Other).into()
             }
-            _ if element.parent().and_then(ast::BinExpr::cast).is_some() => HlTag::Operator.into(),
             _ if element.parent().and_then(ast::RangeExpr::cast).is_some() => {
-                HlTag::Operator.into()
+                HlTag::Operator(HlOperator::Other).into()
+            }
+            _ if element.parent().and_then(ast::RangePat::cast).is_some() => {
+                HlTag::Operator(HlOperator::Other).into()
+            }
+            _ if element.parent().and_then(ast::RestPat::cast).is_some() => {
+                HlTag::Operator(HlOperator::Other).into()
             }
-            _ if element.parent().and_then(ast::RangePat::cast).is_some() => HlTag::Operator.into(),
-            _ if element.parent().and_then(ast::RestPat::cast).is_some() => HlTag::Operator.into(),
             _ if element.parent().and_then(ast::Attr::cast).is_some() => HlTag::Attribute.into(),
             kind => HlTag::Punctuation(match kind {
                 T!['['] | T![']'] => HlPunct::Bracket,
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 1cec991aa6..8128d231d5 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -28,7 +28,7 @@ pub enum HlTag {
     FormatSpecifier,
     Keyword,
     NumericLiteral,
-    Operator,
+    Operator(HlOperator),
     Punctuation(HlPunct),
     StringLiteral,
     UnresolvedReference,
@@ -87,6 +87,20 @@ pub enum HlPunct {
     Other,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub enum HlOperator {
+    /// |, &, !, ^, |=, &=, ^=
+    Bitwise,
+    /// +, -, *, /, +=, -=, *=, /=
+    Arithmetic,
+    /// &&, ||, !
+    Logical,
+    /// >, <, ==, >=, <=, !=
+    Comparision,
+    ///
+    Other,
+}
+
 impl HlTag {
     fn as_str(self) -> &'static str {
         match self {
@@ -133,7 +147,13 @@ impl HlTag {
                 HlPunct::Other => "punctuation",
             },
             HlTag::NumericLiteral => "numeric_literal",
-            HlTag::Operator => "operator",
+            HlTag::Operator(op) => match op {
+                HlOperator::Bitwise => "bitwise",
+                HlOperator::Arithmetic => "arithmetic",
+                HlOperator::Logical => "logical",
+                HlOperator::Comparision => "comparision",
+                HlOperator::Other => "operator",
+            },
             HlTag::StringLiteral => "string_literal",
             HlTag::UnresolvedReference => "unresolved_reference",
             HlTag::None => "none",
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 2dc8a42f1e..adc0598173 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -39,7 +39,9 @@ macro_rules! define_semantic_token_types {
 
 define_semantic_token_types![
     (ANGLE, "angle"),
+    (ARITHMETIC, "arithmetic"),
     (ATTRIBUTE, "attribute"),
+    (BITWISE, "bitwise"),
     (BOOLEAN, "boolean"),
     (BRACE, "brace"),
     (BRACKET, "bracket"),
@@ -47,6 +49,7 @@ define_semantic_token_types![
     (CHAR_LITERAL, "characterLiteral"),
     (COLON, "colon"),
     (COMMA, "comma"),
+    (COMPARISION, "comparision"),
     (CONST_PARAMETER, "constParameter"),
     (DOT, "dot"),
     (ESCAPE_SEQUENCE, "escapeSequence"),
@@ -54,6 +57,8 @@ define_semantic_token_types![
     (GENERIC, "generic"),
     (LABEL, "label"),
     (LIFETIME, "lifetime"),
+    (LOGICAL, "logical"),
+    (OPERATOR, "operator"),
     (PARENTHESIS, "parenthesis"),
     (PUNCTUATION, "punctuation"),
     (SELF_KEYWORD, "selfKeyword"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 2ac31d981b..df9292f8e4 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -7,9 +7,9 @@ use std::{
 use ide::{
     Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
     CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
-    Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
-    Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
-    StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
+    Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind,
+    InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity,
+    SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
 };
 use itertools::Itertools;
 use serde_json::to_value;
@@ -445,7 +445,13 @@ fn semantic_token_type_and_modifiers(
         HlTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
         HlTag::Keyword => lsp_types::SemanticTokenType::KEYWORD,
         HlTag::None => semantic_tokens::GENERIC,
-        HlTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
+        HlTag::Operator(op) => match op {
+            HlOperator::Bitwise => semantic_tokens::BITWISE,
+            HlOperator::Arithmetic => semantic_tokens::ARITHMETIC,
+            HlOperator::Logical => semantic_tokens::LOGICAL,
+            HlOperator::Comparision => semantic_tokens::COMPARISION,
+            HlOperator::Other => semantic_tokens::OPERATOR,
+        },
         HlTag::StringLiteral => lsp_types::SemanticTokenType::STRING,
         HlTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
         HlTag::Punctuation(punct) => match punct {


Test patch:
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index b6d1cac4ea..6ee6d85fbe 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -76,7 +76,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="comment documentation">/// </span><span class="comment injected">// calls bar on foo</span>
     <span class="comment documentation">/// </span><span class="macro injected">assert!</span><span class="parenthesis injected">(</span><span class="none injected">foo</span><span class="operator injected">.</span><span class="none injected">bar</span><span class="parenthesis injected">(</span><span class="parenthesis injected">)</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span>
     <span class="comment documentation">///</span>
-    <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="operator injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
+    <span class="comment documentation">/// </span><span class="keyword injected">let</span><span class="none injected"> </span><span class="variable declaration injected">bar</span><span class="none injected"> </span><span class="operator injected">=</span><span class="none injected"> </span><span class="variable injected">foo</span><span class="operator injected">.</span><span class="field injected">bar</span><span class="none injected"> </span><span class="logical injected">||</span><span class="none injected"> </span><span class="struct injected">Foo</span><span class="operator injected">::</span><span class="constant injected">bar</span><span class="semicolon injected">;</span>
     <span class="comment documentation">///</span>
     <span class="comment documentation">/// </span><span class="comment injected">/* multi-line</span>
     <span class="comment documentation">/// </span><span class="comment injected">       comment */</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 973173254c..c43bcb6911 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -213,7 +213,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="numeric_literal">-</span><span class="numeric_literal">42</span><span class="semicolon">;</span>
     <span class="keyword">let</span> <span class="variable declaration">baz</span> <span class="operator">=</span> <span class="operator">-</span><span class="variable">baz</span><span class="semicolon">;</span>
 
-    <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="operator">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="logical">!</span><span class="bool_literal">true</span><span class="semicolon">;</span>
 
     <span class="label declaration">'foo</span><span class="colon">:</span> <span class="keyword control">loop</span> <span class="brace">{</span>
         <span class="keyword control">break</span> <span class="label">'foo</span><span class="semicolon">;</span>