]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide/src/syntax_highlighting/tests.rs
Inspect markdown code fences to determine whether to apply syntax highlighting
[rust.git] / crates / ra_ide / src / syntax_highlighting / tests.rs
1 use std::fs;
2
3 use test_utils::{assert_eq_text, project_dir, read_text};
4
5 use crate::{
6     mock_analysis::{single_file, MockAnalysis},
7     FileRange, TextRange,
8 };
9
10 #[test]
11 fn test_highlighting() {
12     check_highlighting(
13         r#"
14 #[derive(Clone, Debug)]
15 struct Foo {
16     pub x: i32,
17     pub y: i32,
18 }
19
20 trait Bar {
21     fn bar(&self) -> i32;
22 }
23
24 impl Bar for Foo {
25     fn bar(&self) -> i32 {
26         self.x
27     }
28 }
29
30 static mut STATIC_MUT: i32 = 0;
31
32 fn foo<'a, T>() -> T {
33     foo::<'a, i32>()
34 }
35
36 macro_rules! def_fn {
37     ($($tt:tt)*) => {$($tt)*}
38 }
39
40 def_fn! {
41     fn bar() -> u32 {
42         100
43     }
44 }
45
46 macro_rules! noop {
47     ($expr:expr) => {
48         $expr
49     }
50 }
51
52 // comment
53 fn main() {
54     println!("Hello, {}!", 92);
55
56     let mut vec = Vec::new();
57     if true {
58         let x = 92;
59         vec.push(Foo { x, y: 1 });
60     }
61     unsafe {
62         vec.set_len(0);
63         STATIC_MUT = 1;
64     }
65
66     for e in vec {
67         // Do nothing
68     }
69
70     noop!(noop!(1));
71
72     let mut x = 42;
73     let y = &mut x;
74     let z = &y;
75
76     let Foo { x: z, y } = Foo { x: z, y };
77
78     y;
79 }
80
81 enum Option<T> {
82     Some(T),
83     None,
84 }
85 use Option::*;
86
87 impl<T> Option<T> {
88     fn and<U>(self, other: Option<U>) -> Option<(T, U)> {
89         match other {
90             None => unimplemented!(),
91             Nope => Nope,
92         }
93     }
94 }
95 "#
96         .trim(),
97         "crates/ra_ide/src/snapshots/highlighting.html",
98         false,
99     );
100 }
101
102 #[test]
103 fn test_rainbow_highlighting() {
104     check_highlighting(
105         r#"
106 fn main() {
107     let hello = "hello";
108     let x = hello.to_string();
109     let y = hello.to_string();
110
111     let x = "other color please!";
112     let y = x.to_string();
113 }
114
115 fn bar() {
116     let mut hello = "hello";
117 }
118 "#
119         .trim(),
120         "crates/ra_ide/src/snapshots/rainbow_highlighting.html",
121         true,
122     );
123 }
124
125 #[test]
126 fn accidentally_quadratic() {
127     let file = project_dir().join("crates/ra_syntax/test_data/accidentally_quadratic");
128     let src = fs::read_to_string(file).unwrap();
129
130     let mut mock = MockAnalysis::new();
131     let file_id = mock.add_file("/main.rs", &src);
132     let host = mock.analysis_host();
133
134     // let t = std::time::Instant::now();
135     let _ = host.analysis().highlight(file_id).unwrap();
136     // eprintln!("elapsed: {:?}", t.elapsed());
137 }
138
139 #[test]
140 fn test_ranges() {
141     let (analysis, file_id) = single_file(
142         r#"
143             #[derive(Clone, Debug)]
144             struct Foo {
145                 pub x: i32,
146                 pub y: i32,
147             }"#,
148     );
149
150     // The "x"
151     let highlights = &analysis
152         .highlight_range(FileRange { file_id, range: TextRange::at(82.into(), 1.into()) })
153         .unwrap();
154
155     assert_eq!(&highlights[0].highlight.to_string(), "field.declaration");
156 }
157
158 #[test]
159 fn test_flattening() {
160     check_highlighting(
161         r##"
162 fn fixture(ra_fixture: &str) {}
163
164 fn main() {
165     fixture(r#"
166         trait Foo {
167             fn foo() {
168                 println!("2 + 2 = {}", 4);
169             }
170         }"#
171     );
172 }"##
173         .trim(),
174         "crates/ra_ide/src/snapshots/highlight_injection.html",
175         false,
176     );
177 }
178
179 #[test]
180 fn ranges_sorted() {
181     let (analysis, file_id) = single_file(
182         r#"
183 #[foo(bar = "bar")]
184 macro_rules! test {}
185 }"#
186         .trim(),
187     );
188     let _ = analysis.highlight(file_id).unwrap();
189 }
190
191 #[test]
192 fn test_string_highlighting() {
193     // The format string detection is based on macro-expansion,
194     // thus, we have to copy the macro definition from `std`
195     check_highlighting(
196         r#"
197 macro_rules! println {
198     ($($arg:tt)*) => ({
199         $crate::io::_print($crate::format_args_nl!($($arg)*));
200     })
201 }
202 #[rustc_builtin_macro]
203 macro_rules! format_args_nl {
204     ($fmt:expr) => {{ /* compiler built-in */ }};
205     ($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
206 }
207
208 fn main() {
209     // from https://doc.rust-lang.org/std/fmt/index.html
210     println!("Hello");                 // => "Hello"
211     println!("Hello, {}!", "world");   // => "Hello, world!"
212     println!("The number is {}", 1);   // => "The number is 1"
213     println!("{:?}", (3, 4));          // => "(3, 4)"
214     println!("{value}", value=4);      // => "4"
215     println!("{} {}", 1, 2);           // => "1 2"
216     println!("{:04}", 42);             // => "0042" with leading zerosV
217     println!("{1} {} {0} {}", 1, 2);   // => "2 1 1 2"
218     println!("{argument}", argument = "test");   // => "test"
219     println!("{name} {}", 1, name = 2);          // => "2 1"
220     println!("{a} {c} {b}", a="a", b='b', c=3);  // => "a 3 b"
221     println!("{{{}}}", 2);                       // => "{2}"
222     println!("Hello {:5}!", "x");
223     println!("Hello {:1$}!", "x", 5);
224     println!("Hello {1:0$}!", 5, "x");
225     println!("Hello {:width$}!", "x", width = 5);
226     println!("Hello {:<5}!", "x");
227     println!("Hello {:-<5}!", "x");
228     println!("Hello {:^5}!", "x");
229     println!("Hello {:>5}!", "x");
230     println!("Hello {:+}!", 5);
231     println!("{:#x}!", 27);
232     println!("Hello {:05}!", 5);
233     println!("Hello {:05}!", -5);
234     println!("{:#010x}!", 27);
235     println!("Hello {0} is {1:.5}", "x", 0.01);
236     println!("Hello {1} is {2:.0$}", 5, "x", 0.01);
237     println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
238     println!("Hello {} is {:.*}",    "x", 5, 0.01);
239     println!("Hello {} is {2:.*}",   "x", 5, 0.01);
240     println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
241     println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
242     println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
243     println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
244     println!("Hello {{}}");
245     println!("{{ Hello");
246
247     println!(r"Hello, {}!", "world");
248
249     println!("{\x41}", A = 92);
250     println!("{ничоси}", ничоси = 92);
251 }"#
252         .trim(),
253         "crates/ra_ide/src/snapshots/highlight_strings.html",
254         false,
255     );
256 }
257
258 #[test]
259 fn test_unsafe_highlighting() {
260     check_highlighting(
261         r#"
262 unsafe fn unsafe_fn() {}
263
264 struct HasUnsafeFn;
265
266 impl HasUnsafeFn {
267     unsafe fn unsafe_method(&self) {}
268 }
269
270 fn main() {
271     let x = &5 as *const usize;
272     unsafe {
273         unsafe_fn();
274         HasUnsafeFn.unsafe_method();
275         let y = *(x);
276         let z = -x;
277     }
278 }
279 "#
280         .trim(),
281         "crates/ra_ide/src/snapshots/highlight_unsafe.html",
282         false,
283     );
284 }
285
286 #[test]
287 fn test_highlight_doctest() {
288     check_highlighting(
289         r#"
290 struct Foo {
291     bar: bool,
292 }
293
294 impl Foo {
295     pub const bar: bool = true;
296
297     /// Constructs a new `Foo`.
298     ///
299     /// # Examples
300     ///
301     /// ```
302     /// # #![allow(unused_mut)]
303     /// let mut foo: Foo = Foo::new();
304     /// ```
305     pub const fn new() -> Foo {
306         Foo { bar: true }
307     }
308
309     /// `bar` method on `Foo`.
310     ///
311     /// # Examples
312     ///
313     /// ```
314     /// use x::y;
315     ///
316     /// let foo = Foo::new();
317     ///
318     /// // calls bar on foo
319     /// assert!(foo.bar());
320     ///
321     /// let bar = foo.bar || Foo::bar;
322     ///
323     /// /* multi-line
324     ///        comment */
325     ///
326     /// let multi_line_string = "Foo
327     ///   bar
328     ///          ";
329     ///
330     /// ```
331     ///
332     /// ```rust,no_run
333     /// let foobar = Foo::new().bar();
334     /// ```
335     ///
336     /// ```sh
337     /// echo 1
338     /// ```
339     pub fn foo(&self) -> bool {
340         true
341     }
342 }
343 "#
344         .trim(),
345         "crates/ra_ide/src/snapshots/highlight_doctest.html",
346         false,
347     );
348 }
349
350 /// Highlights the code given by the `ra_fixture` argument, renders the
351 /// result as HTML, and compares it with the HTML file given as `snapshot`.
352 /// Note that the `snapshot` file is overwritten by the rendered HTML.
353 fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
354     let (analysis, file_id) = single_file(ra_fixture);
355     let dst_file = project_dir().join(snapshot);
356     let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
357     let expected_html = &read_text(&dst_file);
358     fs::write(dst_file, &actual_html).unwrap();
359     assert_eq_text!(expected_html, actual_html);
360 }