@@ -17,13 +17,13 @@ extern crate directories;
1717extern crate git2;
1818extern crate syntect;
1919
20+mod app;
2021mod assets;
2122mod diff;
2223mod printer;
2324mod terminal;
2425
2526use std::collections::HashSet;
26−use std::env;
2727use std::fs::{self, File};
2828use std::io::{self, BufRead, BufReader, Write};
2929use std::process::{self, Child, Command, Stdio};
@@ -34,13 +34,12 @@ use std::os::unix::fs::FileTypeExt;
3434
3535use ansi_term::Colour::{Fixed, Green, Red, White, Yellow};
3636use ansi_term::Style;
37−use atty::Stream;
38−use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
3937
4038use syntect::easy::HighlightLines;
4139use syntect::highlighting::Theme;
4240use syntect::parsing::SyntaxSet;
4341
42+use app::{App, Config};
4443use assets::{config_dir, syntax_set_path, theme_set_path, HighlightingAssets};
4544use diff::get_git_diff;
4645use printer::Printer;
@@ -215,7 +214,7 @@ impl Colors {
215214}
216215
217216fn print_file(
218− options: &Options,
217+ config: &Config,
219218 theme: &Theme,
220219 syntax_set: &SyntaxSet,
221220 printer: &mut Printer,
@@ -228,7 +227,7 @@ fn print_file(
228227 Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
229228 };
230229
231− let syntax = match (options.language, filename) {
230+ let syntax = match (config.language, filename) {
232231 (Some(language), _) => syntax_set.find_syntax_by_token(language),
233232 (None, Some(filename)) => {
234233 #[cfg(not(unix))]
@@ -292,110 +291,10 @@ fn get_output_type(paging: bool) -> OutputType {
292291 }
293292}
294293
295−fn is_truecolor_terminal() -> bool {
296− env::var("COLORTERM")
297− .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
298− .unwrap_or(false)
299−}
300−
301294fn run() -> Result<()> {
302− let interactive_terminal = atty::is(Stream::Stdout);
303−
304− let clap_color_setting = if interactive_terminal {
305− AppSettings::ColoredHelp
306− } else {
307− AppSettings::ColorNever
308− };
295+ let app = App::new();
309296
310− let app_matches = App::new(crate_name!())
311− .version(crate_version!())
312− .global_setting(clap_color_setting)
313− .global_setting(AppSettings::DeriveDisplayOrder)
314− .global_setting(AppSettings::UnifiedHelpMessage)
315− .global_setting(AppSettings::NextLineHelp)
316− .setting(AppSettings::InferSubcommands)
317− .setting(AppSettings::ArgsNegateSubcommands)
318− .setting(AppSettings::DisableHelpSubcommand)
319− .setting(AppSettings::VersionlessSubcommands)
320− .max_term_width(90)
321− .about(crate_description!())
322− .arg(
323− Arg::with_name("language")
324− .short("l")
325− .long("language")
326− .help("Set the language for highlighting")
327− .takes_value(true),
328− )
329− .arg(
330− Arg::with_name("FILE")
331− .help("File(s) to print")
332− .multiple(true)
333− .empty_values(false),
334− )
335− .arg(
336− Arg::with_name("style")
337− .long("style")
338− .use_delimiter(true)
339− .takes_value(true)
340− .possible_values(&[
341− "auto", "full", "plain", "changes", "header", "grid", "numbers",
342− ])
343− .default_value("auto")
344− .help("Additional info to display along with content"),
345− )
346− .arg(
347− Arg::with_name("color")
348− .long("color")
349− .takes_value(true)
350− .possible_values(&["auto", "never", "always"])
351− .default_value("auto")
352− .help("When to use colors"),
353− )
354− .arg(
355− Arg::with_name("paging")
356− .long("paging")
357− .takes_value(true)
358− .possible_values(&["auto", "never", "always"])
359− .default_value("auto")
360− .help("When to use the pager"),
361− )
362− .arg(
363− Arg::with_name("list-languages")
364− .long("list-languages")
365− .help("Displays supported languages"),
366− )
367− .subcommand(
368− SubCommand::with_name("cache")
369− .about("Modify the syntax-definition and theme cache")
370− .arg(
371− Arg::with_name("init")
372− .long("init")
373− .short("i")
374− .help("Initialize the cache by loading from the config dir"),
375− )
376− .arg(
377− Arg::with_name("clear")
378− .long("clear")
379− .short("c")
380− .help("Reset the cache"),
381− )
382− .arg(
383− Arg::with_name("config-dir")
384− .long("config-dir")
385− .short("d")
386− .help("Show the configuration directory"),
387− )
388− .group(
389− ArgGroup::with_name("cache-actions")
390− .args(&["init", "clear", "config-dir"])
391− .required(true),
392− ),
393− )
394− .help_message("Print this help message.")
395− .version_message("Show version information.")
396− .get_matches();
397−
398− match app_matches.subcommand() {
297+ match app.matches.subcommand() {
399298 ("cache", Some(cache_matches)) => {
400299 if cache_matches.is_present("init") {
401300 let assets = HighlightingAssets::from_files()?;
@@ -413,57 +312,12 @@ fn run() -> Result<()> {
413312 }
414313 }
415314 _ => {
416− let files: Vec<Option<&str>> = app_matches
417− .values_of("FILE")
418− .map(|values| {
419− values
420− .map(|filename| {
421− if filename == "-" {
422− None
423− } else {
424− Some(filename)
425− }
426− })
427− .collect()
428− })
429− .unwrap_or_else(|| vec![None]); // read from stdin (None) if no args are given
430−
431− let output_components = values_t!(app_matches.values_of("style"), OutputComponent)?
432− .into_iter()
433− .map(|style| style.components(interactive_terminal))
434− .fold(HashSet::new(), |mut acc, components| {
435− acc.extend(components.iter().cloned());
436− acc
437− });
438−
439− let options = Options {
440− true_color: is_truecolor_terminal(),
441− output_components: OutputComponents(output_components),
442− language: app_matches.value_of("language"),
443− colored_output: match app_matches.value_of("color") {
444− Some("always") => true,
445− Some("never") => false,
446− _ => interactive_terminal,
447− },
448− paging: match app_matches.value_of("paging") {
449− Some("always") => true,
450− Some("never") => false,
451− Some("auto") | _ => if files.contains(&None) {
452− // If we are reading from stdin, only enable paging if we write to an
453− // interactive terminal and if we do not *read* from an interactive
454− // terminal.
455− interactive_terminal && !atty::is(Stream::Stdin)
456− } else {
457− interactive_terminal
458− },
459− },
460− term_width: console::Term::stdout().size().1 as usize,
461− };
315+ let config = app.config()?;
462316
463317 let assets = HighlightingAssets::new();
464318 let theme = assets.default_theme()?;
465319
466− if app_matches.is_present("list-languages") {
320+ if app.matches.is_present("list-languages") {
467321 let languages = assets.syntax_set.syntaxes();
468322
469323 let longest = languages
@@ -481,7 +335,7 @@ fn run() -> Result<()> {
481335 print!("{:width$}{}", lang.name, separator, width = longest);
482336
483337 // Line-wrapping for the possible file extension overflow.
484− let desired_width = options.term_width - longest - separator.len();
338+ let desired_width = config.term_width - longest - separator.len();
485339 // Number of characters on this line so far, wrap before `desired_width`
486340 let mut num_chars = 0;
487341
@@ -507,14 +361,14 @@ fn run() -> Result<()> {
507361 return Ok(());
508362 }
509363
510− let mut output_type = get_output_type(options.paging);
364+ let mut output_type = get_output_type(config.paging);
511365 let handle = output_type.handle()?;
512− let mut printer = Printer::new(handle, &options);
366+ let mut printer = Printer::new(handle, &config);
513367
514− for file in files {
368+ for file in &config.files {
515369 printer.line_changes = file.and_then(|filename| get_git_diff(filename));
516370
517− print_file(&options, theme, &assets.syntax_set, &mut printer, file)?;
371+ print_file(&config, theme, &assets.syntax_set, &mut printer, *file)?;
518372 }
519373 }
520374 }