diff --git a/CHANGES.md b/CHANGES.md
index 016bc0bc76..78eb8f7fc4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -6,6 +6,7 @@ Core Grammars:
- enh(dos) add `batch` as an alias, issue #4395 [Hashim Khan][]
- fix(lisp) preserve highlighting after quoted multiplication expressions [arturict][]
- fix(rust) recognize `\\` and `\"` char-literal escapes so highlighting doesn't leak, issue #4351 [Sarath Francis][]
+- fix(rust) reuse the string rules inside attributes so raw and byte strings are highlighted there, issue #3817 [Maksim Zhukau][]
- fix(cmake) only highlight standalone numbers, not digits that begin an identifier (e.g. `3rdparty`), issue #4170 [MarkXian][]
- fix(cpp) require a word boundary before numeric literals so digits inside identifiers aren't highlighted as numbers, issue #4231 [Mark Xian][]
- fix(c) only match real `atomic_*` type names, not C11 atomic functions, issue #3837 [MarkXian][]
@@ -43,6 +44,7 @@ CONTRIBUTORS
[Nicolas Le Cam]: https://github.com/KuSh
[Konstantin Baltsat]: https://github.com/Baltsat
[David Pavlovschii]: https://github.com/davidpavlovschi
+[Maksim Zhukau]: https://github.com/MaksZhukov
[Pablo]: https://github.com/MsfPablo
[Jayesh Bhade]: https://github.com/Jaybhade
[Hirse]: https://github.com/Hirse
diff --git a/src/languages/rust.js b/src/languages/rust.js
index 633ec69bde..a98e89b62f 100644
--- a/src/languages/rust.js
+++ b/src/languages/rust.js
@@ -157,6 +157,26 @@ export default function(hljs) {
"assert_ne!",
"debug_assert_ne!"
];
+ const QUOTE_STRING = hljs.inherit(hljs.QUOTE_STRING_MODE, {
+ begin: /b?"/,
+ illegal: null
+ });
+ const RAW_STRING = {
+ scope: 'string',
+ // rustc caps raw string delimiters at 255 hashes
+ begin: /b?r(#{0,255})"(.|\n)*?"\1(?!#)/
+ };
+ const CHARACTER = {
+ scope: 'string',
+ begin: /b?'/,
+ end: /'/,
+ contains: [
+ {
+ scope: "char.escape",
+ match: /\\('|"|\\|\w|x\w{2}|u\w{4}|U\w{8})/
+ }
+ ]
+ };
const TYPES = [
"i8",
"i16",
@@ -197,31 +217,14 @@ export default function(hljs) {
contains: [
hljs.C_LINE_COMMENT_MODE,
hljs.COMMENT('/\\*', '\\*/', { contains: [ 'self' ] }),
- hljs.inherit(hljs.QUOTE_STRING_MODE, {
- begin: /b?"/,
- illegal: null
- }),
+ QUOTE_STRING,
{
scope: 'symbol',
// negative lookahead to avoid matching `'`
begin: /'[a-zA-Z_][a-zA-Z0-9_]*(?!')/
},
- {
- scope: 'string',
- variants: [
- { begin: /b?r(#*)"(.|\n)*?"\1(?!#)/ },
- {
- begin: /b?'/,
- end: /'/,
- contains: [
- {
- scope: "char.escape",
- match: /\\('|"|\\|\w|x\w{2}|u\w{4}|U\w{8})/
- }
- ]
- }
- ]
- },
+ RAW_STRING,
+ CHARACTER,
{
scope: 'number',
variants: [
@@ -260,14 +263,10 @@ export default function(hljs) {
begin: '#!?\\[',
end: '\\]',
contains: [
- {
- scope: 'string',
- begin: /"/,
- end: /"/,
- contains: [
- hljs.BACKSLASH_ESCAPE
- ]
- }
+ // attributes hold arbitrary token trees, so any string literal
+ // that is valid elsewhere is valid here too
+ QUOTE_STRING,
+ RAW_STRING
]
},
{
diff --git a/test/markup/rust/strings.expect.txt b/test/markup/rust/strings.expect.txt
index 6ddd0da66e..26f1188722 100644
--- a/test/markup/rust/strings.expect.txt
+++ b/test/markup/rust/strings.expect.txt
@@ -22,5 +22,11 @@
br" ";
br#"hello"#;
br##"foo"##;
+r################"many hashes"################;
#[error("\" appears in a string")]
+#[doc = r#"a "quoted" word"#]
+#[doc = b"bytes"]
+#[doc = r##"multi
+line "# raw"##]
+#[foo(Bar<'a>)]
diff --git a/test/markup/rust/strings.txt b/test/markup/rust/strings.txt
index e4d85db470..502deac7de 100644
--- a/test/markup/rust/strings.txt
+++ b/test/markup/rust/strings.txt
@@ -22,5 +22,11 @@ r##" "###
br" ";
br#"hello"#;
br##"foo"##;
+r################"many hashes"################;
#[error("\" appears in a string")]
+#[doc = r#"a "quoted" word"#]
+#[doc = b"bytes"]
+#[doc = r##"multi
+line "# raw"##]
+#[foo(Bar<'a>)]