on master help sign in
Merge pull request #871 from neuronull/fix_654_stdin_filename

Implement --file-name<name> option

commit37b3b8730d313cef0fc9faa1e1ecc98406c62ed7 authorDavid Peter <sharkdp@users.noreply.github.com> dateThu 26 Mar 2020, 09:35 (7 years ago) parent7c50fe583772bd
7 files changed+150−13
src/bin/bat/app.rs+11 src/bin/bat/clap_app.rs+12 src/config.rs+3 src/controller.rs+10−4 src/printer.rs+28−9 tests/examples/test.binary tests/integration_tests.rs+86
src/bin/bat/app.rs+11
@@ -136,6 +136,13 @@ impl App {
136136 }
137137 });
138138
139+ match self.matches.values_of("file-name") {
140+ Some(filenames) if filenames.len() != files.len() => {
141+ return Err(format!("{} {}", filenames.len(), files.len()).into());
142+ }
143+ _ => {}
144+ }
145+
139146 Ok(Config {
140147 true_color: is_truecolor_terminal(),
141148 language: self.matches.value_of("language").or_else(|| {
@@ -222,6 +229,10 @@ impl App {
222229 .map(LineRanges::from)
223230 .map(|lr| HighlightedLineRanges(lr))
224231 .unwrap_or_default(),
232+ filenames: self
233+ .matches
234+ .values_of("file-name")
235+ .map(|values| values.collect()),
225236 })
226237 }
227238
src/bin/bat/clap_app.rs+12
@@ -93,6 +93,18 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
9393 '--highlight-line 40:' highlights lines 40 to the end of the file"
9494 ),
9595 )
96+ .arg(
97+ Arg::with_name("file-name")
98+ .long("file-name")
99+ .takes_value(true)
100+ .number_of_values(1)
101+ .multiple(true)
102+ .value_name("name")
103+ .help("Specify the name to display for a file.")
104+ .long_help("Specify the name to display for a file. Useful when piping \
105+ data to bat from STDIN when bat does not otherwise know \
106+ the filename."),
107+ )
96108 .arg(
97109 Arg::with_name("tabs")
98110 .long("tabs")
src/config.rs+3
@@ -70,6 +70,9 @@ pub struct Config<'a> {
7070
7171 /// Ranges of lines which should be highlighted with a special background color
7272 pub highlighted_lines: HighlightedLineRanges,
73+
74+ /// Names of files to display when printing
75+ pub filenames: Option<Vec<&'a str>>,
7376}
7477
7578#[test]
src/controller.rs+10−4
@@ -45,7 +45,12 @@ impl<'b> Controller<'b> {
4545
4646 let stdin = io::stdin();
4747
48− for input_file in &self.config.files {
48+ let filenames: Box<dyn Iterator<Item = _>> = match self.config.filenames {
49+ Some(ref filenames) => Box::new(filenames.into_iter().map(|name| Some(*name))),
50+ None => Box::new(std::iter::repeat(None)),
51+ };
52+
53+ for (input_file, file_name) in self.config.files.iter().zip(filenames) {
4954 match input_file.get_reader(&stdin) {
5055 Err(error) => {
5156 handle_error(&error);
@@ -54,7 +59,7 @@ impl<'b> Controller<'b> {
5459 Ok(mut reader) => {
5560 let result = if self.config.loop_through {
5661 let mut printer = SimplePrinter::new();
57− self.print_file(reader, &mut printer, writer, *input_file)
62+ self.print_file(reader, &mut printer, writer, *input_file, file_name)
5863 } else {
5964 let mut printer = InteractivePrinter::new(
6065 &self.config,
@@ -62,7 +67,7 @@ impl<'b> Controller<'b> {
6267 *input_file,
6368 &mut reader,
6469 );
65− self.print_file(reader, &mut printer, writer, *input_file)
70+ self.print_file(reader, &mut printer, writer, *input_file, file_name)
6671 };
6772
6873 if let Err(error) = result {
@@ -82,9 +87,10 @@ impl<'b> Controller<'b> {
8287 printer: &mut P,
8388 writer: &mut dyn Write,
8489 input_file: InputFile<'a>,
90+ file_name: Option<&str>,
8591 ) -> Result<()> {
8692 if !reader.first_line.is_empty() || self.config.style_components.header() {
87− printer.print_header(writer, input_file)?;
93+ printer.print_header(writer, input_file, file_name)?;
8894 }
8995
9096 if !reader.first_line.is_empty() {
src/printer.rs+28−9
@@ -34,7 +34,12 @@ use crate::terminal::{as_terminal_escaped, to_ansi_color};
3434use crate::wrap::OutputWrap;
3535
3636pub trait Printer {
37− fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()>;
37+ fn print_header(
38+ &mut self,
39+ handle: &mut dyn Write,
40+ file: InputFile,
41+ file_name: Option<&str>,
42+ ) -> Result<()>;
3843 fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>;
3944
4045 fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>;
@@ -57,7 +62,12 @@ impl SimplePrinter {
5762}
5863
5964impl Printer for SimplePrinter {
60− fn print_header(&mut self, _handle: &mut dyn Write, _file: InputFile) -> Result<()> {
65+ fn print_header(
66+ &mut self,
67+ _handle: &mut dyn Write,
68+ _file: InputFile,
69+ _file_name: Option<&str>,
70+ ) -> Result<()> {
6171 Ok(())
6272 }
6373
@@ -224,14 +234,20 @@ impl<'a> InteractivePrinter<'a> {
224234}
225235
226236impl<'a> Printer for InteractivePrinter<'a> {
227− fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()> {
237+ fn print_header(
238+ &mut self,
239+ handle: &mut dyn Write,
240+ file: InputFile,
241+ file_name: Option<&str>,
242+ ) -> Result<()> {
228243 if !self.config.style_components.header() {
229244 if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
230245 let input = match file {
231− InputFile::Ordinary(filename) => {
232− format!("file '{}'", filename.to_string_lossy())
233− }
234− _ => "STDIN".into(),
246+ InputFile::Ordinary(filename) => format!(
247+ "file '{}'",
248+ file_name.unwrap_or(&filename.to_string_lossy())
249+ ),
250+ _ => file_name.unwrap_or("STDIN").to_owned(),
235251 };
236252
237253 writeln!(
@@ -266,8 +282,11 @@ impl<'a> Printer for InteractivePrinter<'a> {
266282 }
267283
268284 let (prefix, name) = match file {
269− InputFile::Ordinary(filename) => ("File: ", filename.to_string_lossy()),
270− _ => ("", Cow::from("STDIN")),
285+ InputFile::Ordinary(filename) => (
286+ "File: ",
287+ Cow::from(file_name.unwrap_or(&filename.to_string_lossy()).to_owned()),
288+ ),
289+ _ => ("File: ", Cow::from(file_name.unwrap_or("STDIN").to_owned())),
271290 };
272291
273292 let mode = match self.content_type {
tests/examples/test.binarynew file

Binary file not shown.

tests/integration_tests.rs+86
@@ -541,3 +541,89 @@ fn empty_file_leads_to_empty_output_with_grid_enabled() {
541541 .success()
542542 .stdout("");
543543}
544+
545+#[test]
546+fn filename_basic() {
547+ bat()
548+ .arg("test.txt")
549+ .arg("--decorations=always")
550+ .arg("--style=header")
551+ .arg("-r=0:0")
552+ .arg("--file-name=foo")
553+ .assert()
554+ .success()
555+ .stdout("File: foo\n")
556+ .stderr("");
557+}
558+
559+#[test]
560+fn filename_binary() {
561+ bat()
562+ .arg("test.binary")
563+ .arg("--decorations=always")
564+ .arg("--style=header")
565+ .arg("-r=0:0")
566+ .arg("--file-name=foo")
567+ .assert()
568+ .success()
569+ .stdout("File: foo <BINARY>\n")
570+ .stderr("");
571+}
572+
573+#[test]
574+fn filename_stdin() {
575+ bat()
576+ .arg("--decorations=always")
577+ .arg("--style=header")
578+ .arg("-r=0:0")
579+ .arg("-")
580+ .write_stdin("stdin\n")
581+ .arg("--file-name=foo")
582+ .assert()
583+ .success()
584+ .stdout("File: foo\n")
585+ .stderr("");
586+}
587+
588+#[test]
589+fn filename_stdin_binary() {
590+ let vec = vec![0; 1];
591+ bat_with_config()
592+ .arg("--decorations=always")
593+ .arg("--style=header")
594+ .write_stdin(vec)
595+ .arg("--file-name=foo")
596+ .assert()
597+ .success()
598+ .stdout("File: foo <BINARY>\n")
599+ .stderr("");
600+}
601+
602+#[test]
603+fn filename_multiple_ok() {
604+ bat()
605+ .arg("--decorations=always")
606+ .arg("--style=header")
607+ .arg("-r=0:0")
608+ .arg("test.txt")
609+ .arg("--file-name=foo")
610+ .arg("single-line.txt")
611+ .arg("--file-name=bar")
612+ .assert()
613+ .success()
614+ .stdout("File: foo\nFile: bar\n")
615+ .stderr("");
616+}
617+
618+#[test]
619+fn filename_multiple_err() {
620+ bat()
621+ .arg("--decorations=always")
622+ .arg("--style=header")
623+ .arg("-r=0:0")
624+ .arg("test.txt")
625+ .arg("--file-name=foo")
626+ .arg("single-line.txt")
627+ .assert()
628+ .failure();
629+}