From 9316f2a758d015ced23af421e1257f5b926f25c4 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Wed, 22 Aug 2018 22:29:12 +0200 Subject: [PATCH] Major refactoring and cleanup --- src/app.rs | 147 +++++++++++++-------------------------------- src/assets.rs | 2 +- src/decorations.rs | 3 - src/features.rs | 27 ++++++--- src/line_range.rs | 73 ++++++++++++++++++++++ src/main.rs | 55 +++++++++-------- src/output.rs | 5 +- src/printer.rs | 20 +++--- src/style.rs | 3 +- src/terminal.rs | 1 + 10 files changed, 181 insertions(+), 155 deletions(-) create mode 100644 src/line_range.rs diff --git a/src/app.rs b/src/app.rs index 71ab2110..5ecb3d53 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,15 +1,51 @@ -use atty::{self, Stream}; -use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand}; -use console::Term; -use errors::*; use std::collections::HashSet; use std::env; -use style::{OutputComponent, OutputComponents, OutputWrap}; + +use atty::{self, Stream}; + +use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, ArgMatches, SubCommand}; + +use console::Term; #[cfg(windows)] use ansi_term; use assets::BAT_THEME_DEFAULT; +use errors::*; +use line_range::LineRange; +use style::{OutputComponent, OutputComponents, OutputWrap}; + +#[derive(Debug, Clone, Copy)] +pub enum PagingMode { + Always, + QuitIfOneScreen, + Never, +} + +pub struct Config<'a> { + pub true_color: bool, + pub output_wrap: OutputWrap, + pub output_components: OutputComponents, + pub language: Option<&'a str>, + pub colored_output: bool, + pub paging_mode: PagingMode, + pub term_width: usize, + pub files: Vec>, + pub theme: String, + pub line_range: Option, +} + +fn is_truecolor_terminal() -> bool { + env::var("COLORTERM") + .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit") + .unwrap_or(false) +} + +/// Helper function that should might appear in Rust stable at some point +/// (https://doc.rust-lang.org/stable/std/option/enum.Option.html#method.transpose) +fn transpose(opt: Option>) -> Result> { + opt.map_or(Ok(None), |res| res.map(Some)) +} pub struct App { pub matches: ArgMatches<'static>, @@ -319,104 +355,3 @@ impl App { })) } } - -#[derive(Debug, Clone, Copy)] -pub enum PagingMode { - Always, - QuitIfOneScreen, - Never, -} - -pub struct Config<'a> { - pub true_color: bool, - pub output_wrap: OutputWrap, - pub output_components: OutputComponents, - pub language: Option<&'a str>, - pub colored_output: bool, - pub paging_mode: PagingMode, - pub term_width: usize, - pub files: Vec>, - pub theme: String, - pub line_range: Option, -} - -fn is_truecolor_terminal() -> bool { - env::var("COLORTERM") - .map(|colorterm| colorterm == "truecolor" || colorterm == "24bit") - .unwrap_or(false) -} - -pub struct LineRange { - pub lower: usize, - pub upper: usize, -} - -impl LineRange { - pub fn from(range_raw: &str) -> Result { - LineRange::parse_range(range_raw) - } - - pub fn new() -> LineRange { - LineRange { - lower: usize::min_value(), - upper: usize::max_value(), - } - } - - pub fn parse_range(range_raw: &str) -> Result { - let mut new_range = LineRange::new(); - - if range_raw.bytes().nth(0).ok_or("Empty line range")? == b':' { - new_range.upper = range_raw[1..].parse()?; - return Ok(new_range); - } else if range_raw.bytes().last().ok_or("Empty line range")? == b':' { - new_range.lower = range_raw[..range_raw.len() - 1].parse()?; - return Ok(new_range); - } - - let line_numbers: Vec<&str> = range_raw.split(':').collect(); - if line_numbers.len() == 2 { - new_range.lower = line_numbers[0].parse()?; - new_range.upper = line_numbers[1].parse()?; - return Ok(new_range); - } - Err("expected single ':' character".into()) - } -} - -#[test] -fn test_parse_line_range_full() { - let range = LineRange::from("40:50").expect("Shouldn't fail on test!"); - assert_eq!(40, range.lower); - assert_eq!(50, range.upper); -} - -#[test] -fn test_parse_line_range_partial_min() { - let range = LineRange::from(":50").expect("Shouldn't fail on test!"); - assert_eq!(usize::min_value(), range.lower); - assert_eq!(50, range.upper); -} - -#[test] -fn test_parse_line_range_partial_max() { - let range = LineRange::from("40:").expect("Shouldn't fail on test!"); - assert_eq!(40, range.lower); - assert_eq!(usize::max_value(), range.upper); -} - -#[test] -fn test_parse_line_range_fail() { - let range = LineRange::from("40:50:80"); - assert!(range.is_err()); - let range = LineRange::from("40::80"); - assert!(range.is_err()); - let range = LineRange::from(":40:"); - assert!(range.is_err()); - let range = LineRange::from("40"); - assert!(range.is_err()); -} - -fn transpose(opt: Option>) -> Result> { - opt.map_or(Ok(None), |res| res.map(Some)) -} diff --git a/src/assets.rs b/src/assets.rs index b36233b5..12a42166 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -193,7 +193,7 @@ impl HighlightingAssets { } } -// TODO: ideally, this function would be part of syntect's `ThemeSet`. +// TODO: this function will soon be part of syntect's `ThemeSet`. fn extend_theme_set>(theme_set: &mut ThemeSet, folder: P) -> Result<()> { let paths = ThemeSet::discover_theme_paths(folder)?; for p in &paths { diff --git a/src/decorations.rs b/src/decorations.rs index 40c4d80e..9d779438 100644 --- a/src/decorations.rs +++ b/src/decorations.rs @@ -14,7 +14,6 @@ pub trait Decoration { fn width(&self) -> usize; } -// Line number decoration. pub struct LineNumberDecoration { color: Style, cached_wrap: DecorationText, @@ -65,7 +64,6 @@ impl Decoration for LineNumberDecoration { } } -// Line changes decoration. pub struct LineChangesDecoration { cached_none: DecorationText, cached_added: DecorationText, @@ -121,7 +119,6 @@ impl Decoration for LineChangesDecoration { } } -// Grid border decoration. pub struct GridBorderDecoration { cached: DecorationText, } diff --git a/src/features.rs b/src/features.rs index 151901e9..ff293fdb 100644 --- a/src/features.rs +++ b/src/features.rs @@ -1,16 +1,20 @@ -use ansi_term::Colour::Green; -use app::{Config, LineRange}; -use assets::HighlightingAssets; -use diff::get_git_diff; -use errors::*; -use output::OutputType; -use printer::Printer; use std::fs::File; use std::io::{self, BufRead, BufReader}; + +use ansi_term::Colour::Green; + use syntect::easy::HighlightLines; use syntect::highlighting::Theme; use syntect::parsing::SyntaxDefinition; +use app::Config; +use assets::HighlightingAssets; +use diff::get_git_diff; +use errors::*; +use line_range::LineRange; +use output::OutputType; +use printer::Printer; + pub fn list_languages(assets: &HighlightingAssets, term_width: usize) { let mut languages = assets .syntax_set @@ -56,6 +60,13 @@ pub fn list_languages(assets: &HighlightingAssets, term_width: usize) { } } +pub fn list_themes(assets: &HighlightingAssets) { + let themes = &assets.theme_set.themes; + for (theme, _) in themes.iter() { + println!("{}", theme); + } +} + pub fn print_files(assets: &HighlightingAssets, config: &Config) -> Result { let theme = assets.get_theme(&config.theme); @@ -86,7 +97,7 @@ fn print_file( printer: &mut Printer, filename: Option<&str>, ) -> Result<()> { - let stdin = io::stdin(); // TODO: this is not always needed + let stdin = io::stdin(); // TODO: this variable is not always needed { let reader: Box = match filename { None => Box::new(stdin.lock()), diff --git a/src/line_range.rs b/src/line_range.rs new file mode 100644 index 00000000..c9843e17 --- /dev/null +++ b/src/line_range.rs @@ -0,0 +1,73 @@ +use errors::*; + +pub struct LineRange { + pub lower: usize, + pub upper: usize, +} + +impl LineRange { + pub fn from(range_raw: &str) -> Result { + LineRange::parse_range(range_raw) + } + + pub fn new() -> LineRange { + LineRange { + lower: usize::min_value(), + upper: usize::max_value(), + } + } + + pub fn parse_range(range_raw: &str) -> Result { + let mut new_range = LineRange::new(); + + if range_raw.bytes().nth(0).ok_or("Empty line range")? == b':' { + new_range.upper = range_raw[1..].parse()?; + return Ok(new_range); + } else if range_raw.bytes().last().ok_or("Empty line range")? == b':' { + new_range.lower = range_raw[..range_raw.len() - 1].parse()?; + return Ok(new_range); + } + + let line_numbers: Vec<&str> = range_raw.split(':').collect(); + if line_numbers.len() == 2 { + new_range.lower = line_numbers[0].parse()?; + new_range.upper = line_numbers[1].parse()?; + return Ok(new_range); + } + + Err("expected single ':' character".into()) + } +} + +#[test] +fn test_parse_full() { + let range = LineRange::from("40:50").expect("Shouldn't fail on test!"); + assert_eq!(40, range.lower); + assert_eq!(50, range.upper); +} + +#[test] +fn test_parse_partial_min() { + let range = LineRange::from(":50").expect("Shouldn't fail on test!"); + assert_eq!(usize::min_value(), range.lower); + assert_eq!(50, range.upper); +} + +#[test] +fn test_parse_partial_max() { + let range = LineRange::from("40:").expect("Shouldn't fail on test!"); + assert_eq!(40, range.lower); + assert_eq!(usize::max_value(), range.upper); +} + +#[test] +fn test_parse_fail() { + let range = LineRange::from("40:50:80"); + assert!(range.is_err()); + let range = LineRange::from("40::80"); + assert!(range.is_err()); + let range = LineRange::from(":40:"); + assert!(range.is_err()); + let range = LineRange::from("40"); + assert!(range.is_err()); +} diff --git a/src/main.rs b/src/main.rs index 1ec440c1..80e772b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ mod assets; mod decorations; mod diff; mod features; +mod line_range; mod output; mod printer; mod style; @@ -33,7 +34,7 @@ use std::process; use app::App; use assets::{clear_assets, config_dir, HighlightingAssets}; -use features::{list_languages, print_files}; +use features::{list_languages, list_themes, print_files}; mod errors { error_chain! { @@ -62,6 +63,24 @@ mod errors { use errors::*; +fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> { + if matches.is_present("init") { + let source_dir = matches.value_of("source").map(Path::new); + let target_dir = matches.value_of("target").map(Path::new); + + let blank = matches.is_present("blank"); + + let assets = HighlightingAssets::from_files(source_dir, blank)?; + assets.save(target_dir)?; + } else if matches.is_present("clear") { + clear_assets(); + } else if matches.is_present("config-dir") { + println!("{}", config_dir()); + } + + Ok(()) +} + /// Returns `Err(..)` upon fatal errors. Otherwise, returns `Some(true)` on full success and /// `Some(false)` if any intermediate errors occurred (were printed). fn run() -> Result { @@ -69,21 +88,8 @@ fn run() -> Result { match app.matches.subcommand() { ("cache", Some(cache_matches)) => { - if cache_matches.is_present("init") { - let source_dir = cache_matches.value_of("source").map(Path::new); - let target_dir = cache_matches.value_of("target").map(Path::new); - - let blank = cache_matches.is_present("blank"); - - let assets = HighlightingAssets::from_files(source_dir, blank)?; - assets.save(target_dir)?; - } else if cache_matches.is_present("clear") { - clear_assets(); - } else if cache_matches.is_present("config-dir") { - println!("{}", config_dir()); - } - - return Ok(true); + run_cache_subcommand(cache_matches)?; + Ok(true) } _ => { let config = app.config()?; @@ -91,18 +97,15 @@ fn run() -> Result { if app.matches.is_present("list-languages") { list_languages(&assets, config.term_width); - return Ok(true); - } - if app.matches.is_present("list-themes") { - let themes = &assets.theme_set.themes; - for (theme, _) in themes.iter() { - println!("{}", theme); - } - return Ok(true); - } + Ok(true) + } else if app.matches.is_present("list-themes") { + list_themes(&assets); - print_files(&assets, &config) + Ok(true) + } else { + print_files(&assets, &config) + } } } } diff --git a/src/output.rs b/src/output.rs index bd565033..07473535 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,9 +1,10 @@ -use app::PagingMode; -use errors::*; use std::env; use std::io::{self, Write}; use std::process::{Child, Command, Stdio}; +use app::PagingMode; +use errors::*; + pub enum OutputType { Pager(Child), Stdout(io::Stdout), diff --git a/src/printer.rs b/src/printer.rs index 7b288538..2d156806 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -1,15 +1,19 @@ -use ansi_term::Colour::{Fixed, Green, Red, Yellow}; -use ansi_term::Style; -use app::Config; -use console::AnsiCodeIterator; -use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration}; -use diff::LineChanges; -use errors::*; use std::boxed::Box; use std::io::Write; use std::vec::Vec; -use style::OutputWrap; + +use ansi_term::Colour::{Fixed, Green, Red, Yellow}; +use ansi_term::Style; + +use console::AnsiCodeIterator; + use syntect::highlighting::{self, Theme}; + +use app::Config; +use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration}; +use diff::LineChanges; +use errors::*; +use style::OutputWrap; use terminal::{as_terminal_escaped, to_ansi_color}; pub struct Printer<'a> { diff --git a/src/style.rs b/src/style.rs index 82ba2ea1..3af76347 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,7 +1,8 @@ -use errors::*; use std::collections::HashSet; use std::str::FromStr; +use errors::*; + #[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)] pub enum OutputComponent { Auto, diff --git a/src/terminal.rs b/src/terminal.rs index 64f61de4..db472779 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,5 +1,6 @@ use ansi_term::Colour::{Fixed, RGB}; use ansi_term::{self, Style}; + use syntect::highlighting::{self, FontStyle}; /// Approximate a 24 bit color value by a 8 bit ANSI code