]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide/src/syntax_highlighting/html.rs
Merge #3363
[rust.git] / crates / ra_ide / src / syntax_highlighting / html.rs
1 //! Renders a bit of code as HTML.
2
3 use ra_db::SourceDatabase;
4 use ra_syntax::AstNode;
5
6 use crate::{FileId, HighlightedRange, RootDatabase};
7
8 use super::highlight;
9
10 pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: bool) -> String {
11     let parse = db.parse(file_id);
12
13     fn rainbowify(seed: u64) -> String {
14         use rand::prelude::*;
15         let mut rng = SmallRng::seed_from_u64(seed);
16         format!(
17             "hsl({h},{s}%,{l}%)",
18             h = rng.gen_range::<u16, _, _>(0, 361),
19             s = rng.gen_range::<u16, _, _>(42, 99),
20             l = rng.gen_range::<u16, _, _>(40, 91),
21         )
22     }
23
24     let mut ranges = highlight(db, file_id, None);
25     ranges.sort_by_key(|it| it.range.start());
26     // quick non-optimal heuristic to intersect token ranges and highlighted ranges
27     let mut frontier = 0;
28     let mut could_intersect: Vec<&HighlightedRange> = Vec::new();
29
30     let mut buf = String::new();
31     buf.push_str(&STYLE);
32     buf.push_str("<pre><code>");
33     let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.into_token());
34     for token in tokens {
35         could_intersect.retain(|it| token.text_range().start() <= it.range.end());
36         while let Some(r) = ranges.get(frontier) {
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);
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         }
68     }
69     buf.push_str("</code></pre>");
70     buf
71 }
72
73 //FIXME: like, real html escaping
74 fn html_escape(text: &str) -> String {
75     text.replace("<", "&lt;").replace(">", "&gt;")
76 }
77
78 const STYLE: &str = "
79 <style>
80 body                { margin: 0; }
81 pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
82
83
84 .comment            { color: #7F9F7F; }
85 .struct, .enum      { color: #7CB8BB; }
86 .enum_variant       { color: #BDE0F3; }
87 .string_literal     { color: #CC9393; }
88 .field              { color: #94BFF3; }
89 .function           { color: #93E0E3; }
90 .parameter          { color: #94BFF3; }
91 .text               { color: #DCDCCC; }
92 .type               { color: #7CB8BB; }
93 .builtin_type       { color: #8CD0D3; }
94 .type_param         { color: #DFAF8F; }
95 .attribute          { color: #94BFF3; }
96 .numeric_literal    { color: #BFEBBF; }
97 .macro              { color: #94BFF3; }
98 .module             { color: #AFD8AF; }
99 .variable           { color: #DCDCCC; }
100 .mutable            { text-decoration: underline; }
101
102 .keyword            { color: #F0DFAF; font-weight: bold; }
103 .keyword.unsafe     { color: #BC8383; font-weight: bold; }
104 .control            { font-style: italic; }
105 </style>
106 ";