Repository: rust-analyzer/rust-analyzer
Pull request #11687 (2022-03-12 11:02:04)

Highlight escape sequences in byte strings
#11605 


Patch:
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 0119ba5cb7..e5d4ed8b92 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -7,6 +7,7 @@ mod highlight;
 mod format;
 mod macro_;
 mod inject;
+mod escape;
 
 mod html;
 #[cfg(test)]
@@ -16,16 +17,13 @@ use hir::{InFile, Name, Semantics};
 use ide_db::RootDatabase;
 use rustc_hash::FxHashMap;
 use syntax::{
-    ast::{self, IsString},
-    AstNode, AstToken, NodeOrToken,
-    SyntaxKind::*,
-    SyntaxNode, TextRange, WalkEvent, T,
+    ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T,
 };
 
 use crate::{
     syntax_highlighting::{
-        format::highlight_format_string, highlights::Highlights, macro_::MacroHighlighter,
-        tags::Highlight,
+        escape::highlight_escape_string, format::highlight_format_string, highlights::Highlights,
+        macro_::MacroHighlighter, tags::Highlight,
     },
     FileId, HlMod, HlTag,
 };
@@ -371,29 +369,25 @@ fn traverse(
         // 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(descended_token)) = (token, descended_element.as_token()) {
-            let string = ast::String::cast(token);
-            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() {
-                        continue;
+            if ast::String::can_cast(token.kind()) && ast::String::can_cast(descended_token.kind())
+            {
+                let string = ast::String::cast(token);
+                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() {
+                            continue;
+                        }
                     }
+                    highlight_format_string(hl, &string, &expanded_string, range);
+                    highlight_escape_string(hl, &string, range.start());
+                }
+            } else if ast::ByteString::can_cast(token.kind())
+                && ast::ByteString::can_cast(descended_token.kind())
+            {
+                if let Some(byte_string) = ast::ByteString::cast(token) {
+                    highlight_escape_string(hl, &byte_string, range.start());
                 }
-                highlight_format_string(hl, &string, &expanded_string, range);
-                // Highlight escape sequences
-                string.escaped_char_ranges(&mut |piece_range, char| {
-                    if char.is_err() {
-                        return;
-                    }
-
-                    if string.text()[piece_range.start().into()..].starts_with('\\') {
-                        hl.add(HlRange {
-                            range: piece_range + range.start(),
-                            highlight: HlTag::EscapeSequence.into(),
-                            binding_hash: None,
-                        });
-                    }
-                });
             }
         }
 
diff --git a/crates/ide/src/syntax_highlighting/escape.rs b/crates/ide/src/syntax_highlighting/escape.rs
new file mode 100644
index 0000000000..6a1236c793
--- /dev/null
+++ b/crates/ide/src/syntax_highlighting/escape.rs
@@ -0,0 +1,25 @@
+//! Syntax highlighting for escape sequences
+use crate::syntax_highlighting::highlights::Highlights;
+use crate::{HlRange, HlTag};
+use syntax::ast::IsString;
+use syntax::TextSize;
+
+pub(super) fn highlight_escape_string<T: IsString>(
+    stack: &mut Highlights,
+    string: &T,
+    start: TextSize,
+) {
+    string.escaped_char_ranges(&mut |piece_range, char| {
+        if char.is_err() {
+            return;
+        }
+
+        if string.text()[piece_range.start().into()..].starts_with('\\') {
+            stack.add(HlRange {
+                range: piece_range + start,
+                highlight: HlTag::EscapeSequence.into(),
+                binding_hash: None,
+            });
+        }
+    });
+}


Test patch:
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index 07c7881a1f..3715164bbf 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -136,6 +136,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
+    <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
+    <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span>
+
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> A <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
     <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span><span class="comma">,</span> ничоси <span class="operator">=</span> <span class="numeric_literal">92</span><span class="parenthesis">)</span><span class="semicolon">;</span>
 
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index efd03c2823..a8c69875e0 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -459,6 +459,9 @@ fn main() {
     println!("Hello\nWorld");
     println!("\u{48}\x65\x6C\x6C\x6F World");
 
+    let _ = "\x28\x28\x00\x63\n";
+    let _ = b"\x28\x28\x00\x63\n";
+
     println!("{\x41}", A = 92);
     println!("{ничоси}", ничоси = 92);