mirror of
				https://github.com/sharkdp/bat.git
				synced 2025-11-04 09:01:56 +00:00 
			
		
		
		
	Add InputDescription
This commit is contained in:
		@@ -188,11 +188,11 @@ impl HighlightingAssets {
 | 
			
		||||
    pub(crate) fn get_syntax(
 | 
			
		||||
        &self,
 | 
			
		||||
        language: Option<&str>,
 | 
			
		||||
        file: &Input,
 | 
			
		||||
        input: &Input,
 | 
			
		||||
        reader: &mut InputReader,
 | 
			
		||||
        mapping: &SyntaxMapping,
 | 
			
		||||
    ) -> &SyntaxReference {
 | 
			
		||||
        let syntax = match (language, file) {
 | 
			
		||||
        let syntax = match (language, input) {
 | 
			
		||||
            (Some(language), _) => self.syntax_set.find_syntax_by_token(language),
 | 
			
		||||
            (None, Input::Ordinary(ofile)) => {
 | 
			
		||||
                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 syntax = self.assets.get_syntax(
 | 
			
		||||
                None,
 | 
			
		||||
                &input,
 | 
			
		||||
                &mut input.get_reader(io::stdin().lock()).unwrap(),
 | 
			
		||||
                &self.syntax_mapping,
 | 
			
		||||
            );
 | 
			
		||||
            let stdin = io::stdin();
 | 
			
		||||
            let mut reader = input.get_reader(stdin.lock()).unwrap();
 | 
			
		||||
            let syntax = self
 | 
			
		||||
                .assets
 | 
			
		||||
                .get_syntax(None, &input, &mut reader, &self.syntax_mapping);
 | 
			
		||||
 | 
			
		||||
            syntax.name.clone()
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ use crate::config::Config;
 | 
			
		||||
#[cfg(feature = "paging")]
 | 
			
		||||
use crate::config::PagingMode;
 | 
			
		||||
use crate::errors::*;
 | 
			
		||||
use crate::input::{Input, InputReader};
 | 
			
		||||
use crate::input::{Input, InputDescription, InputReader};
 | 
			
		||||
use crate::line_range::{LineRanges, RangeCheckResult};
 | 
			
		||||
use crate::output::OutputType;
 | 
			
		||||
use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
 | 
			
		||||
@@ -61,6 +61,8 @@ impl<'b> Controller<'b> {
 | 
			
		||||
        let mut no_errors: bool = true;
 | 
			
		||||
 | 
			
		||||
        for input in inputs.into_iter() {
 | 
			
		||||
            let description = input.description();
 | 
			
		||||
 | 
			
		||||
            match input.get_reader(io::stdin().lock()) {
 | 
			
		||||
                Err(error) => {
 | 
			
		||||
                    handle_error(&error);
 | 
			
		||||
@@ -69,7 +71,7 @@ impl<'b> Controller<'b> {
 | 
			
		||||
                Ok(mut reader) => {
 | 
			
		||||
                    let result = if self.config.loop_through {
 | 
			
		||||
                        let mut printer = SimplePrinter::new();
 | 
			
		||||
                        self.print_file(reader, &mut printer, writer, &input)
 | 
			
		||||
                        self.print_file(reader, &mut printer, writer, &description)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        let mut printer = InteractivePrinter::new(
 | 
			
		||||
                            &self.config,
 | 
			
		||||
@@ -77,7 +79,7 @@ impl<'b> Controller<'b> {
 | 
			
		||||
                            &input,
 | 
			
		||||
                            &mut reader,
 | 
			
		||||
                        );
 | 
			
		||||
                        self.print_file(reader, &mut printer, writer, &input)
 | 
			
		||||
                        self.print_file(reader, &mut printer, writer, &description)
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    if let Err(error) = result {
 | 
			
		||||
@@ -96,10 +98,10 @@ impl<'b> Controller<'b> {
 | 
			
		||||
        reader: InputReader,
 | 
			
		||||
        printer: &mut P,
 | 
			
		||||
        writer: &mut dyn Write,
 | 
			
		||||
        input: &Input,
 | 
			
		||||
        input_description: &InputDescription,
 | 
			
		||||
    ) -> Result<()> {
 | 
			
		||||
        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() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										152
									
								
								src/input.rs
									
									
									
									
									
								
							
							
						
						
									
										152
									
								
								src/input.rs
									
									
									
									
									
								
							@@ -7,6 +7,104 @@ use content_inspector::{self, ContentType};
 | 
			
		||||
use crate::errors::*;
 | 
			
		||||
 | 
			
		||||
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> {
 | 
			
		||||
    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]
 | 
			
		||||
fn basic() {
 | 
			
		||||
    let content = b"#!/bin/bash\necho hello";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
use std::ffi::OsStr;
 | 
			
		||||
use std::io::Read;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    config::{
 | 
			
		||||
@@ -52,6 +53,18 @@ impl<'a> PrettyPrinter<'a> {
 | 
			
		||||
        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)
 | 
			
		||||
    pub fn language(&mut self, language: &'a str) -> &mut Self {
 | 
			
		||||
        self.config.language = Some(language);
 | 
			
		||||
 
 | 
			
		||||
@@ -27,14 +27,14 @@ use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration}
 | 
			
		||||
#[cfg(feature = "git")]
 | 
			
		||||
use crate::diff::{get_git_diff, LineChanges};
 | 
			
		||||
use crate::errors::*;
 | 
			
		||||
use crate::input::{Input, InputReader};
 | 
			
		||||
use crate::input::{Input, InputDescription, InputReader};
 | 
			
		||||
use crate::line_range::RangeCheckResult;
 | 
			
		||||
use crate::preprocessor::{expand_tabs, replace_nonprintable};
 | 
			
		||||
use crate::terminal::{as_terminal_escaped, to_ansi_color};
 | 
			
		||||
use crate::wrap::WrappingMode;
 | 
			
		||||
 | 
			
		||||
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_snip(&mut self, handle: &mut dyn Write) -> Result<()>;
 | 
			
		||||
@@ -57,7 +57,7 @@ impl 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(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -101,7 +101,7 @@ impl<'a> InteractivePrinter<'a> {
 | 
			
		||||
    pub fn new(
 | 
			
		||||
        config: &'a Config,
 | 
			
		||||
        assets: &'a HighlightingAssets,
 | 
			
		||||
        file: &Input,
 | 
			
		||||
        input: &Input,
 | 
			
		||||
        reader: &mut InputReader,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        let theme = assets.get_theme(&config.theme);
 | 
			
		||||
@@ -160,14 +160,14 @@ impl<'a> InteractivePrinter<'a> {
 | 
			
		||||
            #[cfg(feature = "git")]
 | 
			
		||||
            {
 | 
			
		||||
                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());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // 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))
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -230,32 +230,20 @@ impl<'a> 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 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!(
 | 
			
		||||
                    handle,
 | 
			
		||||
                    "{}: 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' \
 | 
			
		||||
                     to show the binary file contents.",
 | 
			
		||||
                    Yellow.paint("[bat warning]"),
 | 
			
		||||
                    input
 | 
			
		||||
                    description.full,
 | 
			
		||||
                )?;
 | 
			
		||||
            } else {
 | 
			
		||||
                if self.config.style_components.grid() {
 | 
			
		||||
@@ -280,20 +268,6 @@ impl<'a> Printer for InteractivePrinter<'a> {
 | 
			
		||||
            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 {
 | 
			
		||||
            Some(ContentType::BINARY) => "   <BINARY>",
 | 
			
		||||
            Some(ContentType::UTF_16LE) => "   <UTF-16LE>",
 | 
			
		||||
@@ -305,8 +279,8 @@ impl<'a> Printer for InteractivePrinter<'a> {
 | 
			
		||||
        writeln!(
 | 
			
		||||
            handle,
 | 
			
		||||
            "{}{}{}",
 | 
			
		||||
            prefix,
 | 
			
		||||
            self.colors.filename.paint(name),
 | 
			
		||||
            description.prefix,
 | 
			
		||||
            self.colors.filename.paint(&description.name),
 | 
			
		||||
            mode
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user