Add InputDescription

This commit is contained in:
sharkdp 2020-04-21 22:24:47 +02:00 committed by David Peter
parent 3bacfc5184
commit f3b90ddb38
5 changed files with 139 additions and 107 deletions

View File

@ -188,11 +188,11 @@ impl HighlightingAssets {
pub(crate) fn get_syntax( pub(crate) fn get_syntax(
&self, &self,
language: Option<&str>, language: Option<&str>,
file: &Input, input: &Input,
reader: &mut InputReader, reader: &mut InputReader,
mapping: &SyntaxMapping, mapping: &SyntaxMapping,
) -> &SyntaxReference { ) -> &SyntaxReference {
let syntax = match (language, file) { let syntax = match (language, input) {
(Some(language), _) => self.syntax_set.find_syntax_by_token(language), (Some(language), _) => self.syntax_set.find_syntax_by_token(language),
(None, Input::Ordinary(ofile)) => { (None, Input::Ordinary(ofile)) => {
let path = Path::new(ofile.provided_path()); let path = Path::new(ofile.provided_path());
@ -282,12 +282,11 @@ mod tests {
} }
let input = Input::Ordinary(OrdinaryFile::from_path(file_path.as_os_str())); let input = Input::Ordinary(OrdinaryFile::from_path(file_path.as_os_str()));
let syntax = self.assets.get_syntax( let stdin = io::stdin();
None, let mut reader = input.get_reader(stdin.lock()).unwrap();
&input, let syntax = self
&mut input.get_reader(io::stdin().lock()).unwrap(), .assets
&self.syntax_mapping, .get_syntax(None, &input, &mut reader, &self.syntax_mapping);
);
syntax.name.clone() syntax.name.clone()
} }

View File

@ -5,7 +5,7 @@ use crate::config::Config;
#[cfg(feature = "paging")] #[cfg(feature = "paging")]
use crate::config::PagingMode; use crate::config::PagingMode;
use crate::errors::*; use crate::errors::*;
use crate::input::{Input, InputReader}; use crate::input::{Input, InputDescription, InputReader};
use crate::line_range::{LineRanges, RangeCheckResult}; use crate::line_range::{LineRanges, RangeCheckResult};
use crate::output::OutputType; use crate::output::OutputType;
use crate::printer::{InteractivePrinter, Printer, SimplePrinter}; use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
@ -61,6 +61,8 @@ impl<'b> Controller<'b> {
let mut no_errors: bool = true; let mut no_errors: bool = true;
for input in inputs.into_iter() { for input in inputs.into_iter() {
let description = input.description();
match input.get_reader(io::stdin().lock()) { match input.get_reader(io::stdin().lock()) {
Err(error) => { Err(error) => {
handle_error(&error); handle_error(&error);
@ -69,7 +71,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) self.print_file(reader, &mut printer, writer, &description)
} else { } else {
let mut printer = InteractivePrinter::new( let mut printer = InteractivePrinter::new(
&self.config, &self.config,
@ -77,7 +79,7 @@ impl<'b> Controller<'b> {
&input, &input,
&mut reader, &mut reader,
); );
self.print_file(reader, &mut printer, writer, &input) self.print_file(reader, &mut printer, writer, &description)
}; };
if let Err(error) = result { if let Err(error) = result {
@ -96,10 +98,10 @@ impl<'b> Controller<'b> {
reader: InputReader, reader: InputReader,
printer: &mut P, printer: &mut P,
writer: &mut dyn Write, writer: &mut dyn Write,
input: &Input, input_description: &InputDescription,
) -> 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)?; printer.print_header(writer, input_description)?;
} }
if !reader.first_line.is_empty() { if !reader.first_line.is_empty() {

View File

@ -7,6 +7,104 @@ use content_inspector::{self, ContentType};
use crate::errors::*; use crate::errors::*;
const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs"); const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
#[derive(Debug, Clone, PartialEq)]
pub struct OrdinaryFile {
path: OsString,
user_provided_path: Option<OsString>,
}
impl OrdinaryFile {
pub fn from_path(path: &OsStr) -> OrdinaryFile {
OrdinaryFile {
path: path.to_os_string(),
user_provided_path: None,
}
}
pub fn set_provided_path(&mut self, user_provided_path: &OsStr) {
self.user_provided_path = Some(user_provided_path.to_os_string());
}
pub(crate) fn provided_path<'a>(&'a self) -> &'a OsStr {
self.user_provided_path
.as_ref()
.unwrap_or_else(|| &self.path)
}
}
#[derive(Debug, Clone)]
pub struct InputDescription {
pub full: String,
pub prefix: String,
pub name: String,
}
pub enum Input {
StdIn(Option<OsString>),
Ordinary(OrdinaryFile),
FromReader(Box<dyn Read>, Option<OsString>),
ThemePreviewFile,
}
impl Input {
pub(crate) fn get_reader<'a, R: BufRead + 'a>(&self, stdin: R) -> Result<InputReader<'a>> {
match self {
Input::StdIn(_) => Ok(InputReader::new(stdin)),
Input::Ordinary(ofile) => {
let file = File::open(&ofile.path)
.map_err(|e| format!("'{}': {}", ofile.path.to_string_lossy(), e))?;
if file.metadata()?.is_dir() {
return Err(
format!("'{}' is a directory.", ofile.path.to_string_lossy()).into(),
);
}
Ok(InputReader::new(BufReader::new(file)))
}
Input::ThemePreviewFile => Ok(InputReader::new(THEME_PREVIEW_FILE)),
Input::FromReader(_, _) => unimplemented!(), //Ok(InputReader::new(BufReader::new(reader))),
}
}
pub(crate) fn description(&self) -> InputDescription {
match self {
Input::Ordinary(ofile) => InputDescription {
full: format!("file '{}'", &ofile.provided_path().to_string_lossy()),
prefix: "File: ".to_owned(),
name: ofile.provided_path().to_string_lossy().into_owned(),
},
Input::StdIn(Some(name)) => InputDescription {
full: format!(
"STDIN (with name '{}')",
name.to_string_lossy().into_owned()
),
prefix: "File: ".to_owned(),
name: name.to_string_lossy().into_owned(),
},
Input::StdIn(None) => InputDescription {
full: "STDIN".to_owned(),
prefix: "".to_owned(),
name: "STDIN".to_owned(),
},
Input::ThemePreviewFile => InputDescription {
full: "".to_owned(),
prefix: "".to_owned(),
name: "".to_owned(),
},
Input::FromReader(_, Some(name)) => InputDescription {
full: format!("file '{}'", name.to_string_lossy()),
prefix: "File: ".to_owned(),
name: name.to_string_lossy().into_owned(),
},
Input::FromReader(_, None) => InputDescription {
full: "reader".to_owned(),
prefix: "".to_owned(),
name: "READER".into(),
},
}
}
}
pub struct InputReader<'a> { pub struct InputReader<'a> {
inner: Box<dyn BufRead + 'a>, inner: Box<dyn BufRead + 'a>,
@ -52,60 +150,6 @@ impl<'a> InputReader<'a> {
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub struct OrdinaryFile {
path: OsString,
user_provided_path: Option<OsString>,
}
impl OrdinaryFile {
pub fn from_path(path: &OsStr) -> OrdinaryFile {
OrdinaryFile {
path: path.to_os_string(),
user_provided_path: None,
}
}
pub fn set_provided_path(&mut self, user_provided_path: &OsStr) {
self.user_provided_path = Some(user_provided_path.to_os_string());
}
pub(crate) fn provided_path<'a>(&'a self) -> &'a OsStr {
self.user_provided_path
.as_ref()
.unwrap_or_else(|| &self.path)
}
}
pub enum Input {
StdIn(Option<OsString>),
Ordinary(OrdinaryFile),
FromReader(Box<dyn Read>, Option<OsString>),
ThemePreviewFile,
}
impl Input {
pub(crate) fn get_reader<'a, R: BufRead + 'a>(&self, stdin: R) -> Result<InputReader<'a>> {
match self {
Input::StdIn(_) => Ok(InputReader::new(stdin)),
Input::Ordinary(ofile) => {
let file = File::open(&ofile.path)
.map_err(|e| format!("'{}': {}", ofile.path.to_string_lossy(), e))?;
if file.metadata()?.is_dir() {
return Err(
format!("'{}' is a directory.", ofile.path.to_string_lossy()).into(),
);
}
Ok(InputReader::new(BufReader::new(file)))
}
Input::ThemePreviewFile => Ok(InputReader::new(THEME_PREVIEW_FILE)),
Input::FromReader(_, _) => unimplemented!(), //Ok(InputReader::new(BufReader::new(reader))),
}
}
}
#[test] #[test]
fn basic() { fn basic() {
let content = b"#!/bin/bash\necho hello"; let content = b"#!/bin/bash\necho hello";

View File

@ -1,4 +1,5 @@
use std::ffi::OsStr; use std::ffi::OsStr;
use std::io::Read;
use crate::{ use crate::{
config::{ config::{
@ -52,6 +53,18 @@ impl<'a> PrettyPrinter<'a> {
self self
} }
/// Add STDIN as an input
pub fn input_stdin(&mut self) -> &mut Self {
self.inputs.push(Input::StdIn(None));
self
}
/// Add STDIN as an input
pub fn input_reader(&mut self, reader: impl Read) -> &mut Self {
//self.inputs.push(Input::FromReader(Box::new(reader), None));
self
}
/// Specify the syntax file which should be used (default: auto-detect) /// Specify the syntax file which should be used (default: auto-detect)
pub fn language(&mut self, language: &'a str) -> &mut Self { pub fn language(&mut self, language: &'a str) -> &mut Self {
self.config.language = Some(language); self.config.language = Some(language);

View File

@ -27,14 +27,14 @@ use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration}
#[cfg(feature = "git")] #[cfg(feature = "git")]
use crate::diff::{get_git_diff, LineChanges}; use crate::diff::{get_git_diff, LineChanges};
use crate::errors::*; use crate::errors::*;
use crate::input::{Input, InputReader}; use crate::input::{Input, InputDescription, InputReader};
use crate::line_range::RangeCheckResult; use crate::line_range::RangeCheckResult;
use crate::preprocessor::{expand_tabs, replace_nonprintable}; use crate::preprocessor::{expand_tabs, replace_nonprintable};
use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::terminal::{as_terminal_escaped, to_ansi_color};
use crate::wrap::WrappingMode; use crate::wrap::WrappingMode;
pub trait Printer { pub trait Printer {
fn print_header(&mut self, handle: &mut dyn Write, file: &Input) -> Result<()>; fn print_header(&mut self, handle: &mut dyn Write, input: &InputDescription) -> 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 +57,7 @@ impl SimplePrinter {
} }
impl Printer for SimplePrinter { impl Printer for SimplePrinter {
fn print_header(&mut self, _handle: &mut dyn Write, _file: &Input) -> Result<()> { fn print_header(&mut self, _handle: &mut dyn Write, input: &InputDescription) -> Result<()> {
Ok(()) Ok(())
} }
@ -101,7 +101,7 @@ impl<'a> InteractivePrinter<'a> {
pub fn new( pub fn new(
config: &'a Config, config: &'a Config,
assets: &'a HighlightingAssets, assets: &'a HighlightingAssets,
file: &Input, input: &Input,
reader: &mut InputReader, reader: &mut InputReader,
) -> Self { ) -> Self {
let theme = assets.get_theme(&config.theme); let theme = assets.get_theme(&config.theme);
@ -160,14 +160,14 @@ impl<'a> InteractivePrinter<'a> {
#[cfg(feature = "git")] #[cfg(feature = "git")]
{ {
if config.style_components.changes() { if config.style_components.changes() {
if let Input::Ordinary(ofile) = file { if let Input::Ordinary(ofile) = input {
line_changes = get_git_diff(ofile.provided_path()); line_changes = get_git_diff(ofile.provided_path());
} }
} }
} }
// Determine the type of syntax for highlighting // Determine the type of syntax for highlighting
let syntax = assets.get_syntax(config.language, file, reader, &config.syntax_mapping); let syntax = assets.get_syntax(config.language, input, reader, &config.syntax_mapping);
Some(HighlightLines::new(syntax, theme)) Some(HighlightLines::new(syntax, theme))
}; };
@ -230,32 +230,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: &Input) -> Result<()> { fn print_header(
&mut self,
handle: &mut dyn Write,
description: &InputDescription,
) -> 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 {
Input::Ordinary(ofile) => {
format!("file '{}'", &ofile.provided_path().to_string_lossy())
}
Input::StdIn(Some(name)) => format!(
"STDIN (with name '{}')",
name.to_string_lossy().into_owned()
),
Input::StdIn(None) => "STDIN".to_owned(),
Input::ThemePreviewFile => "".to_owned(),
Input::FromReader(_, Some(name)) => {
format!("file '{}'", name.to_string_lossy())
}
Input::FromReader(_, None) => "READER".to_owned(),
};
writeln!( writeln!(
handle, handle,
"{}: Binary content from {} will not be printed to the terminal \ "{}: Binary content from {} will not be printed to the terminal \
(but will be present if the output of 'bat' is piped). You can use 'bat -A' \ (but will be present if the output of 'bat' is piped). You can use 'bat -A' \
to show the binary file contents.", to show the binary file contents.",
Yellow.paint("[bat warning]"), Yellow.paint("[bat warning]"),
input description.full,
)?; )?;
} else { } else {
if self.config.style_components.grid() { if self.config.style_components.grid() {
@ -280,20 +268,6 @@ impl<'a> Printer for InteractivePrinter<'a> {
write!(handle, "{}", " ".repeat(self.panel_width))?; write!(handle, "{}", " ".repeat(self.panel_width))?;
} }
let (prefix, name) = match file {
Input::Ordinary(ofile) => (
"File: ",
Cow::from(ofile.provided_path().to_string_lossy().to_owned()),
),
Input::StdIn(Some(name)) => ("File: ", Cow::from(name.to_string_lossy().to_owned())),
Input::StdIn(None) => ("File: ", Cow::from("STDIN".to_owned())),
Input::ThemePreviewFile => ("", Cow::from("")),
Input::FromReader(_, Some(name)) => {
("File: ", Cow::from(name.to_string_lossy().to_owned()))
}
Input::FromReader(_, None) => ("File: ", Cow::from("READER".to_owned())),
};
let mode = match self.content_type { let mode = match self.content_type {
Some(ContentType::BINARY) => " <BINARY>", Some(ContentType::BINARY) => " <BINARY>",
Some(ContentType::UTF_16LE) => " <UTF-16LE>", Some(ContentType::UTF_16LE) => " <UTF-16LE>",
@ -305,8 +279,8 @@ impl<'a> Printer for InteractivePrinter<'a> {
writeln!( writeln!(
handle, handle,
"{}{}{}", "{}{}{}",
prefix, description.prefix,
self.colors.filename.paint(name), self.colors.filename.paint(&description.name),
mode mode
)?; )?;