aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/syntax_highlighting
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting')
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs11
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs188
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs18
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs201
4 files changed, 374 insertions, 44 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index edfe61f39..0be55bca9 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -19,7 +19,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
19 ) 19 )
20 } 20 }
21 21
22 let ranges = highlight(db, file_id, None); 22 let ranges = highlight(db, file_id, None, false);
23 let text = parse.tree().syntax().to_string(); 23 let text = parse.tree().syntax().to_string();
24 let mut prev_pos = TextSize::from(0); 24 let mut prev_pos = TextSize::from(0);
25 let mut buf = String::new(); 25 let mut buf = String::new();
@@ -64,11 +64,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
64 64
65.lifetime { color: #DFAF8F; font-style: italic; } 65.lifetime { color: #DFAF8F; font-style: italic; }
66.comment { color: #7F9F7F; } 66.comment { color: #7F9F7F; }
67.documentation { color: #629755; }
68.injected { opacity: 0.65 ; }
67.struct, .enum { color: #7CB8BB; } 69.struct, .enum { color: #7CB8BB; }
68.enum_variant { color: #BDE0F3; } 70.enum_variant { color: #BDE0F3; }
69.string_literal { color: #CC9393; } 71.string_literal { color: #CC9393; }
70.field { color: #94BFF3; } 72.field { color: #94BFF3; }
71.function { color: #93E0E3; } 73.function { color: #93E0E3; }
74.function.unsafe { color: #BC8383; }
75.operator.unsafe { color: #BC8383; }
72.parameter { color: #94BFF3; } 76.parameter { color: #94BFF3; }
73.text { color: #DCDCCC; } 77.text { color: #DCDCCC; }
74.type { color: #7CB8BB; } 78.type { color: #7CB8BB; }
@@ -79,12 +83,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
79.bool_literal { color: #BFE6EB; } 83.bool_literal { color: #BFE6EB; }
80.macro { color: #94BFF3; } 84.macro { color: #94BFF3; }
81.module { color: #AFD8AF; } 85.module { color: #AFD8AF; }
86.value_param { color: #DCDCCC; }
82.variable { color: #DCDCCC; } 87.variable { color: #DCDCCC; }
83.format_specifier { color: #CC696B; } 88.format_specifier { color: #CC696B; }
84.mutable { text-decoration: underline; } 89.mutable { text-decoration: underline; }
85 90.escape_sequence { color: #94BFF3; }
86.keyword { color: #F0DFAF; font-weight: bold; } 91.keyword { color: #F0DFAF; font-weight: bold; }
87.keyword.unsafe { color: #BC8383; font-weight: bold; } 92.keyword.unsafe { color: #BC8383; font-weight: bold; }
88.control { font-style: italic; } 93.control { font-style: italic; }
94
95.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
89</style> 96</style>
90"; 97";
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
new file mode 100644
index 000000000..8665b480f
--- /dev/null
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -0,0 +1,188 @@
1//! Syntax highlighting injections such as highlighting of documentation tests.
2
3use std::{collections::BTreeMap, convert::TryFrom};
4
5use ast::{HasQuotes, HasStringValue};
6use hir::Semantics;
7use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize};
8use stdx::SepBy;
9
10use crate::{
11 call_info::ActiveParameter, Analysis, Highlight, HighlightModifier, HighlightTag,
12 HighlightedRange, RootDatabase,
13};
14
15use super::HighlightedRangeStack;
16
17pub(super) fn highlight_injection(
18 acc: &mut HighlightedRangeStack,
19 sema: &Semantics<RootDatabase>,
20 literal: ast::RawString,
21 expanded: SyntaxToken,
22) -> Option<()> {
23 let active_parameter = ActiveParameter::at_token(&sema, expanded)?;
24 if !active_parameter.name.starts_with("ra_fixture") {
25 return None;
26 }
27 let value = literal.value()?;
28 let (analysis, tmp_file_id) = Analysis::from_single_file(value.into_owned());
29
30 if let Some(range) = literal.open_quote_text_range() {
31 acc.add(HighlightedRange {
32 range,
33 highlight: HighlightTag::StringLiteral.into(),
34 binding_hash: None,
35 })
36 }
37
38 for mut h in analysis.highlight(tmp_file_id).unwrap() {
39 if let Some(r) = literal.map_range_up(h.range) {
40 h.range = r;
41 acc.add(h)
42 }
43 }
44
45 if let Some(range) = literal.close_quote_text_range() {
46 acc.add(HighlightedRange {
47 range,
48 highlight: HighlightTag::StringLiteral.into(),
49 binding_hash: None,
50 })
51 }
52
53 Some(())
54}
55
56/// Mapping from extracted documentation code to original code
57type RangesMap = BTreeMap<TextSize, TextSize>;
58
59const RUSTDOC_FENCE: &'static str = "```";
60const RUSTDOC_FENCE_TOKENS: &[&'static str] =
61 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
62
63/// Extracts Rust code from documentation comments as well as a mapping from
64/// the extracted source code back to the original source ranges.
65/// Lastly, a vector of new comment highlight ranges (spanning only the
66/// comment prefix) is returned which is used in the syntax highlighting
67/// injection to replace the previous (line-spanning) comment ranges.
68pub(super) fn extract_doc_comments(
69 node: &SyntaxNode,
70) -> Option<(String, RangesMap, Vec<HighlightedRange>)> {
71 // wrap the doctest into function body to get correct syntax highlighting
72 let prefix = "fn doctest() {\n";
73 let suffix = "}\n";
74 // Mapping from extracted documentation code to original code
75 let mut range_mapping: RangesMap = BTreeMap::new();
76 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
77 let mut is_codeblock = false;
78 let mut is_doctest = false;
79 // Replace the original, line-spanning comment ranges by new, only comment-prefix
80 // spanning comment ranges.
81 let mut new_comments = Vec::new();
82 let doctest = node
83 .children_with_tokens()
84 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
85 .filter(|comment| comment.kind().doc.is_some())
86 .filter(|comment| {
87 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
88 is_codeblock = !is_codeblock;
89 // Check whether code is rust by inspecting fence guards
90 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
91 let is_rust =
92 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
93 is_doctest = is_codeblock && is_rust;
94 false
95 } else {
96 is_doctest
97 }
98 })
99 .map(|comment| {
100 let prefix_len = comment.prefix().len();
101 let line: &str = comment.text().as_str();
102 let range = comment.syntax().text_range();
103
104 // whitespace after comment is ignored
105 let pos = if let Some(ws) = line.chars().nth(prefix_len).filter(|c| c.is_whitespace()) {
106 prefix_len + ws.len_utf8()
107 } else {
108 prefix_len
109 };
110
111 // lines marked with `#` should be ignored in output, we skip the `#` char
112 let pos = if let Some(ws) = line.chars().nth(pos).filter(|&c| c == '#') {
113 pos + ws.len_utf8()
114 } else {
115 pos
116 };
117
118 range_mapping.insert(line_start, range.start() + TextSize::try_from(pos).unwrap());
119 new_comments.push(HighlightedRange {
120 range: TextRange::new(
121 range.start(),
122 range.start() + TextSize::try_from(pos).unwrap(),
123 ),
124 highlight: HighlightTag::Comment | HighlightModifier::Documentation,
125 binding_hash: None,
126 });
127 line_start += range.len() - TextSize::try_from(pos).unwrap();
128 line_start += TextSize::try_from('\n'.len_utf8()).unwrap();
129
130 line[pos..].to_owned()
131 })
132 .sep_by("\n")
133 .to_string();
134
135 if doctest.is_empty() {
136 return None;
137 }
138
139 let doctest = format!("{}{}{}", prefix, doctest, suffix);
140 Some((doctest, range_mapping, new_comments))
141}
142
143/// Injection of syntax highlighting of doctests.
144pub(super) fn highlight_doc_comment(
145 text: String,
146 range_mapping: RangesMap,
147 new_comments: Vec<HighlightedRange>,
148 stack: &mut HighlightedRangeStack,
149) {
150 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
151
152 stack.push();
153 for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
154 // Determine start offset and end offset in case of multi-line ranges
155 let mut start_offset = None;
156 let mut end_offset = None;
157 for (line_start, orig_line_start) in range_mapping.range(..h.range.end()).rev() {
158 // It's possible for orig_line_start - line_start to be negative. Add h.range.start()
159 // here and remove it from the end range after the loop below so that the values are
160 // always non-negative.
161 let offset = h.range.start() + orig_line_start - line_start;
162 if line_start <= &h.range.start() {
163 start_offset.get_or_insert(offset);
164 break;
165 } else {
166 end_offset.get_or_insert(offset);
167 }
168 }
169 if let Some(start_offset) = start_offset {
170 h.range = TextRange::new(
171 start_offset,
172 h.range.end() + end_offset.unwrap_or(start_offset) - h.range.start(),
173 );
174
175 h.highlight |= HighlightModifier::Injected;
176 stack.add(h);
177 }
178 }
179
180 // Inject the comment prefix highlight ranges
181 stack.push();
182 for comment in new_comments {
183 stack.add(comment);
184 }
185 stack.pop_and_inject(None);
186 stack
187 .pop_and_inject(Some(Highlight::from(HighlightTag::Generic) | HighlightModifier::Injected));
188}
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 1514531de..49ec94bdc 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -23,13 +23,16 @@ pub enum HighlightTag {
23 Constant, 23 Constant,
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 EscapeSequence,
26 Field, 27 Field,
27 Function, 28 Function,
29 Generic,
28 Keyword, 30 Keyword,
29 Lifetime, 31 Lifetime,
30 Macro, 32 Macro,
31 Module, 33 Module,
32 NumericLiteral, 34 NumericLiteral,
35 Punctuation,
33 SelfKeyword, 36 SelfKeyword,
34 SelfType, 37 SelfType,
35 Static, 38 Static,
@@ -39,6 +42,7 @@ pub enum HighlightTag {
39 TypeAlias, 42 TypeAlias,
40 TypeParam, 43 TypeParam,
41 Union, 44 Union,
45 ValueParam,
42 Local, 46 Local,
43 UnresolvedReference, 47 UnresolvedReference,
44 FormatSpecifier, 48 FormatSpecifier,
@@ -55,6 +59,8 @@ pub enum HighlightModifier {
55 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is 59 /// `foo` in `fn foo(x: i32)` is a definition, `foo` in `foo(90 + 2)` is
56 /// not. 60 /// not.
57 Definition, 61 Definition,
62 Documentation,
63 Injected,
58 Mutable, 64 Mutable,
59 Unsafe, 65 Unsafe,
60} 66}
@@ -71,13 +77,18 @@ impl HighlightTag {
71 HighlightTag::Constant => "constant", 77 HighlightTag::Constant => "constant",
72 HighlightTag::Enum => "enum", 78 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 79 HighlightTag::EnumVariant => "enum_variant",
80 HighlightTag::EscapeSequence => "escape_sequence",
74 HighlightTag::Field => "field", 81 HighlightTag::Field => "field",
82 HighlightTag::FormatSpecifier => "format_specifier",
75 HighlightTag::Function => "function", 83 HighlightTag::Function => "function",
84 HighlightTag::Generic => "generic",
76 HighlightTag::Keyword => "keyword", 85 HighlightTag::Keyword => "keyword",
77 HighlightTag::Lifetime => "lifetime", 86 HighlightTag::Lifetime => "lifetime",
87 HighlightTag::Punctuation => "punctuation",
78 HighlightTag::Macro => "macro", 88 HighlightTag::Macro => "macro",
79 HighlightTag::Module => "module", 89 HighlightTag::Module => "module",
80 HighlightTag::NumericLiteral => "numeric_literal", 90 HighlightTag::NumericLiteral => "numeric_literal",
91 HighlightTag::Operator => "operator",
81 HighlightTag::SelfKeyword => "self_keyword", 92 HighlightTag::SelfKeyword => "self_keyword",
82 HighlightTag::SelfType => "self_type", 93 HighlightTag::SelfType => "self_type",
83 HighlightTag::Static => "static", 94 HighlightTag::Static => "static",
@@ -87,10 +98,9 @@ impl HighlightTag {
87 HighlightTag::TypeAlias => "type_alias", 98 HighlightTag::TypeAlias => "type_alias",
88 HighlightTag::TypeParam => "type_param", 99 HighlightTag::TypeParam => "type_param",
89 HighlightTag::Union => "union", 100 HighlightTag::Union => "union",
101 HighlightTag::ValueParam => "value_param",
90 HighlightTag::Local => "variable", 102 HighlightTag::Local => "variable",
91 HighlightTag::UnresolvedReference => "unresolved_reference", 103 HighlightTag::UnresolvedReference => "unresolved_reference",
92 HighlightTag::FormatSpecifier => "format_specifier",
93 HighlightTag::Operator => "operator",
94 } 104 }
95 } 105 }
96} 106}
@@ -106,6 +116,8 @@ impl HighlightModifier {
106 HighlightModifier::Attribute, 116 HighlightModifier::Attribute,
107 HighlightModifier::ControlFlow, 117 HighlightModifier::ControlFlow,
108 HighlightModifier::Definition, 118 HighlightModifier::Definition,
119 HighlightModifier::Documentation,
120 HighlightModifier::Injected,
109 HighlightModifier::Mutable, 121 HighlightModifier::Mutable,
110 HighlightModifier::Unsafe, 122 HighlightModifier::Unsafe,
111 ]; 123 ];
@@ -115,6 +127,8 @@ impl HighlightModifier {
115 HighlightModifier::Attribute => "attribute", 127 HighlightModifier::Attribute => "attribute",
116 HighlightModifier::ControlFlow => "control", 128 HighlightModifier::ControlFlow => "control",
117 HighlightModifier::Definition => "declaration", 129 HighlightModifier::Definition => "declaration",
130 HighlightModifier::Documentation => "documentation",
131 HighlightModifier::Injected => "injected",
118 HighlightModifier::Mutable => "mutable", 132 HighlightModifier::Mutable => "mutable",
119 HighlightModifier::Unsafe => "unsafe", 133 HighlightModifier::Unsafe => "unsafe",
120 } 134 }
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index eb43a23da..87a6e2523 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -1,15 +1,13 @@
1use std::fs; 1use std::fs;
2 2
3use test_utils::{assert_eq_text, project_dir, read_text}; 3use expect::{expect_file, ExpectFile};
4use test_utils::project_dir;
4 5
5use crate::{ 6use crate::{mock_analysis::single_file, FileRange, TextRange};
6 mock_analysis::{single_file, MockAnalysis},
7 FileRange, TextRange,
8};
9 7
10#[test] 8#[test]
11fn test_highlighting() { 9fn test_highlighting() {
12 let (analysis, file_id) = single_file( 10 check_highlighting(
13 r#" 11 r#"
14#[derive(Clone, Debug)] 12#[derive(Clone, Debug)]
15struct Foo { 13struct Foo {
@@ -27,6 +25,16 @@ impl Bar for Foo {
27 } 25 }
28} 26}
29 27
28impl Foo {
29 fn baz(mut self) -> i32 {
30 self.x
31 }
32
33 fn qux(&mut self) {
34 self.x = 0;
35 }
36}
37
30static mut STATIC_MUT: i32 = 0; 38static mut STATIC_MUT: i32 = 0;
31 39
32fn foo<'a, T>() -> T { 40fn foo<'a, T>() -> T {
@@ -43,6 +51,12 @@ def_fn! {
43 } 51 }
44} 52}
45 53
54macro_rules! noop {
55 ($expr:expr) => {
56 $expr
57 }
58}
59
46// comment 60// comment
47fn main() { 61fn main() {
48 println!("Hello, {}!", 92); 62 println!("Hello, {}!", 92);
@@ -61,10 +75,14 @@ fn main() {
61 // Do nothing 75 // Do nothing
62 } 76 }
63 77
78 noop!(noop!(1));
79
64 let mut x = 42; 80 let mut x = 42;
65 let y = &mut x; 81 let y = &mut x;
66 let z = &y; 82 let z = &y;
67 83
84 let Foo { x: z, y } = Foo { x: z, y };
85
68 y; 86 y;
69} 87}
70 88
@@ -84,17 +102,14 @@ impl<T> Option<T> {
84} 102}
85"# 103"#
86 .trim(), 104 .trim(),
105 expect_file!["crates/ra_ide/test_data/highlighting.html"],
106 false,
87 ); 107 );
88 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlighting.html");
89 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
90 let expected_html = &read_text(&dst_file);
91 fs::write(dst_file, &actual_html).unwrap();
92 assert_eq_text!(expected_html, actual_html);
93} 108}
94 109
95#[test] 110#[test]
96fn test_rainbow_highlighting() { 111fn test_rainbow_highlighting() {
97 let (analysis, file_id) = single_file( 112 check_highlighting(
98 r#" 113 r#"
99fn main() { 114fn main() {
100 let hello = "hello"; 115 let hello = "hello";
@@ -110,12 +125,9 @@ fn bar() {
110} 125}
111"# 126"#
112 .trim(), 127 .trim(),
128 expect_file!["crates/ra_ide/test_data/rainbow_highlighting.html"],
129 true,
113 ); 130 );
114 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/rainbow_highlighting.html");
115 let actual_html = &analysis.highlight_as_html(file_id, true).unwrap();
116 let expected_html = &read_text(&dst_file);
117 fs::write(dst_file, &actual_html).unwrap();
118 assert_eq_text!(expected_html, actual_html);
119} 131}
120 132
121#[test] 133#[test]
@@ -123,12 +135,10 @@ fn accidentally_quadratic() {
123 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic"); 135 let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
124 let src = fs::read_to_string(file).unwrap(); 136 let src = fs::read_to_string(file).unwrap();
125 137
126 let mut mock = MockAnalysis::new(); 138 let (analysis, file_id) = single_file(&src);
127 let file_id = mock.add_file("/main.rs", &src);
128 let host = mock.analysis_host();
129 139
130 // let t = std::time::Instant::now(); 140 // let t = std::time::Instant::now();
131 let _ = host.analysis().highlight(file_id).unwrap(); 141 let _ = analysis.highlight(file_id).unwrap();
132 // eprintln!("elapsed: {:?}", t.elapsed()); 142 // eprintln!("elapsed: {:?}", t.elapsed());
133} 143}
134 144
@@ -136,16 +146,17 @@ fn accidentally_quadratic() {
136fn test_ranges() { 146fn test_ranges() {
137 let (analysis, file_id) = single_file( 147 let (analysis, file_id) = single_file(
138 r#" 148 r#"
139 #[derive(Clone, Debug)] 149#[derive(Clone, Debug)]
140 struct Foo { 150struct Foo {
141 pub x: i32, 151 pub x: i32,
142 pub y: i32, 152 pub y: i32,
143 }"#, 153}
154"#,
144 ); 155 );
145 156
146 // The "x" 157 // The "x"
147 let highlights = &analysis 158 let highlights = &analysis
148 .highlight_range(FileRange { file_id, range: TextRange::at(82.into(), 1.into()) }) 159 .highlight_range(FileRange { file_id, range: TextRange::at(45.into(), 1.into()) })
149 .unwrap(); 160 .unwrap();
150 161
151 assert_eq!(&highlights[0].highlight.to_string(), "field.declaration"); 162 assert_eq!(&highlights[0].highlight.to_string(), "field.declaration");
@@ -153,7 +164,7 @@ fn test_ranges() {
153 164
154#[test] 165#[test]
155fn test_flattening() { 166fn test_flattening() {
156 let (analysis, file_id) = single_file( 167 check_highlighting(
157 r##" 168 r##"
158fn fixture(ra_fixture: &str) {} 169fn fixture(ra_fixture: &str) {}
159 170
@@ -167,13 +178,9 @@ fn main() {
167 ); 178 );
168}"## 179}"##
169 .trim(), 180 .trim(),
181 expect_file!["crates/ra_ide/test_data/highlight_injection.html"],
182 false,
170 ); 183 );
171
172 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html");
173 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap();
174 let expected_html = &read_text(&dst_file);
175 fs::write(dst_file, &actual_html).unwrap();
176 assert_eq_text!(expected_html, actual_html);
177} 184}
178 185
179#[test] 186#[test]
@@ -192,7 +199,7 @@ macro_rules! test {}
192fn test_string_highlighting() { 199fn test_string_highlighting() {
193 // The format string detection is based on macro-expansion, 200 // The format string detection is based on macro-expansion,
194 // thus, we have to copy the macro definition from `std` 201 // thus, we have to copy the macro definition from `std`
195 let (analysis, file_id) = single_file( 202 check_highlighting(
196 r#" 203 r#"
197macro_rules! println { 204macro_rules! println {
198 ($($arg:tt)*) => ({ 205 ($($arg:tt)*) => ({
@@ -218,6 +225,7 @@ fn main() {
218 println!("{argument}", argument = "test"); // => "test" 225 println!("{argument}", argument = "test"); // => "test"
219 println!("{name} {}", 1, name = 2); // => "2 1" 226 println!("{name} {}", 1, name = 2); // => "2 1"
220 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" 227 println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b"
228 println!("{{{}}}", 2); // => "{2}"
221 println!("Hello {:5}!", "x"); 229 println!("Hello {:5}!", "x");
222 println!("Hello {:1$}!", "x", 5); 230 println!("Hello {:1$}!", "x", 5);
223 println!("Hello {1:0$}!", 5, "x"); 231 println!("Hello {1:0$}!", 5, "x");
@@ -245,15 +253,128 @@ fn main() {
245 253
246 println!(r"Hello, {}!", "world"); 254 println!(r"Hello, {}!", "world");
247 255
256 // escape sequences
257 println!("Hello\nWorld");
258 println!("\u{48}\x65\x6C\x6C\x6F World");
259
248 println!("{\x41}", A = 92); 260 println!("{\x41}", A = 92);
249 println!("{ничоси}", ничоси = 92); 261 println!("{ничоси}", ничоси = 92);
250}"# 262}"#
251 .trim(), 263 .trim(),
264 expect_file!["crates/ra_ide/test_data/highlight_strings.html"],
265 false,
266 );
267}
268
269#[test]
270fn test_unsafe_highlighting() {
271 check_highlighting(
272 r#"
273unsafe fn unsafe_fn() {}
274
275struct HasUnsafeFn;
276
277impl HasUnsafeFn {
278 unsafe fn unsafe_method(&self) {}
279}
280
281fn main() {
282 let x = &5 as *const usize;
283 unsafe {
284 unsafe_fn();
285 HasUnsafeFn.unsafe_method();
286 let y = *(x);
287 let z = -x;
288 }
289}
290"#
291 .trim(),
292 expect_file!["crates/ra_ide/test_data/highlight_unsafe.html"],
293 false,
294 );
295}
296
297#[test]
298fn test_highlight_doctest() {
299 check_highlighting(
300 r#"
301/// ```
302/// let _ = "early doctests should not go boom";
303/// ```
304struct Foo {
305 bar: bool,
306}
307
308impl Foo {
309 pub const bar: bool = true;
310
311 /// Constructs a new `Foo`.
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// # #![allow(unused_mut)]
317 /// let mut foo: Foo = Foo::new();
318 /// ```
319 pub const fn new() -> Foo {
320 Foo { bar: true }
321 }
322
323 /// `bar` method on `Foo`.
324 ///
325 /// # Examples
326 ///
327 /// ```
328 /// use x::y;
329 ///
330 /// let foo = Foo::new();
331 ///
332 /// // calls bar on foo
333 /// assert!(foo.bar());
334 ///
335 /// let bar = foo.bar || Foo::bar;
336 ///
337 /// /* multi-line
338 /// comment */
339 ///
340 /// let multi_line_string = "Foo
341 /// bar
342 /// ";
343 ///
344 /// ```
345 ///
346 /// ```rust,no_run
347 /// let foobar = Foo::new().bar();
348 /// ```
349 ///
350 /// ```sh
351 /// echo 1
352 /// ```
353 pub fn foo(&self) -> bool {
354 true
355 }
356}
357
358/// ```
359/// noop!(1);
360/// ```
361macro_rules! noop {
362 ($expr:expr) => {
363 $expr
364 }
365}
366"#
367 .trim(),
368 expect_file!["crates/ra_ide/test_data/highlight_doctest.html"],
369 false,
252 ); 370 );
371}
253 372
254 let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_strings.html"); 373/// Highlights the code given by the `ra_fixture` argument, renders the
255 let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); 374/// result as HTML, and compares it with the HTML file given as `snapshot`.
256 let expected_html = &read_text(&dst_file); 375/// Note that the `snapshot` file is overwritten by the rendered HTML.
257 fs::write(dst_file, &actual_html).unwrap(); 376fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) {
258 assert_eq_text!(expected_html, actual_html); 377 let (analysis, file_id) = single_file(ra_fixture);
378 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
379 expect.assert_eq(actual_html)
259} 380}