use std::cell::LazyCell;
use std::cmp::PartialEq;
use std::collections::HashSet;
use std::path::Path;
use syntect::html::{ClassStyle, ClassedHTMLGenerator};
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;
#[derive(PartialEq)]
enum HelpType {
Abbreviated,
Full,
}
fn lazy_vec<T, F: FnOnce() -> T, C>(getters: C) -> Vec<LazyCell<T, F>> where C: IntoIterator<Item = F>{
getters.into_iter().map(|getter| LazyCell::new(getter)).collect()
}
fn syntax_sets() -> Vec<LazyCell<SyntaxSet>> {
lazy_vec([
|| SyntaxSet::load_defaults_newlines(),
|| two_face::syntax::extra_newlines(),
])
}
fn print_help(arg0: &str, help_type: HelpType) {
print!(" Usage: {} <filename> [extension]
If the syntax cannot be readily (or correctly) determined by the file's extension,
an explicit one should be provided as the second argument.
-h, --help Display this help text
--help-all Also lists all supported extensions
", arg0);
if help_type == HelpType::Abbreviated {
return;
}
let syntax_sets = syntax_sets();
let all_extensions = syntax_sets.iter()
.flat_map(|set| set.syntaxes())
.flat_map(|syntax| &syntax.file_extensions);
let unique_extensions: HashSet<&String> = HashSet::from_iter(all_extensions);
let mut unique_extensions: Vec<&String> = unique_extensions.into_iter().collect();
unique_extensions.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
let mut line_length = 0;
let joiner = ", ";
let indent = " ";
print!("\n Supported extensions:\n\n{}", indent);
let mut iter = unique_extensions.iter().peekable();
while let Some(ext) = iter.next() {
line_length += ext.len() + joiner.len();
if line_length > 80 {
print!("\n{}", indent);
line_length = 0;
}
print!("{}", ext);
if iter.peek().is_some() {
print!("{}", joiner);
}
}
}
fn main() {
let program_name = std::env::args().next().unwrap_or("syntax-html".into());
let mut args = std::env::args().skip(1);
let file = args.next().expect("filename argument should be provided");
if file == "-h" || file == "--help" {
print_help(&program_name, HelpType::Abbreviated);
return;
}
if file == "--help-all" {
print_help(&program_name, HelpType::Full);
return;
}
let ext = args.next().or_else(|| {
Path::new(&file).extension()
.and_then(|os_str| os_str.to_str())
.map(|str| str.to_owned())
}).expect("a valid extension should be on the file or an explicit extension argument should be provided");
let syntax_sets = syntax_sets();
let (syntax_set, syntax) = syntax_sets.iter().filter_map(|set| {
match set.find_syntax_by_extension(&ext) {
Some(syn) => Some((set, syn)),
None => None,
}
}).next().expect(&format!("no syntax found in syntect or two_face or extension: '{}'", ext));
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, syntax_set, ClassStyle::Spaced);
let code = std::fs::read_to_string(file).expect("reading file to string");
for line in LinesWithEndings::from(&code) {
html_generator.parse_html_for_line_which_includes_newline(line).expect("generate html line");
}
println!("{}", html_generator.finalize());
}