diff --git a/src/bin/bat/app.rs b/src/bin/bat/app.rs index b924ab29..7b514901 100644 --- a/src/bin/bat/app.rs +++ b/src/bin/bat/app.rs @@ -257,7 +257,7 @@ impl App { let files: Option> = self.matches.values_of_os("FILE").map(|vs| vs.collect()); if files.is_none() { - let input = Input::stdin().with_name(filenames_or_none.next().unwrap_or(None)); + let input = Input::stdin().as_file(filenames_or_none.next().unwrap_or(None)); return Ok(vec![input]); } let files_or_none: Box> = match files { @@ -269,9 +269,9 @@ impl App { for (filepath, provided_name) in files_or_none.zip(filenames_or_none) { if let Some(filepath) = filepath { if filepath.to_str().unwrap_or_default() == "-" { - file_input.push(Input::stdin().with_name(provided_name)); + file_input.push(Input::stdin().as_file(provided_name)); } else { - file_input.push(Input::ordinary_file(filepath).with_name(provided_name)); + file_input.push(Input::ordinary_file(filepath).as_file(provided_name)); } } } diff --git a/src/bin/bat/main.rs b/src/bin/bat/main.rs index 422fffdf..d28964d5 100644 --- a/src/bin/bat/main.rs +++ b/src/bin/bat/main.rs @@ -25,7 +25,6 @@ use assets::{assets_from_cache_or_binary, cache_dir, clear_assets, config_dir}; use clap::crate_version; use directories::PROJECT_DIRS; -use bat::input::InputDescription; use bat::{ assets::HighlightingAssets, config::Config, @@ -124,7 +123,6 @@ pub fn list_languages(config: &Config) -> Result<()> { fn theme_preview_file<'a>() -> Input<'a> { Input::from_reader(Box::new(BufReader::new(THEME_PREVIEW_DATA))) .with_name(Some("theme.rs".as_ref())) - .with_description(Some(InputDescription::new(""))) } pub fn list_themes(cfg: &Config) -> Result<()> { diff --git a/src/input.rs b/src/input.rs index 38300946..55fbbf03 100644 --- a/src/input.rs +++ b/src/input.rs @@ -10,39 +10,48 @@ use crate::error::*; /// This tells bat how to refer to the input. #[derive(Clone)] pub struct InputDescription { - name: String, + pub(crate) name: String, + + /// The input title. + /// This replaces the name if provided. + title: Option, + + /// The input kind. kind: Option, + + /// A summary description of the input. + /// Defaults to "{kind} '{name}'" summary: Option, } impl InputDescription { /// Creates a description for an input. - /// - /// The name should describe where the input came from (e.g. "README.md") pub fn new(name: impl Into) -> Self { InputDescription { name: name.into(), + title: None, kind: None, summary: None, } } - /// A description for the type of input (e.g. "File") - pub fn with_kind(mut self, kind: Option>) -> Self { - self.kind = kind.map(|kind| kind.into()); - self + pub fn set_kind(&mut self, kind: Option) -> () { + self.kind = kind; } - /// A summary description of the input. - /// - /// Defaults to "{kind} '{name}'" - pub fn with_summary(mut self, summary: Option>) -> Self { - self.summary = summary.map(|summary| summary.into()); - self + pub fn set_summary(&mut self, summary: Option) -> () { + self.summary = summary; } - pub fn name(&self) -> &String { - &self.name + pub fn set_title(&mut self, title: Option) -> () { + self.title = title; + } + + pub fn title(&self) -> &String { + match self.title.as_ref() { + Some(ref title) => title, + None => &self.name, + } } pub fn kind(&self) -> Option<&String> { @@ -66,9 +75,7 @@ pub(crate) enum InputKind<'a> { impl<'a> InputKind<'a> { pub fn description(&self) -> InputDescription { match self { - InputKind::OrdinaryFile(ref path) => { - InputDescription::new(path.to_string_lossy()).with_kind(Some("File")) - } + InputKind::OrdinaryFile(ref path) => InputDescription::new(path.to_string_lossy()), InputKind::StdIn => InputDescription::new("STDIN"), InputKind::CustomReader(_) => InputDescription::new("READER"), } @@ -83,7 +90,7 @@ pub(crate) struct InputMetadata { pub struct Input<'a> { pub(crate) kind: InputKind<'a>, pub(crate) metadata: InputMetadata, - pub(crate) description: Option, + pub(crate) description: InputDescription, } pub(crate) enum OpenedInputKind { @@ -101,26 +108,29 @@ pub(crate) struct OpenedInput<'a> { impl<'a> Input<'a> { pub fn ordinary_file(path: &OsStr) -> Self { + let kind = InputKind::OrdinaryFile(path.to_os_string()); Input { - kind: InputKind::OrdinaryFile(path.to_os_string()), + description: kind.description(), metadata: InputMetadata::default(), - description: None, + kind, } } pub fn stdin() -> Self { + let kind = InputKind::StdIn; Input { - kind: InputKind::StdIn, + description: kind.description(), metadata: InputMetadata::default(), - description: None, + kind, } } pub fn from_reader(reader: Box) -> Self { + let kind = InputKind::CustomReader(reader); Input { - kind: InputKind::CustomReader(reader), + description: kind.description(), metadata: InputMetadata::default(), - description: None, + kind, } } @@ -133,23 +143,26 @@ impl<'a> Input<'a> { } pub fn with_name(mut self, provided_name: Option<&OsStr>) -> Self { + match provided_name { + Some(name) => self.description.name = name.to_string_lossy().to_string(), + None => {} + } + self.metadata.user_provided_name = provided_name.map(|n| n.to_owned()); self } - pub fn with_description(mut self, description: Option) -> Self { - self.description = description; - self + pub fn as_file(mut self, provided_name: Option<&OsStr>) -> Self { + self.description.kind = Some("File".to_owned()); + self.with_name(provided_name) } - pub fn description(&self) -> InputDescription { - if let Some(ref description) = self.description { - description.clone() - } else if let Some(ref name) = self.metadata.user_provided_name { - InputDescription::new(name.to_string_lossy()).with_kind(Some("File")) - } else { - self.kind.description() - } + pub fn description(&self) -> &InputDescription { + &self.description + } + + pub fn description_mut(&mut self) -> &mut InputDescription { + &mut self.description } pub(crate) fn open(self, stdin: R) -> Result> { diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index 6dedf8a4..2a1c98ab 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -9,7 +9,7 @@ use crate::{ config::{Config, VisibleLines}, controller::Controller, error::Result, - input::Input, + input::Input as BatInput, line_range::{HighlightedLineRanges, LineRange, LineRanges}, style::{StyleComponent, StyleComponents}, SyntaxMapping, WrappingMode, @@ -61,10 +61,17 @@ impl<'a> PrettyPrinter<'a> { self } + /// Adds multiple inputs which should be pretty-printed + pub fn inputs(&mut self, inputs: impl IntoIterator>) -> &mut Self { + for input in inputs { + self.inputs.push(input); + } + self + } + /// Add a file which should be pretty-printed pub fn input_file(&mut self, path: impl AsRef) -> &mut Self { - self.inputs.push(Input::ordinary_file(path.as_ref())); - self + self.input(Input::from_file(path).kind("File")) } /// Add multiple files which should be pretty-printed @@ -73,22 +80,20 @@ impl<'a> PrettyPrinter<'a> { I: IntoIterator, P: AsRef, { - for path in paths { - self.inputs.push(Input::ordinary_file(path.as_ref())); - } - self + self.inputs(paths.into_iter().map(Input::from_file)) } /// Add STDIN as an input pub fn input_stdin(&mut self) -> &mut Self { - self.inputs.push(Input::stdin()); + self.inputs.push(Input::from_stdin()); self } /// Add STDIN as an input (with customized name) + #[deprecated] pub fn input_stdin_with_name(&mut self, name: impl AsRef) -> &mut Self { self.inputs - .push(Input::stdin().with_name(Some(name.as_ref()))); + .push(Input::from_stdin().name(name).kind("File")); self } @@ -98,6 +103,8 @@ impl<'a> PrettyPrinter<'a> { } /// Add a byte string as an input (with customized name) + #[deprecated] + #[allow(deprecated)] pub fn input_from_bytes_with_name( &mut self, content: &'a [u8], @@ -108,18 +115,19 @@ impl<'a> PrettyPrinter<'a> { /// Add a custom reader as an input pub fn input_from_reader(&mut self, reader: R) -> &mut Self { - self.inputs.push(Input::from_reader(Box::new(reader))); + self.inputs.push(Input::from_reader(reader)); self } /// Add a custom reader as an input (with customized name) + #[deprecated] pub fn input_from_reader_with_name( &mut self, reader: R, name: impl AsRef, ) -> &mut Self { self.inputs - .push(Input::from_reader(Box::new(reader)).with_name(Some(name.as_ref()))); + .push(Input::from_reader(reader).name(name).kind("File")); self } @@ -292,10 +300,74 @@ impl<'a> PrettyPrinter<'a> { } self.config.style_components = StyleComponents::new(&style_components); + // Collect the inputs to print let mut inputs: Vec = vec![]; std::mem::swap(&mut inputs, &mut self.inputs); + // Run the controller let controller = Controller::new(&self.config, &self.assets); - controller.run(inputs) + controller.run(inputs.into_iter().map(|i| i.into()).collect()) + } +} + +pub struct Input<'a> { + input: BatInput<'a>, +} + +impl<'a> Input<'a> { + /// A new input from a reader. + pub fn from_reader(reader: R) -> Self { + BatInput::from_reader(Box::new(reader)).into() + } + + /// A new input from a file. + pub fn from_file(path: impl AsRef) -> Self { + BatInput::ordinary_file(path.as_ref()).into() + } + + /// A new input from bytes. + pub fn from_bytes(bytes: &'a [u8]) -> Self { + Input::from_reader(bytes).into() + } + + /// A new input from STDIN. + pub fn from_stdin() -> Self { + BatInput::stdin().into() + } + + /// The filename of the input. + /// This affects syntax detection and changes the default header title. + pub fn name(mut self, name: impl AsRef) -> Self { + self.input = self.input.with_name(Some(name.as_ref())); + self + } + + /// The description for the type of input (e.g. "File") + /// This defaults to "File" for files, and nothing for other inputs. + pub fn kind(mut self, kind: impl Into) -> Self { + let kind = kind.into(); + self.input + .description_mut() + .set_kind(if kind.is_empty() { None } else { Some(kind) }); + self + } + + /// The title for the input (e.g. "http://example.com/example.txt") + /// This defaults to the file name. + pub fn title(mut self, title: impl Into) -> Self { + self.input.description_mut().set_title(Some(title.into())); + self + } +} + +impl<'a> Into> for BatInput<'a> { + fn into(self) -> Input<'a> { + Input { input: self } + } +} + +impl<'a> Into> for Input<'a> { + fn into(self) -> BatInput<'a> { + self.input } } diff --git a/src/printer.rs b/src/printer.rs index 1d8ef901..addc7972 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -292,7 +292,7 @@ impl<'a> Printer for InteractivePrinter<'a> { .kind() .map(|kind| format!("{}: ", kind)) .unwrap_or("".into()), - self.colors.filename.paint(description.name()), + self.colors.filename.paint(description.title()), mode )?;