From 226d9a573acb8025278d7afc19359ca167841536 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Thu, 23 Aug 2018 23:13:24 +0200 Subject: [PATCH] Add simple loop-through mode Use a loop-through mode that simply copies input to output if a non-interactive terminal is detected. see #150 --- src/app.rs | 43 ++++++++++++++++++++++++++++++------------- src/controller.rs | 35 +++++++++++++++++++++-------------- src/printer.rs | 43 +++++++++++++++++++++++++++++++++++++++++-- tests/tester.rs | 7 +++++-- 4 files changed, 97 insertions(+), 31 deletions(-) diff --git a/src/app.rs b/src/app.rs index 0f9c0714..f478bb42 100644 --- a/src/app.rs +++ b/src/app.rs @@ -206,6 +206,18 @@ impl App { .help("When to use colors.") .long_help("Specify when to use colored output. The automatic mode \ only enables colors if an interactive terminal is detected."), + ).arg( + Arg::with_name("decorations") + .long("decorations") + .overrides_with("decorations") + .takes_value(true) + .value_name("when") + .possible_values(&["auto", "never", "always"]) + .default_value("auto") + .help("When to show the decorations specified by '--style'.") + .long_help("Specify when to use the decorations that have been specified \ + via '--style'. The automatic mode only enables decorations if \ + an interactive terminal is detected."), ).arg( Arg::with_name("paging") .long("paging") @@ -336,8 +348,9 @@ impl App { }, }, term_width: Term::stdout().size().1 as usize, - loop_through: self.interactive_output - && self.matches.value_of("color") != Some("always"), + loop_through: !(self.interactive_output + || self.matches.value_of("color") == Some("always") + || self.matches.value_of("decorations") == Some("always")), files, theme: self .matches @@ -366,16 +379,20 @@ impl App { fn output_components(&self) -> Result { let matches = &self.matches; - Ok(OutputComponents(if matches.is_present("number") { - [OutputComponent::Numbers].iter().cloned().collect() - } else { - values_t!(matches.values_of("style"), OutputComponent)? - .into_iter() - .map(|style| style.components(self.interactive_output)) - .fold(HashSet::new(), |mut acc, components| { - acc.extend(components.iter().cloned()); - acc - }) - })) + Ok(OutputComponents( + if matches.value_of("decorations") == Some("never") { + HashSet::new() + } else if matches.is_present("number") { + [OutputComponent::Numbers].iter().cloned().collect() + } else { + values_t!(matches.values_of("style"), OutputComponent)? + .into_iter() + .map(|style| style.components(self.interactive_output)) + .fold(HashSet::new(), |mut acc, components| { + acc.extend(components.iter().cloned()); + acc + }) + }, + )) } } diff --git a/src/controller.rs b/src/controller.rs index e2de210e..38cb34a4 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -6,7 +6,7 @@ use assets::HighlightingAssets; use errors::*; use line_range::LineRange; use output::OutputType; -use printer::{InteractivePrinter, Printer}; +use printer::{InteractivePrinter, Printer, SimplePrinter}; pub struct Controller<'a> { config: &'a Config<'a>, @@ -23,8 +23,14 @@ impl<'b> Controller<'b> { let writer = output_type.handle()?; let mut no_errors: bool = true; - for file in &self.config.files { - let result = self.print_file(writer, *file); + for filename in &self.config.files { + let result = if self.config.loop_through { + let mut printer = SimplePrinter::new(); + self.print_file(&mut printer, writer, *filename) + } else { + let mut printer = InteractivePrinter::new(&self.config, &self.assets, *filename); + self.print_file(&mut printer, writer, *filename) + }; if let Err(error) = result { handle_error(&error); @@ -35,9 +41,12 @@ impl<'b> Controller<'b> { Ok(no_errors) } - fn print_file(&self, writer: &mut Write, filename: Option<&str>) -> Result<()> { - let mut printer = InteractivePrinter::new(&self.config, &self.assets, filename); - + fn print_file( + &self, + printer: &mut P, + writer: &mut Write, + filename: Option<&str>, + ) -> Result<()> { let stdin = io::stdin(); { let reader: Box = match filename { @@ -46,7 +55,7 @@ impl<'b> Controller<'b> { }; printer.print_header(writer, filename)?; - self.print_file_ranges(&mut printer, writer, reader, &self.config.line_range)?; + self.print_file_ranges(printer, writer, reader, &self.config.line_range)?; printer.print_footer(writer)?; } Ok(()) @@ -59,14 +68,12 @@ impl<'b> Controller<'b> { mut reader: Box, line_ranges: &Option, ) -> Result<()> { - let mut buffer = Vec::new(); + let mut line_buffer = Vec::new(); let mut line_number: usize = 1; - while reader.read_until(b'\n', &mut buffer)? > 0 { + while reader.read_until(b'\n', &mut line_buffer)? > 0 { { - let line = String::from_utf8_lossy(&buffer); - match line_ranges { &Some(ref range) => { if line_number < range.lower { @@ -75,17 +82,17 @@ impl<'b> Controller<'b> { // no more lines in range break; } else { - printer.print_line(writer, line_number, &line)?; + printer.print_line(writer, line_number, &line_buffer)?; } } &None => { - printer.print_line(writer, line_number, &line)?; + printer.print_line(writer, line_number, &line_buffer)?; } } line_number += 1; } - buffer.clear(); + line_buffer.clear(); } Ok(()) } diff --git a/src/printer.rs b/src/printer.rs index c3cc38a6..854c6ad3 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -22,7 +22,40 @@ use terminal::{as_terminal_escaped, to_ansi_color}; pub trait Printer { fn print_header(&mut self, handle: &mut Write, filename: Option<&str>) -> Result<()>; fn print_footer(&mut self, handle: &mut Write) -> Result<()>; - fn print_line(&mut self, handle: &mut Write, line_number: usize, line: &str) -> Result<()>; + fn print_line( + &mut self, + handle: &mut Write, + line_number: usize, + line_buffer: &[u8], + ) -> Result<()>; +} + +pub struct SimplePrinter; + +impl SimplePrinter { + pub fn new() -> Self { + SimplePrinter {} + } +} + +impl Printer for SimplePrinter { + fn print_header(&mut self, _handle: &mut Write, _filename: Option<&str>) -> Result<()> { + Ok(()) + } + + fn print_footer(&mut self, _handle: &mut Write) -> Result<()> { + Ok(()) + } + + fn print_line( + &mut self, + handle: &mut Write, + _line_number: usize, + line_buffer: &[u8], + ) -> Result<()> { + handle.write(line_buffer)?; + Ok(()) + } } pub struct InteractivePrinter<'a> { @@ -153,7 +186,13 @@ impl<'a> Printer for InteractivePrinter<'a> { } } - fn print_line(&mut self, handle: &mut Write, line_number: usize, line: &str) -> Result<()> { + fn print_line( + &mut self, + handle: &mut Write, + line_number: usize, + line_buffer: &[u8], + ) -> Result<()> { + let line = String::from_utf8_lossy(&line_buffer); let regions = self.highlighter.highlight(line.as_ref()); let mut cursor: usize = 0; diff --git a/tests/tester.rs b/tests/tester.rs index f7e6033d..16e56931 100644 --- a/tests/tester.rs +++ b/tests/tester.rs @@ -41,8 +41,11 @@ impl BatTester { pub fn test_snapshot(&self, style: &str) { let output = Command::new(&self.exe) .current_dir(self.temp_dir.path()) - .args(&["sample.rs", &format!("--style={}", style)]) - .output() + .args(&[ + "sample.rs", + "--decorations=always", + &format!("--style={}", style), + ]).output() .expect("bat failed"); // have to do the replace because the filename in the header changes based on the current working directory