From c6e905a79f7ba083b3f97728aa3a74fb0e03661b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 25 May 2019 13:42:34 +0300 Subject: Colorize Rust code as HTML --- crates/ra_ide_api/src/syntax_highlighting.rs | 87 ++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 6 deletions(-) (limited to 'crates/ra_ide_api/src/syntax_highlighting.rs') 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 String { + let source_file = db.parse(file_id); + + let mut ranges = highlight(db, file_id); + ranges.sort_by_key(|it| it.range.start()); + // quick non-optimal heuristic to intersect token ranges and highlighted ranges + let mut frontier = 0; + let mut could_intersect: Vec<&HighlightedRange> = Vec::new(); + + let mut buf = String::new(); + buf.push_str(&STYLE); + buf.push_str("
");
+    let tokens = source_file.syntax().descendants_with_tokens().filter_map(|it| it.as_token());
+    for token in tokens {
+        could_intersect.retain(|it| token.range().start() <= it.range.end());
+        while let Some(r) = ranges.get(frontier) {
+            if r.range.start() <= token.range().end() {
+                could_intersect.push(r);
+                frontier += 1;
+            } else {
+                break;
+            }
+        }
+        let text = html_escape(&token.text());
+        let classes = could_intersect
+            .iter()
+            .filter(|it| token.range().is_subrange(&it.range))
+            .map(|it| it.tag)
+            .collect::>();
+        if classes.is_empty() {
+            buf.push_str(&text);
+        } else {
+            let classes = classes.join(" ");
+            buf.push_str(&format!("{}", classes, text));
+        }
+    }
+    buf.push_str("
"); + buf +} + +//FIXME: like, real html escaping +fn html_escape(text: &str) -> String { + text.replace("<", "<").replace(">", ">") +} + +const STYLE: &str = " + +"; + #[cfg(test)] mod tests { - use insta::assert_debug_snapshot_matches; - + use test_utils::{project_dir, read_text, assert_eq_text}; use crate::mock_analysis::single_file; #[test] @@ -135,15 +204,21 @@ fn foo() -> T { } // comment -fn main() {} +fn main() { println!("Hello, {}!", 92); let mut vec = Vec::new(); - vec.push(Foo { x: 0, y: 1 }); + if true { + vec.push(Foo { x: 0, y: 1 }); + } unsafe { vec.set_len(0); } +} "#, ); - let result = analysis.highlight(file_id); - assert_debug_snapshot_matches!("highlighting", result); + let dst_file = project_dir().join("crates/ra_ide_api/src/snapshots/highlighting.html"); + let actual_html = &analysis.highlight_as_html(file_id).unwrap(); + let expected_html = &read_text(&dst_file); + // std::fs::write(dst_file, &actual_html).unwrap(); + assert_eq_text!(expected_html, actual_html); } } -- cgit v1.2.3