diff options
Diffstat (limited to 'crates/ra_ide/src/syntax_highlighting')
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting/html.rs | 66 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting/tests.rs | 33 |
2 files changed, 46 insertions, 53 deletions
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs index e13766c9d..4496529a1 100644 --- a/crates/ra_ide/src/syntax_highlighting/html.rs +++ b/crates/ra_ide/src/syntax_highlighting/html.rs | |||
@@ -1,9 +1,9 @@ | |||
1 | //! Renders a bit of code as HTML. | 1 | //! Renders a bit of code as HTML. |
2 | 2 | ||
3 | use ra_db::SourceDatabase; | 3 | use ra_db::SourceDatabase; |
4 | use ra_syntax::AstNode; | 4 | use ra_syntax::{AstNode, TextUnit}; |
5 | 5 | ||
6 | use crate::{FileId, HighlightedRange, RootDatabase}; | 6 | use crate::{FileId, RootDatabase}; |
7 | 7 | ||
8 | use super::highlight; | 8 | use super::highlight; |
9 | 9 | ||
@@ -21,51 +21,35 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo | |||
21 | ) | 21 | ) |
22 | } | 22 | } |
23 | 23 | ||
24 | let mut ranges = highlight(db, file_id, None); | 24 | let ranges = highlight(db, file_id, None); |
25 | ranges.sort_by_key(|it| it.range.start()); | 25 | let text = parse.tree().syntax().to_string(); |
26 | // quick non-optimal heuristic to intersect token ranges and highlighted ranges | 26 | let mut prev_pos = TextUnit::from(0); |
27 | let mut frontier = 0; | ||
28 | let mut could_intersect: Vec<&HighlightedRange> = Vec::new(); | ||
29 | |||
30 | let mut buf = String::new(); | 27 | let mut buf = String::new(); |
31 | buf.push_str(&STYLE); | 28 | buf.push_str(&STYLE); |
32 | buf.push_str("<pre><code>"); | 29 | buf.push_str("<pre><code>"); |
33 | let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token()); | 30 | for range in &ranges { |
34 | for token in tokens { | 31 | if range.range.start() > prev_pos { |
35 | could_intersect.retain(|it| token.text_range().start() <= it.range.end()); | 32 | let curr = &text[prev_pos.to_usize()..range.range.start().to_usize()]; |
36 | while let Some(r) = ranges.get(frontier) { | 33 | let text = html_escape(curr); |
37 | if r.range.start() <= token.text_range().end() { | ||
38 | could_intersect.push(r); | ||
39 | frontier += 1; | ||
40 | } else { | ||
41 | break; | ||
42 | } | ||
43 | } | ||
44 | let text = html_escape(&token.text()); | ||
45 | let ranges = could_intersect | ||
46 | .iter() | ||
47 | .filter(|it| token.text_range().is_subrange(&it.range)) | ||
48 | .collect::<Vec<_>>(); | ||
49 | if ranges.is_empty() { | ||
50 | buf.push_str(&text); | 34 | buf.push_str(&text); |
51 | } else { | ||
52 | let classes = ranges | ||
53 | .iter() | ||
54 | .map(|it| it.highlight.to_string().replace('.', " ")) | ||
55 | .collect::<Vec<_>>() | ||
56 | .join(" "); | ||
57 | let binding_hash = ranges.first().and_then(|x| x.binding_hash); | ||
58 | let color = match (rainbow, binding_hash) { | ||
59 | (true, Some(hash)) => format!( | ||
60 | " data-binding-hash=\"{}\" style=\"color: {};\"", | ||
61 | hash, | ||
62 | rainbowify(hash) | ||
63 | ), | ||
64 | _ => "".into(), | ||
65 | }; | ||
66 | buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", classes, color, text)); | ||
67 | } | 35 | } |
36 | let curr = &text[range.range.start().to_usize()..range.range.end().to_usize()]; | ||
37 | |||
38 | let class = range.highlight.to_string().replace('.', " "); | ||
39 | let color = match (rainbow, range.binding_hash) { | ||
40 | (true, Some(hash)) => { | ||
41 | format!(" data-binding-hash=\"{}\" style=\"color: {};\"", hash, rainbowify(hash)) | ||
42 | } | ||
43 | _ => "".into(), | ||
44 | }; | ||
45 | buf.push_str(&format!("<span class=\"{}\"{}>{}</span>", class, color, html_escape(curr))); | ||
46 | |||
47 | prev_pos = range.range.end(); | ||
68 | } | 48 | } |
49 | // Add the remaining (non-highlighted) text | ||
50 | let curr = &text[prev_pos.to_usize()..]; | ||
51 | let text = html_escape(curr); | ||
52 | buf.push_str(&text); | ||
69 | buf.push_str("</code></pre>"); | 53 | buf.push_str("</code></pre>"); |
70 | buf | 54 | buf |
71 | } | 55 | } |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 7f442bd0f..110887c2a 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -134,16 +134,25 @@ fn test_ranges() { | |||
134 | 134 | ||
135 | #[test] | 135 | #[test] |
136 | fn test_flattening() { | 136 | fn test_flattening() { |
137 | let (analysis, file_id) = single_file(r#"#[cfg(feature = "foo")]"#); | 137 | let (analysis, file_id) = single_file( |
138 | 138 | r##" | |
139 | let highlights = analysis.highlight(file_id).unwrap(); | 139 | fn fixture(ra_fixture: &str) {} |
140 | 140 | ||
141 | // The source code snippet contains 2 nested highlights: | 141 | fn main() { |
142 | // 1) Attribute spanning the whole string | 142 | fixture(r#" |
143 | // 2) The string "foo" | 143 | trait Foo { |
144 | // The resulting flattening splits the attribute range: | 144 | fn foo() { |
145 | assert_eq!(highlights.len(), 3); | 145 | println!("2 + 2 = {}", 4); |
146 | assert_eq!(&highlights[0].highlight.to_string(), "attribute"); | 146 | } |
147 | assert_eq!(&highlights[1].highlight.to_string(), "string_literal"); | 147 | }"# |
148 | assert_eq!(&highlights[2].highlight.to_string(), "attribute"); | 148 | ); |
149 | }"## | ||
150 | .trim(), | ||
151 | ); | ||
152 | |||
153 | let dst_file = project_dir().join("crates/ra_ide/src/snapshots/highlight_injection.html"); | ||
154 | let actual_html = &analysis.highlight_as_html(file_id, false).unwrap(); | ||
155 | let expected_html = &read_text(&dst_file); | ||
156 | fs::write(dst_file, &actual_html).unwrap(); | ||
157 | assert_eq_text!(expected_html, actual_html); | ||
149 | } | 158 | } |