aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-05-25 11:48:47 +0100
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-05-25 11:48:47 +0100
commit91bd783273477d5709ea4bb891355e9aa9a7cdfe (patch)
tree606a17d688e931e35fe45eefd6c13c0baf7bd5bb /crates
parent9800699bab04e97996f0aebec528714165a2619b (diff)
parentc6e905a79f7ba083b3f97728aa3a74fb0e03661b (diff)
Merge #1327
1327: Colorize Rust code as HTML r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_cli/src/main.rs8
-rw-r--r--crates/ra_ide_api/src/lib.rs5
-rw-r--r--crates/ra_ide_api/src/snapshots/highlighting.html45
-rw-r--r--crates/ra_ide_api/src/snapshots/tests__highlighting.snap146
-rw-r--r--crates/ra_ide_api/src/syntax_highlighting.rs87
5 files changed, 138 insertions, 153 deletions
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs
index f11d0e6bd..93aba4c70 100644
--- a/crates/ra_cli/src/main.rs
+++ b/crates/ra_cli/src/main.rs
@@ -3,7 +3,7 @@ mod analysis_stats;
3use std::io::Read; 3use std::io::Read;
4 4
5use clap::{App, Arg, SubCommand}; 5use clap::{App, Arg, SubCommand};
6use ra_ide_api::file_structure; 6use ra_ide_api::{file_structure, Analysis};
7use ra_syntax::{SourceFile, TreeArc, AstNode}; 7use ra_syntax::{SourceFile, TreeArc, AstNode};
8use flexi_logger::Logger; 8use flexi_logger::Logger;
9use ra_prof::profile; 9use ra_prof::profile;
@@ -16,6 +16,7 @@ fn main() -> Result<()> {
16 .setting(clap::AppSettings::SubcommandRequiredElseHelp) 16 .setting(clap::AppSettings::SubcommandRequiredElseHelp)
17 .subcommand(SubCommand::with_name("parse").arg(Arg::with_name("no-dump").long("--no-dump"))) 17 .subcommand(SubCommand::with_name("parse").arg(Arg::with_name("no-dump").long("--no-dump")))
18 .subcommand(SubCommand::with_name("symbols")) 18 .subcommand(SubCommand::with_name("symbols"))
19 .subcommand(SubCommand::with_name("highlight"))
19 .subcommand( 20 .subcommand(
20 SubCommand::with_name("analysis-stats") 21 SubCommand::with_name("analysis-stats")
21 .arg(Arg::with_name("verbose").short("v").long("verbose")) 22 .arg(Arg::with_name("verbose").short("v").long("verbose"))
@@ -38,6 +39,11 @@ fn main() -> Result<()> {
38 println!("{:?}", s); 39 println!("{:?}", s);
39 } 40 }
40 } 41 }
42 ("highlight", _) => {
43 let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
44 let html = analysis.highlight_as_html(file_id).unwrap();
45 println!("{}", html);
46 }
41 ("analysis-stats", Some(matches)) => { 47 ("analysis-stats", Some(matches)) => {
42 let verbose = matches.is_present("verbose"); 48 let verbose = matches.is_present("verbose");
43 let path = matches.value_of("path").unwrap_or(""); 49 let path = matches.value_of("path").unwrap_or("");
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs
index f78348f74..d3456d5b2 100644
--- a/crates/ra_ide_api/src/lib.rs
+++ b/crates/ra_ide_api/src/lib.rs
@@ -462,6 +462,11 @@ impl Analysis {
462 self.with_db(|db| syntax_highlighting::highlight(db, file_id)) 462 self.with_db(|db| syntax_highlighting::highlight(db, file_id))
463 } 463 }
464 464
465 /// Computes syntax highlighting for the given file.
466 pub fn highlight_as_html(&self, file_id: FileId) -> Cancelable<String> {
467 self.with_db(|db| syntax_highlighting::highlight_as_html(db, file_id))
468 }
469
465 /// Computes completions at the given position. 470 /// Computes completions at the given position.
466 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 471 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
467 self.with_db(|db| completion::completions(db, position).map(Into::into)) 472 self.with_db(|db| completion::completions(db, position).map(Into::into))
diff --git a/crates/ra_ide_api/src/snapshots/highlighting.html b/crates/ra_ide_api/src/snapshots/highlighting.html
new file mode 100644
index 000000000..bfc0a67b1
--- /dev/null
+++ b/crates/ra_ide_api/src/snapshots/highlighting.html
@@ -0,0 +1,45 @@
1
2<style>
3pre {
4 color: #DCDCCC;
5 background-color: #3F3F3F;
6 font-size: 22px;
7}
8
9.comment { color: #7F9F7F; }
10.string { color: #CC9393; }
11.function { color: #93E0E3; }
12.parameter { color: #94BFF3; }
13.builtin { color: #DD6718; }
14.text { color: #DCDCCC; }
15.attribute { color: #BFEBBF; }
16.literal { color: #DFAF8F; }
17.macro { color: #DFAF8F; }
18
19.keyword { color: #F0DFAF; }
20.keyword\.unsafe { color: #F0DFAF; font-weight: bold; }
21.keyword\.control { color: #DC8CC3; }
22
23</style>
24<pre><code>
25<span class="attribute">#</span><span class="attribute">[</span><span class="attribute">derive</span><span class="attribute">(</span><span class="attribute">Clone</span><span class="attribute">,</span><span class="attribute"> </span><span class="attribute">Debug</span><span class="attribute">)</span><span class="attribute">]</span>
26<span class="keyword">struct</span> <span class="function">Foo</span> {
27 <span class="keyword">pub</span> <span class="function">x</span>: <span class="text">i32</span>,
28 <span class="keyword">pub</span> <span class="function">y</span>: <span class="text">i32</span>,
29}
30
31<span class="keyword">fn</span> <span class="function">foo</span>&lt;<span class="type function">T</span>&gt;() -&gt; <span class="type">T</span> {
32 <span class="macro">unimplemented</span><span class="macro">!</span>();
33}
34
35<span class="comment">// comment</span>
36<span class="keyword">fn</span> <span class="function">main</span>() {
37 <span class="macro">println</span><span class="macro">!</span>(<span class="string">"Hello, {}!"</span>, <span class="literal">92</span>);
38
39 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="function">vec</span> = <span class="text">Vec</span>::<span class="text">new</span>();
40 <span class="keyword.control">if</span> <span class="keyword">true</span> {
41 <span class="text">vec</span>.<span class="text">push</span>(<span class="type">Foo</span> { <span class="field">x</span>: <span class="literal">0</span>, <span class="field">y</span>: <span class="literal">1</span> });
42 }
43 <span class="keyword.unsafe">unsafe</span> { <span class="text">vec</span>.<span class="text">set_len</span>(<span class="literal">0</span>); }
44}
45</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap b/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
deleted file mode 100644
index 9c60aed2a..000000000
--- a/crates/ra_ide_api/src/snapshots/tests__highlighting.snap
+++ /dev/null
@@ -1,146 +0,0 @@
1---
2created: "2019-05-23T22:23:35.242742395Z"
3creator: [email protected]
4source: crates/ra_ide_api/src/syntax_highlighting.rs
5expression: result
6---
7Ok(
8 [
9 HighlightedRange {
10 range: [1; 24),
11 tag: "attribute",
12 },
13 HighlightedRange {
14 range: [25; 31),
15 tag: "keyword",
16 },
17 HighlightedRange {
18 range: [32; 35),
19 tag: "function",
20 },
21 HighlightedRange {
22 range: [42; 45),
23 tag: "keyword",
24 },
25 HighlightedRange {
26 range: [46; 47),
27 tag: "function",
28 },
29 HighlightedRange {
30 range: [49; 52),
31 tag: "text",
32 },
33 HighlightedRange {
34 range: [58; 61),
35 tag: "keyword",
36 },
37 HighlightedRange {
38 range: [62; 63),
39 tag: "function",
40 },
41 HighlightedRange {
42 range: [65; 68),
43 tag: "text",
44 },
45 HighlightedRange {
46 range: [73; 75),
47 tag: "keyword",
48 },
49 HighlightedRange {
50 range: [76; 79),
51 tag: "function",
52 },
53 HighlightedRange {
54 range: [80; 81),
55 tag: "type",
56 },
57 HighlightedRange {
58 range: [80; 81),
59 tag: "function",
60 },
61 HighlightedRange {
62 range: [88; 89),
63 tag: "type",
64 },
65 HighlightedRange {
66 range: [96; 110),
67 tag: "macro",
68 },
69 HighlightedRange {
70 range: [117; 127),
71 tag: "comment",
72 },
73 HighlightedRange {
74 range: [128; 130),
75 tag: "keyword",
76 },
77 HighlightedRange {
78 range: [131; 135),
79 tag: "function",
80 },
81 HighlightedRange {
82 range: [145; 153),
83 tag: "macro",
84 },
85 HighlightedRange {
86 range: [154; 166),
87 tag: "string",
88 },
89 HighlightedRange {
90 range: [168; 170),
91 tag: "literal",
92 },
93 HighlightedRange {
94 range: [178; 181),
95 tag: "keyword",
96 },
97 HighlightedRange {
98 range: [182; 185),
99 tag: "keyword",
100 },
101 HighlightedRange {
102 range: [186; 189),
103 tag: "macro",
104 },
105 HighlightedRange {
106 range: [197; 200),
107 tag: "macro",
108 },
109 HighlightedRange {
110 range: [192; 195),
111 tag: "text",
112 },
113 HighlightedRange {
114 range: [208; 211),
115 tag: "macro",
116 },
117 HighlightedRange {
118 range: [212; 216),
119 tag: "macro",
120 },
121 HighlightedRange {
122 range: [226; 227),
123 tag: "literal",
124 },
125 HighlightedRange {
126 range: [232; 233),
127 tag: "literal",
128 },
129 HighlightedRange {
130 range: [242; 248),
131 tag: "keyword.unsafe",
132 },
133 HighlightedRange {
134 range: [251; 254),
135 tag: "text",
136 },
137 HighlightedRange {
138 range: [255; 262),
139 tag: "text",
140 },
141 HighlightedRange {
142 range: [263; 264),
143 tag: "literal",
144 },
145 ],
146)
diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs
index 7bba7a550..87e053364 100644
--- a/crates/ra_ide_api/src/syntax_highlighting.rs
+++ b/crates/ra_ide_api/src/syntax_highlighting.rs
@@ -114,10 +114,79 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
114 res 114 res
115} 115}
116 116
117pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId) -> String {
118 let source_file = db.parse(file_id);
119
120 let mut ranges = highlight(db, file_id);
121 ranges.sort_by_key(|it| it.range.start());
122 // quick non-optimal heuristic to intersect token ranges and highlighted ranges
123 let mut frontier = 0;
124 let mut could_intersect: Vec<&HighlightedRange> = Vec::new();
125
126 let mut buf = String::new();
127 buf.push_str(&STYLE);
128 buf.push_str("<pre><code>");
129 let tokens = source_file.syntax().descendants_with_tokens().filter_map(|it| it.as_token());
130 for token in tokens {
131 could_intersect.retain(|it| token.range().start() <= it.range.end());
132 while let Some(r) = ranges.get(frontier) {
133 if r.range.start() <= token.range().end() {
134 could_intersect.push(r);
135 frontier += 1;
136 } else {
137 break;
138 }
139 }
140 let text = html_escape(&token.text());
141 let classes = could_intersect
142 .iter()
143 .filter(|it| token.range().is_subrange(&it.range))
144 .map(|it| it.tag)
145 .collect::<Vec<_>>();
146 if classes.is_empty() {
147 buf.push_str(&text);
148 } else {
149 let classes = classes.join(" ");
150 buf.push_str(&format!("<span class=\"{}\">{}</span>", classes, text));
151 }
152 }
153 buf.push_str("</code></pre>");
154 buf
155}
156
157//FIXME: like, real html escaping
158fn html_escape(text: &str) -> String {
159 text.replace("<", "&lt;").replace(">", "&gt;")
160}
161
162const STYLE: &str = "
163<style>
164pre {
165 color: #DCDCCC;
166 background-color: #3F3F3F;
167 font-size: 22px;
168}
169
170.comment { color: #7F9F7F; }
171.string { color: #CC9393; }
172.function { color: #93E0E3; }
173.parameter { color: #94BFF3; }
174.builtin { color: #DD6718; }
175.text { color: #DCDCCC; }
176.attribute { color: #BFEBBF; }
177.literal { color: #DFAF8F; }
178.macro { color: #DFAF8F; }
179
180.keyword { color: #F0DFAF; }
181.keyword\\.unsafe { color: #F0DFAF; font-weight: bold; }
182.keyword\\.control { color: #DC8CC3; }
183
184</style>
185";
186
117#[cfg(test)] 187#[cfg(test)]
118mod tests { 188mod tests {
119 use insta::assert_debug_snapshot_matches; 189 use test_utils::{project_dir, read_text, assert_eq_text};
120
121 use crate::mock_analysis::single_file; 190 use crate::mock_analysis::single_file;
122 191
123 #[test] 192 #[test]
@@ -135,15 +204,21 @@ fn foo<T>() -> T {
135} 204}
136 205
137// comment 206// comment
138fn main() {} 207fn main() {
139 println!("Hello, {}!", 92); 208 println!("Hello, {}!", 92);
140 209
141 let mut vec = Vec::new(); 210 let mut vec = Vec::new();
142 vec.push(Foo { x: 0, y: 1 }); 211 if true {
212 vec.push(Foo { x: 0, y: 1 });
213 }
143 unsafe { vec.set_len(0); } 214 unsafe { vec.set_len(0); }
215}
144"#, 216"#,
145 ); 217 );
146 let result = analysis.highlight(file_id); 218 let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html");
147 assert_debug_snapshot_matches!("highlighting", result); 219 let actual_html = &analysis.highlight_as_html(file_id).unwrap();
220 let expected_html = &read_text(&dst_file);
221 // std::fs::write(dst_file, &actual_html).unwrap();
222 assert_eq_text!(expected_html, actual_html);
148 } 223 }
149} 224}