sexprs_formatter/
lib.rs

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}