Merge pull request #871 from neuronull/fix_654_stdin_filename

Implement --file-name<name> option
This commit is contained in:
David Peter 2020-03-26 09:35:01 +01:00 committed by GitHub
commit 37b3b8730d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 150 additions and 13 deletions

View File

@ -136,6 +136,13 @@ impl App {
} }
}); });
match self.matches.values_of("file-name") {
Some(filenames) if filenames.len() != files.len() => {
return Err(format!("{} {}", filenames.len(), files.len()).into());
}
_ => {}
}
Ok(Config { Ok(Config {
true_color: is_truecolor_terminal(), true_color: is_truecolor_terminal(),
language: self.matches.value_of("language").or_else(|| { language: self.matches.value_of("language").or_else(|| {
@ -222,6 +229,10 @@ impl App {
.map(LineRanges::from) .map(LineRanges::from)
.map(|lr| HighlightedLineRanges(lr)) .map(|lr| HighlightedLineRanges(lr))
.unwrap_or_default(), .unwrap_or_default(),
filenames: self
.matches
.values_of("file-name")
.map(|values| values.collect()),
}) })
} }

View File

@ -93,6 +93,18 @@ pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
'--highlight-line 40:' highlights lines 40 to the end of the file" '--highlight-line 40:' highlights lines 40 to the end of the file"
), ),
) )
.arg(
Arg::with_name("file-name")
.long("file-name")
.takes_value(true)
.number_of_values(1)
.multiple(true)
.value_name("name")
.help("Specify the name to display for a file.")
.long_help("Specify the name to display for a file. Useful when piping \
data to bat from STDIN when bat does not otherwise know \
the filename."),
)
.arg( .arg(
Arg::with_name("tabs") Arg::with_name("tabs")
.long("tabs") .long("tabs")

View File

@ -70,6 +70,9 @@ pub struct Config<'a> {
/// Ranges of lines which should be highlighted with a special background color /// Ranges of lines which should be highlighted with a special background color
pub highlighted_lines: HighlightedLineRanges, pub highlighted_lines: HighlightedLineRanges,
/// Names of files to display when printing
pub filenames: Option<Vec<&'a str>>,
} }
#[test] #[test]

View File

