Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make --style parameter more flexible #74

Merged
merged 6 commits into from
May 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 89 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ extern crate syntect;
mod printer;
mod terminal;

use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::env;
use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str::FromStr;

#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
Expand All @@ -51,22 +52,92 @@ lazy_static! {
mod errors {
error_chain! {
foreign_links {
Clap(::clap::Error);
Io(::std::io::Error);
}

errors {
NoCorrectStylesSpecified {
description("no correct styles specified")
}

UnknownStyleName(name: String) {
description("unknown style name")
display("unknown style name: '{}'", name)
}
}
}
}

use errors::*;

pub enum OptionsStyle {
Plain,
LineNumbers,
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub enum OutputComponent {
Changes,
Grid,
Header,
Numbers,
Full,
Plain,
}

impl OutputComponent {
fn components(&self) -> &'static [OutputComponent] {
match *self {
OutputComponent::Changes => &[OutputComponent::Changes],
OutputComponent::Grid => &[OutputComponent::Grid],
OutputComponent::Header => &[OutputComponent::Header],
OutputComponent::Numbers => &[OutputComponent::Numbers],
OutputComponent::Full => &[
OutputComponent::Changes,
OutputComponent::Grid,
OutputComponent::Header,
OutputComponent::Numbers,
],
OutputComponent::Plain => &[],
}
}
}

impl FromStr for OutputComponent {
type Err = Error;

fn from_str(s: &str) -> Result<Self> {
match s {
"changes" => Ok(OutputComponent::Changes),
"grid" => Ok(OutputComponent::Grid),
"header" => Ok(OutputComponent::Header),
"numbers" => Ok(OutputComponent::Numbers),
"full" => Ok(OutputComponent::Full),
"plain" => Ok(OutputComponent::Plain),
_ => Err(ErrorKind::UnknownStyleName(s.to_owned()).into()),
}
}
}

pub struct OutputComponents(HashSet<OutputComponent>);

impl OutputComponents {
fn changes(&self) -> bool {
self.0.contains(&OutputComponent::Changes)
}

fn grid(&self) -> bool {
self.0.contains(&OutputComponent::Grid)
}

fn header(&self) -> bool {
self.0.contains(&OutputComponent::Header)
}

fn numbers(&self) -> bool {
self.0.contains(&OutputComponent::Numbers)
}
}

pub struct Options<'a> {
pub true_color: bool,
pub style: OptionsStyle,
pub output_components: OutputComponents,
pub language: Option<&'a str>,
pub colored_output: bool,
pub paging: bool,
Expand Down Expand Up @@ -456,8 +527,10 @@ fn run() -> Result<()> {
.arg(
Arg::with_name("style")
.long("style")
.possible_values(&["auto", "plain", "line-numbers", "full"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no more list of possible values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have updated the handling of the comma-separated argument and could re-add the possible values! 👍

.default_value("auto")
.use_delimiter(true)
.takes_value(true)
.possible_values(&["full", "plain", "changes", "header", "grid", "numbers"])
.default_value("full")
.help("Additional info to display along with content"),
)
.arg(
Expand Down Expand Up @@ -545,18 +618,17 @@ fn run() -> Result<()> {
})
.unwrap_or_else(|| vec![None]); // read from stdin (None) if no args are given

let output_components = values_t!(app_matches.values_of("style"), OutputComponent)?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very nice 👍

.into_iter()
.map(|style| style.components())
.fold(HashSet::new(), |mut acc, components| {
acc.extend(components.iter().cloned());
acc
});

let options = Options {
true_color: is_truecolor_terminal(),
style: match app_matches.value_of("style") {
Some("plain") => OptionsStyle::Plain,
Some("line-numbers") => OptionsStyle::LineNumbers,
Some("full") => OptionsStyle::Full,
Some("auto") | _ => if interactive_terminal {
OptionsStyle::Full
} else {
OptionsStyle::Plain
},
},
output_components: OutputComponents(output_components),
language: app_matches.value_of("language"),
colored_output: match app_matches.value_of("color") {
Some("always") => true,
Expand Down
101 changes: 57 additions & 44 deletions src/printer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use errors::*;
use std::io::Write;
use syntect::highlighting;
use terminal::as_terminal_escaped;
use {Colors, LineChange, LineChanges, Options, OptionsStyle};
use {Colors, LineChange, LineChanges, Options};

const PANEL_WIDTH: usize = 7;

Expand Down Expand Up @@ -31,19 +31,20 @@ impl<'a> Printer<'a> {
}

pub fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
match self.options.style {
OptionsStyle::Full => {}
_ => return Ok(()),
if !self.options.output_components.header() {
return Ok(());
}

self.print_horizontal_line('┬')?;
if self.options.output_components.grid() {
self.print_horizontal_line('┬')?;

write!(
self.handle,
"{}{} ",
" ".repeat(PANEL_WIDTH),
self.colors.grid.paint("│"),
)?;
write!(
self.handle,
"{}{} ",
" ".repeat(PANEL_WIDTH),
self.colors.grid.paint("│"),
)?;
}

writeln!(
self.handle,
Expand All @@ -52,11 +53,15 @@ impl<'a> Printer<'a> {
self.colors.filename.paint(filename.unwrap_or("STDIN"))
)?;

self.print_horizontal_line('┼')
if self.options.output_components.grid() {
self.print_horizontal_line('┼')?;
}

Ok(())
}

pub fn print_footer(&mut self) -> Result<()> {
if let OptionsStyle::Full = self.options.style {
if self.options.output_components.grid() {
self.print_horizontal_line('┴')
} else {
Ok(())
Expand All @@ -79,12 +84,17 @@ impl<'a> Printer<'a> {
)),
];

let grid_requested = self.options.output_components.grid();
write!(
self.handle,
"{}",
decorations
.into_iter()
.filter_map(|dec| dec)
.filter_map(|dec| if grid_requested {
Some(dec.unwrap_or(" ".to_owned()))
} else {
dec
})
.collect::<Vec<_>>()
.join(" ")
)?;
Expand All @@ -93,45 +103,48 @@ impl<'a> Printer<'a> {
}

fn print_line_number(&self, line_number: usize) -> Option<String> {
if let OptionsStyle::Plain = self.options.style {
return None;
if self.options.output_components.numbers() {
Some(
self.colors
.line_number
.paint(format!("{:4}", line_number))
.to_string(),
)
} else if self.options.output_components.grid() {
Some(" ".to_owned())
} else {
None
}

Some(
self.colors
.line_number
.paint(format!("{:4}", line_number))
.to_string(),
)
}

fn print_git_marker(&self, line_number: usize) -> Option<String> {
match self.options.style {
OptionsStyle::Full => {}
_ => return None,
}

let marker = if let Some(ref changes) = self.line_changes {
match changes.get(&(line_number as u32)) {
Some(&LineChange::Added) => self.colors.git_added.paint("+"),
Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
_ => Style::default().paint(" "),
}
if self.options.output_components.changes() {
Some(
if let Some(ref changes) = self.line_changes {
match changes.get(&(line_number as u32)) {
Some(&LineChange::Added) => self.colors.git_added.paint("+"),
Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
_ => Style::default().paint(" "),
}
} else {
Style::default().paint(" ")
}.to_string(),
)
} else if self.options.output_components.grid() {
Some(" ".to_owned())
} else {
Style::default().paint(" ")
};

Some(marker.to_string())
None
}
}

fn print_line_border(&self) -> Option<String> {
if let OptionsStyle::Plain = self.options.style {
return None;
if self.options.output_components.grid() {
Some(self.colors.grid.paint("│").to_string())
} else {
None
}

Some(self.colors.grid.paint("│").to_string())
}

fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
Expand Down