1pub use errors::{Error, ErrorType, Result};
2pub mod errors;
3pub mod naive;
4use std::io::{BufReader, Cursor};
5
6use iocore::Path;
7pub use naive::{
8 delimiter_wrappers, format_code_naive, format_ident, format_item, format_literal,
9 format_token_stream, format_token_tree, indent_strings, is_capitalized,
10};
11use proc_macro2::{TokenStream, TokenTree};
12use quote::ToTokens;
13use syn::Item;
14use syntect::easy::HighlightLines;
15use syntect::highlighting::{Style, Theme, ThemeSet};
16use syntect::parsing::SyntaxSet;
17use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
18
19pub fn highlight_code(stream: &TokenStream) -> Result<String> {
20 match format_code_rustfmt(stream.to_string()) {
21 Ok(code) => Ok(try_result!(highlight(code, "rs"))),
22 Err(_) => Ok(try_result!(highlight(format_code_string(stream), "rs"))),
23 }
24}
25
26pub fn highlight_to_tokens<T: ToTokens>(stream: T) -> Result<String> {
27 Ok(try_result!(highlight_token_stream(&stream.to_token_stream())))
28}
29pub fn highlight_token_stream(stream: &TokenStream) -> Result<String> {
30 Ok(try_result!(highlight(format!("{:#?}", &stream), "rs")))
31}
32pub fn highlight_token_tree(tree: &TokenTree) -> Result<String> {
33 Ok(try_result!(highlight(format!("{:#?}", tree), "rs")))
34}
35
36pub fn highlight(s: String, lang: &str) -> Result<String> {
37 let ps = SyntaxSet::load_defaults_newlines();
38
39 let syntax = match ps.find_syntax_by_extension(lang) {
40 Some(syntax) => syntax,
41 None =>
42 return Err(Error::new(
43 format!("syntax not found for language {:#?}", lang),
44 ErrorType::SyntectError,
45 )),
46 };
47 let theme = try_result!(theme());
48 let mut h = HighlightLines::new(syntax, &theme);
49 let mut lines = Vec::<String>::new();
50 for line in LinesWithEndings::from(&s) {
51 let ranges: Vec<(Style, &str)> = try_result!(h.highlight_line(line, &ps));
52 let escaped = as_24_bit_terminal_escaped(&ranges[..], true);
53 lines.push(format!("{}", escaped));
54 }
55 Ok(lines.join(""))
56}
57
58pub fn format_code(stream: &TokenStream) -> String {
59 let source = format!("{}", stream);
60 format_code_string(source.as_str())
61}
62
63pub fn highlight_code_string<T: std::fmt::Display>(source: T) -> Result<String> {
64 let source = source.to_string();
65 match syn::parse_str::<Item>(source.as_str()) {
66 Ok(source) => Ok(try_result!(highlight_code(&source.to_token_stream()))),
67 Err(_) => Ok(try_result!(highlight(format_code_naive(source.as_str()), "rs"))),
68 }
69}
70
71pub fn format_code_string<T: std::fmt::Display>(source: T) -> String {
72 format_code_rustfmt(source.to_string())
73 .unwrap_or_else(|_| format_code_naive(source.to_string()))
74}
75
76pub fn format_code_rustfmt<T: std::fmt::Display>(source: T) -> Result<String> {
77 let source = source.to_string();
78 let path = try_result!(Path::tmp_file().write(source.as_bytes()));
79 let (status, _, stderr) =
80 try_result!(iocore::shell_command_string_output(format!("rustfmt {}", &path), "."));
81 if status != 0 {
82 let error = stderr
83 .lines()
84 .filter(|line| line.len() > 3 && line[2..3] == *"|")
85 .map(|line| line[4..].to_string())
86 .collect::<Vec<String>>()
87 .join("\n");
88 return Err(Error::new(error, ErrorType::FormatError));
89 }
90 let formatted = try_result!(path.read());
91 path.delete_unchecked();
92 Ok(formatted)
93}
94
95pub fn theme_from_bytes(bytes: &[u8]) -> Result<Theme> {
96 let f = Cursor::new(bytes.to_vec());
97 let mut reader = BufReader::new(f);
98 let theme = try_result!(ThemeSet::load_from_reader(&mut reader));
99 Ok(theme)
100}
101
102pub fn theme() -> Result<Theme> {
103 Ok(try_result!(theme_from_bytes(include_bytes!("./themes/DarkKorokai.tmTheme"))))
104}