@ -45,7 +45,12 @@ impl<'b> Controller<'b> {
let stdin = io::stdin(); let stdin = io::stdin();
for input_file in &self.config.files { let filenames: Box<dyn Iterator<Item = _>> = match self.config.filenames {
Some(ref filenames) => Box::new(filenames.into_iter().map(|name| Some(*name))),
None => Box::new(std::iter::repeat(None)),
};
for (input_file, file_name) in self.config.files.iter().zip(filenames) {
match input_file.get_reader(&stdin) { match input_file.get_reader(&stdin) {
Err(error) => { Err(error) => {
handle_error(&error); handle_error(&error);
@ -54,7 +59,7 @@ impl<'b> Controller<'b> {
Ok(mut reader) => { Ok(mut reader) => {
let result = if self.config.loop_through { let result = if self.config.loop_through {
let mut printer = SimplePrinter::new(); let mut printer = SimplePrinter::new();
self.print_file(reader, &mut printer, writer, *input_file) self.print_file(reader, &mut printer, writer, *input_file, file_name)
} else { } else {
let mut printer = InteractivePrinter::new( let mut printer = InteractivePrinter::new(
&self.config, &self.config,
@ -62,7 +67,7 @@ impl<'b> Controller<'b> {
*input_file, *input_file,
&mut reader, &mut reader,
); );
self.print_file(reader, &mut printer, writer, *input_file) self.print_file(reader, &mut printer, writer, *input_file, file_name)
}; };
if let Err(error) = result { if let Err(error) = result {
@ -82,9 +87,10 @@ impl<'b> Controller<'b> {
printer: &mut P, printer: &mut P,
writer: &mut dyn Write, writer: &mut dyn Write,
input_file: InputFile<'a>, input_file: InputFile<'a>,
file_name: Option<&str>,
) -> Result<()> { ) -> Result<()> {
if !reader.first_line.is_empty() || self.config.style_components.header() { if !reader.first_line.is_empty() || self.config.style_components.header() {
printer.print_header(writer, input_file)?; printer.print_header(writer, input_file, file_name)?;
} }
if !reader.first_line.is_empty() { if !reader.first_line.is_empty() {

View File

@ -34,7 +34,12 @@ use crate::terminal::{as_terminal_escaped, to_ansi_color};
use crate::wrap::OutputWrap; use crate::wrap::OutputWrap;
pub trait Printer { pub trait Printer {
fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()>; fn print_header(
&mut self,
handle: &mut dyn Write,
file: InputFile,
file_name: Option<&str>,
) -> Result<()>;
fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>; fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>;
fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>; fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>;
@ -57,7 +62,12 @@ impl SimplePrinter {
} }
impl Printer for SimplePrinter { impl Printer for SimplePrinter {
fn print_header(&mut self, _handle: &mut dyn Write, _file: InputFile) -> Result<()> { fn print_header(
&mut self,
_handle: &mut dyn Write,
_file: InputFile,
_file_name: Option<&str>,
) -> Result<()> {
Ok(()) Ok(())
} }
@ -224,14 +234,20 @@ impl<'a> InteractivePrinter<'a> {
} }
impl<'a> Printer for InteractivePrinter<'a> { impl<'a> Printer for InteractivePrinter<'a> {
fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()> { fn print_header(
&mut self,
handle: &mut dyn Write,
file: InputFile,
file_name: Option<&str>,
) -> Result<()> {
if !self.config.style_components.header() { if !self.config.style_components.header() {
if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable { if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
let input = match file { let input = match file {
InputFile::Ordinary(filename) => { InputFile::Ordinary(filename) => format!(
format!("file '{}'", filename.to_string_lossy()) "file '{}'",
} file_name.unwrap_or(&filename.to_string_lossy())
_ => "STDIN".into(), ),
_ => file_name.unwrap_or("STDIN").to_owned(),
}; };
writeln!( writeln!(
@ -266,8 +282,11 @@ impl<'a> Printer for InteractivePrinter<'a> {
} }
let (prefix, name) = match file { let (prefix, name) = match file {
InputFile::Ordinary(filename) => ("File: ", filename.to_string_lossy()), InputFile::Ordinary(filename) => (
_ => ("", Cow::from("STDIN")), "File: ",
Cow::from(file_name.unwrap_or(&filename.to_string_lossy()).to_owned()),
),
_ => ("File: ", Cow::from(file_name.unwrap_or("STDIN").to_owned())),
}; };
let mode = match self.content_type { let mode = match self.content_type {

BIN
tests/examples/test.binary vendored Normal file

Binary file not shown.

View File

@ -541,3 +541,89 @@ fn empty_file_leads_to_empty_output_with_grid_enabled() {
.success() .success()
.stdout(""); .stdout("");
} }
#[test]
fn filename_basic() {
bat()
.arg("test.txt")
.arg("--decorations=always")
.arg("--style=header")
.arg("-r=0:0")
.arg("--file-name=foo")
.assert()
.success()
.stdout("File: foo\n")
.stderr("");
}
#[test]
fn filename_binary() {
bat()
.arg("test.binary")
.arg("--decorations=always")
.arg("--style=header")
.arg("-r=0:0")
.arg("--file-name=foo")
.assert()
.success()
.stdout("File: foo <BINARY>\n")
.stderr("");
}
#[test]
fn filename_stdin() {
bat()
.arg("--decorations=always")
.arg("--style=header")
.arg("-r=0:0")
.arg("-")
.write_stdin("stdin\n")
.arg("--file-name=foo")
.assert()
.success()
.stdout("File: foo\n")
.stderr("");
}
#[test]
fn filename_stdin_binary() {
let vec = vec![0; 1];
bat_with_config()
.arg("--decorations=always")
.arg("--style=header")
.write_stdin(vec)
.arg("--file-name=foo")
.assert()
.success()
.stdout("File: foo <BINARY>\n")
.stderr("");
}
#[test]
fn filename_multiple_ok() {
bat()
.arg("--decorations=always")
.arg("--style=header")
.arg("-r=0:0")
.arg("test.txt")
.arg("--file-name=foo")
.arg("single-line.txt")
.arg("--file-name=bar")
.assert()
.success()
.stdout("File: foo\nFile: bar\n")
.stderr("");
}
#[test]
fn filename_multiple_err() {
bat()
.arg("--decorations=always")
.arg("--style=header")
.arg("-r=0:0")
.arg("test.txt")
.arg("--file-name=foo")
.arg("single-line.txt")
.assert()
.failure();
}