on master help sign in
Completely refactor 'input' module
commit590960f7f5b5454c2da8c36409a8d57a58b2980c authorsharkdp <davidpeter@web.de> dateWed 22 Apr 2020, 16:27 (7 years ago) parentb4d5410
9 files changed+233−200
src/assets.rs+46−40 src/bin/bat/app.rs+16−21 src/bin/bat/main.rs+4−5 src/config.rs−2 src/controller.rs+24−28 src/input.rs+118−77 src/lib.rs+1−1 src/pretty_printer.rs+7−7 src/printer.rs+17−19
src/assets.rs+46−40
@@ -10,7 +10,7 @@ use syntect::parsing::{SyntaxReference, SyntaxSet, SyntaxSetBuilder};
1010
1111use crate::assets_metadata::AssetsMetadata;
1212use crate::errors::*;
13−use crate::input::{Input, InputReader};
13+use crate::input::{Input, InputKind, InputReader, OpenedInput, OpenedInputKind};
1414use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
1515
1616#[derive(Debug)]
@@ -188,38 +188,46 @@ impl HighlightingAssets {
188188 pub(crate) fn get_syntax(
189189 &self,
190190 language: Option<&str>,
191− input: &Input,
192− reader: &mut InputReader,
191+ input: &mut OpenedInput,
193192 mapping: &SyntaxMapping,
194193 ) -> &SyntaxReference {
195− let syntax = match (language, input) {
196− (Some(language), _) => self.syntax_set.find_syntax_by_token(language),
197− (None, Input::Ordinary(ofile)) => {
198− let path = Path::new(ofile.provided_path());
199− let line_syntax = self.get_first_line_syntax(reader);
200−
201− let absolute_path = path.canonicalize().ok().unwrap_or(path.to_owned());
202− match mapping.get_syntax_for(absolute_path) {
203− Some(MappingTarget::MapTo(syntax_name)) => {
204− // TODO: we should probably return an error here if this syntax can not be
205− // found. Currently, we just fall back to 'plain'.
206− self.syntax_set.find_syntax_by_name(syntax_name)
194+ let syntax = if let Some(language) = language {
195+ self.syntax_set.find_syntax_by_token(language)
196+ } else {
197+ match input.kind {
198+ OpenedInputKind::OrdinaryFile(ref actual_path) => {
199+ let path_str = input
200+ .metadata
201+ .user_provided_name
202+ .as_ref()
203+ .unwrap_or(actual_path);
204+ let path = Path::new(path_str);
205+ let line_syntax = self.get_first_line_syntax(&mut input.reader);
206+
207+ let absolute_path = path.canonicalize().ok().unwrap_or(path.to_owned());
208+ match mapping.get_syntax_for(absolute_path) {
209+ Some(MappingTarget::MapTo(syntax_name)) => {
210+ // TODO: we should probably return an error here if this syntax can not be
211+ // found. Currently, we just fall back to 'plain'.
212+ self.syntax_set.find_syntax_by_name(syntax_name)
213+ }
214+ Some(MappingTarget::MapToUnknown) => line_syntax,
215+ None => {
216+ let file_name = path.file_name().unwrap_or_default();
217+ self.get_extension_syntax(file_name).or(line_syntax)
218+ }
207219 }
208− Some(MappingTarget::MapToUnknown) => line_syntax,
209− None => {
210− let file_name = path.file_name().unwrap_or_default();
211− self.get_extension_syntax(file_name).or(line_syntax)
220+ }
221+ OpenedInputKind::StdIn | OpenedInputKind::CustomReader => {
222+ if let Some(ref name) = input.metadata.user_provided_name {
223+ self.get_extension_syntax(&name)
224+ .or(self.get_first_line_syntax(&mut input.reader))
225+ } else {
226+ self.get_first_line_syntax(&mut input.reader)
212227 }
213228 }
229+ OpenedInputKind::ThemePreviewFile => self.syntax_set.find_syntax_by_name("Rust"),
214230 }
215− (None, Input::StdIn(None)) => String::from_utf8(reader.first_line.clone())
216− .ok()
217− .and_then(|l| self.syntax_set.find_syntax_by_first_line(&l)),
218− (None, Input::StdIn(Some(file_name))) => self
219− .get_extension_syntax(&file_name)
220− .or(self.get_first_line_syntax(reader)),
221− (_, Input::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
222− (None, Input::FromReader(_, _)) => unimplemented!(),
223231 };
224232
225233 syntax.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
@@ -249,8 +257,6 @@ impl HighlightingAssets {
249257mod tests {
250258 use super::*;
251259
252− use crate::input::OrdinaryFile;
253−
254260 use std::ffi::{OsStr, OsString};
255261 use std::fs::File;
256262 use std::io;
@@ -281,12 +287,12 @@ mod tests {
281287 writeln!(temp_file, "{}", first_line).unwrap();
282288 }
283289
284− let input = Input::Ordinary(OrdinaryFile::from_path(file_path.as_os_str()));
285− let stdin = io::stdin();
286− let mut reader = input.get_reader(stdin.lock()).unwrap();
290+ let input: Input = Input::ordinary_file(file_path.as_os_str());
291+ let dummy_stdin: &[u8] = &[];
292+ let mut opened_input = input.open(dummy_stdin).unwrap();
287293 let syntax = self
288294 .assets
289− .get_syntax(None, &input, &mut reader, &self.syntax_mapping);
295+ .get_syntax(None, &mut opened_input, &self.syntax_mapping);
290296
291297 syntax.name.clone()
292298 }
@@ -304,13 +310,13 @@ mod tests {
304310 }
305311
306312 fn syntax_for_stdin_with_content(&self, file_name: &str, content: &[u8]) -> String {
307− let input = Input::StdIn(Some(OsString::from(file_name)));
308− let syntax = self.assets.get_syntax(
309− None,
310− &input,
311− &mut input.get_reader(content).unwrap(),
312− &self.syntax_mapping,
313− );
313+ let mut input = Input::stdin();
314+ input.set_provided_name(Some(OsStr::new(file_name)));
315+ let mut opened_input = input.open(content).unwrap();
316+
317+ let syntax = self
318+ .assets
319+ .get_syntax(None, &mut opened_input, &self.syntax_mapping);
314320 syntax.name.clone()
315321 }
316322 }
src/bin/bat/app.rs+16−21
@@ -15,10 +15,11 @@ use console::Term;
1515
1616use bat::{
1717 config::{
18− Config, HighlightedLineRanges, Input, LineRange, LineRanges, MappingTarget, OrdinaryFile,
19− PagingMode, StyleComponent, StyleComponents, SyntaxMapping, WrappingMode,
18+ Config, HighlightedLineRanges, LineRange, LineRanges, MappingTarget, PagingMode,
19+ StyleComponent, StyleComponents, SyntaxMapping, WrappingMode,
2020 },
2121 errors::*,
22+ input::Input,
2223 HighlightingAssets,
2324};
2425
@@ -83,13 +84,7 @@ impl App {
8384 if self.matches.occurrences_of("plain") > 1 {
8485 // If we have -pp as an option when in auto mode, the pager should be disabled.
8586 PagingMode::Never
86− } else if inputs.iter().any(|f| {
87− if let Input::StdIn(None) = f {
88− true
89− } else {
90− false
91− }
92− }) {
87+ } else if inputs.iter().any(Input::is_stdin) {
9388 // If we are reading from stdin, only enable paging if we write to an
9489 // interactive terminal and if we do not *read* from an interactive
9590 // terminal.
@@ -251,9 +246,9 @@ impl App {
251246 let files: Option<Vec<&OsStr>> = self.matches.values_of_os("FILE").map(|vs| vs.collect());
252247
253248 if files.is_none() {
254− return Ok(vec![Input::StdIn(
255− filenames_or_none.nth(0).unwrap().map(|f| f.to_owned()),
256− )]);
249+ let mut input = Input::stdin();
250+ input.set_provided_name(filenames_or_none.nth(0).unwrap_or(None));
251+ return Ok(vec![input]);
257252 }
258253 let files_or_none: Box<dyn Iterator<Item = _>> = match files {
259254 Some(ref files) => Box::new(files.into_iter().map(|name| Some(*name))),
@@ -261,16 +256,16 @@ impl App {
261256 };
262257
263258 let mut file_input = Vec::new();
264− for (input, name) in files_or_none.zip(filenames_or_none) {
265− if let Some(input) = input {
266− if input.to_str().unwrap_or_default() == "-" {
267− file_input.push(Input::StdIn(name.map(|n| n.to_owned())));
259+ for (filepath, provided_name) in files_or_none.zip(filenames_or_none) {
260+ if let Some(filepath) = filepath {
261+ if filepath.to_str().unwrap_or_default() == "-" {
262+ let mut input = Input::stdin();
263+ input.set_provided_name(provided_name);
264+ file_input.push(input);
268265 } else {
269− let mut ofile = OrdinaryFile::from_path(input);
270− if let Some(path) = name {
271− ofile.set_provided_path(path);
272− }
273− file_input.push(Input::Ordinary(ofile))
266+ let mut input = Input::ordinary_file(filepath);
267+ input.set_provided_name(provided_name);
268+ file_input.push(input);
274269 }
275270 }
276271 }
src/bin/bat/main.rs+4−5
@@ -26,8 +26,9 @@ use clap::crate_version;
2626use directories::PROJECT_DIRS;
2727
2828use bat::{
29− config::{Config, Input, OrdinaryFile, StyleComponent, StyleComponents},
29+ config::{Config, StyleComponent, StyleComponents},
3030 errors::*,
31+ input::Input,
3132 Controller, HighlightingAssets,
3233};
3334
@@ -134,7 +135,7 @@ pub fn list_themes(cfg: &Config) -> Result<()> {
134135 )?;
135136 config.theme = theme.to_string();
136137 Controller::new(&config, &assets)
137− .run(vec![Input::ThemePreviewFile])
138+ .run(vec![Input::theme_preview_file()])
138139 .ok();
139140 writeln!(stdout)?;
140141 }
@@ -167,9 +168,7 @@ fn run() -> Result<bool> {
167168 run_cache_subcommand(cache_matches)?;
168169 Ok(true)
169170 } else {
170− let inputs = vec![Input::Ordinary(OrdinaryFile::from_path(OsStr::new(
171− "cache",
172− )))];
171+ let inputs = vec![Input::ordinary_file(OsStr::new("cache"))];
173172 let config = app.config(&inputs)?;
174173
175174 run_controller(inputs, &config)
src/config.rs−2
@@ -1,5 +1,3 @@
1−pub use crate::input::Input;
2−pub use crate::input::OrdinaryFile;
31pub use crate::line_range::{HighlightedLineRanges, LineRange, LineRanges};
42pub use crate::style::{StyleComponent, StyleComponents};
53pub use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
src/controller.rs+24−28
@@ -5,7 +5,7 @@ use crate::config::Config;
55#[cfg(feature = "paging")]
66use crate::config::PagingMode;
77use crate::errors::*;
8−use crate::input::{Input, InputDescription, InputReader};
8+use crate::input::{Input, InputDescription, InputKind, InputReader, OpenedInput};
99use crate::line_range::{LineRanges, RangeCheckResult};
1010use crate::output::OutputType;
1111use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
@@ -38,9 +38,9 @@ impl<'b> Controller<'b> {
3838 // Do not launch the pager if NONE of the input files exist
3939 let mut paging_mode = self.config.paging_mode;
4040 if self.config.paging_mode != PagingMode::Never {
41− let call_pager = inputs.iter().any(|file| {
42− if let Input::Ordinary(ofile) = file {
43− return Path::new(ofile.provided_path()).exists();
41+ let call_pager = inputs.iter().any(|ref input| {
42+ if let InputKind::OrdinaryFile(ref path) = input.kind {
43+ return Path::new(path).exists();
4444 } else {
4545 return true;
4646 }
@@ -61,27 +61,24 @@ impl<'b> Controller<'b> {
6161 let mut no_errors: bool = true;
6262
6363 for input in inputs.into_iter() {
64− let description = input.description();
65−
66− match input.get_reader(io::stdin().lock()) {
64+ match input.open(io::stdin().lock()) {
6765 Err(error) => {
6866 handle_error(&error);
6967 no_errors = false;
7068 }
71− Ok(mut reader) => {
72− let result = if self.config.loop_through {
73− let mut printer = SimplePrinter::new();
74− self.print_file(reader, &mut printer, writer, &description)
69+ Ok(mut opened_input) => {
70+ let mut printer: Box<dyn Printer> = if self.config.loop_through {
71+ Box::new(SimplePrinter::new())
7572 } else {
76− let mut printer = InteractivePrinter::new(
73+ Box::new(InteractivePrinter::new(
7774 &self.config,
7875 &self.assets,
79− &input,
80− &mut reader,
81− );
82− self.print_file(reader, &mut printer, writer, &description)
76+ &mut opened_input,
77+ ))
8378 };
8479
80+ let result = self.print_file(&mut *printer, writer, &mut opened_input);
81+
8582 if let Err(error) = result {
8683 handle_error(&error);
8784 no_errors = false;
@@ -93,30 +90,29 @@ impl<'b> Controller<'b> {
9390 Ok(no_errors)
9491 }
9592
96− fn print_file<'a, P: Printer>(
93+ fn print_file<'a>(
9794 &self,
98− reader: InputReader,
99− printer: &mut P,
95+ printer: &mut dyn Printer,
10096 writer: &mut dyn Write,
101− input_description: &InputDescription,
97+ input: &mut OpenedInput,
10298 ) -> Result<()> {
103− if !reader.first_line.is_empty() || self.config.style_components.header() {
104− printer.print_header(writer, input_description)?;
99+ if !input.reader.first_line.is_empty() || self.config.style_components.header() {
100+ printer.print_header(writer, input)?;
105101 }
106102
107− if !reader.first_line.is_empty() {
108− self.print_file_ranges(printer, writer, reader, &self.config.line_ranges)?;
103+ if !input.reader.first_line.is_empty() {
104+ self.print_file_ranges(printer, writer, &mut input.reader, &self.config.line_ranges)?;
109105 }
110− printer.print_footer(writer)?;
106+ printer.print_footer(writer, input)?;
111107
112108 Ok(())
113109 }
114110
115− fn print_file_ranges<P: Printer>(
111+ fn print_file_ranges(
116112 &self,
117− printer: &mut P,
113+ printer: &mut dyn Printer,
118114 writer: &mut dyn Write,
119− mut reader: InputReader,
115+ reader: &mut InputReader,
120116 line_ranges: &LineRanges,
121117 ) -> Result<()> {
122118 let mut line_buffer = Vec::new();
src/input.rs+118−77
@@ -7,30 +7,6 @@ use content_inspector::{self, ContentType};
77use crate::errors::*;
88
99const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
10−#[derive(Debug, Clone, PartialEq)]
11−pub struct OrdinaryFile {
12− path: OsString,
13− user_provided_path: Option<OsString>,
14−}
15−
16−impl OrdinaryFile {
17− pub fn from_path(path: &OsStr) -> OrdinaryFile {
18− OrdinaryFile {
19− path: path.to_os_string(),
20− user_provided_path: None,
21− }
22− }
23−
24− pub fn set_provided_path(&mut self, user_provided_path: &OsStr) {
25− self.user_provided_path = Some(user_provided_path.to_os_string());
26− }
27−
28− pub(crate) fn provided_path<'a>(&'a self) -> &'a OsStr {
29− self.user_provided_path
30− .as_ref()
31− .unwrap_or_else(|| &self.path)
32− }
33−}
3410
3511#[derive(Debug, Clone)]
3612pub struct InputDescription {
@@ -39,69 +15,134 @@ pub struct InputDescription {
3915 pub name: String,
4016}
4117
42−pub enum Input {
43− StdIn(Option<OsString>),
44− Ordinary(OrdinaryFile),
45− FromReader(Box<dyn Read>, Option<OsString>),
18+pub enum InputKind {
19+ OrdinaryFile(OsString),
20+ StdIn,
4621 ThemePreviewFile,
22+ CustomReader(Box<dyn BufRead>),
23+}
24+
25+#[derive(Clone, Default)]
26+pub struct InputMetadata {
27+ pub user_provided_name: Option<OsString>,
28+}
29+
30+pub struct Input {
31+ pub kind: InputKind,
32+ pub metadata: InputMetadata,
33+}
34+
35+pub enum OpenedInputKind {
36+ OrdinaryFile(OsString),
37+ StdIn,
38+ ThemePreviewFile,
39+ CustomReader,
40+}
41+
42+pub struct OpenedInput<'a> {
43+ pub kind: OpenedInputKind,
44+ pub metadata: InputMetadata,
45+ pub reader: InputReader<'a>,
4746}
4847
4948impl Input {
50− pub(crate) fn get_reader<'a, R: BufRead + 'a>(&self, stdin: R) -> Result<InputReader<'a>> {
51− match self {
52− Input::StdIn(_) => Ok(InputReader::new(stdin)),
53− Input::Ordinary(ofile) => {
54− let file = File::open(&ofile.path)
55− .map_err(|e| format!("'{}': {}", ofile.path.to_string_lossy(), e))?;
56−
57− if file.metadata()?.is_dir() {
58− return Err(
59− format!("'{}' is a directory.", ofile.path.to_string_lossy()).into(),
60− );
61− }
62−
63− Ok(InputReader::new(BufReader::new(file)))
64− }
65− Input::ThemePreviewFile => Ok(InputReader::new(THEME_PREVIEW_FILE)),
66− Input::FromReader(_, _) => unimplemented!(), //Ok(InputReader::new(BufReader::new(reader))),
49+ pub fn ordinary_file(path: &OsStr) -> Self {
50+ Input {
51+ kind: InputKind::OrdinaryFile(path.to_os_string()),
52+ metadata: InputMetadata::default(),
6753 }
6854 }
6955
70− pub(crate) fn description(&self) -> InputDescription {
71− match self {
72− Input::Ordinary(ofile) => InputDescription {
73− full: format!("file '{}'", &ofile.provided_path().to_string_lossy()),
74− prefix: "File: ".to_owned(),
75− name: ofile.provided_path().to_string_lossy().into_owned(),
76− },
77− Input::StdIn(Some(name)) => InputDescription {
78− full: format!(
79− "STDIN (with name '{}')",
80− name.to_string_lossy().into_owned()
81− ),
82− prefix: "File: ".to_owned(),
83− name: name.to_string_lossy().into_owned(),
84− },
85− Input::StdIn(None) => InputDescription {
86− full: "STDIN".to_owned(),
87− prefix: "".to_owned(),
88− name: "STDIN".to_owned(),
89− },
90− Input::ThemePreviewFile => InputDescription {
91− full: "".to_owned(),
92− prefix: "".to_owned(),
93− name: "".to_owned(),
94− },
95− Input::FromReader(_, Some(name)) => InputDescription {
56+ pub fn stdin() -> Self {
57+ Input {
58+ kind: InputKind::StdIn,
59+ metadata: InputMetadata::default(),
60+ }
61+ }
62+
63+ pub fn theme_preview_file() -> Self {
64+ Input {
65+ kind: InputKind::ThemePreviewFile,
66+ metadata: InputMetadata::default(),
67+ }
68+ }
69+
70+ pub fn is_stdin(&self) -> bool {
71+ if let InputKind::StdIn = self.kind {
72+ true
73+ } else {
74+ false
75+ }
76+ }
77+
78+ pub fn set_provided_name(&mut self, provided_name: Option<&OsStr>) {
79+ self.metadata.user_provided_name = provided_name.map(|n| n.to_owned());
80+ }
81+
82+ pub fn open<'a, R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
83+ match self.kind {
84+ InputKind::StdIn => Ok(OpenedInput {
85+ kind: OpenedInputKind::StdIn,
86+ metadata: self.metadata,
87+ reader: InputReader::new(stdin),
88+ }),
89+ InputKind::OrdinaryFile(path) => Ok(OpenedInput {
90+ kind: OpenedInputKind::OrdinaryFile(path.clone()),
91+ metadata: self.metadata,
92+ reader: {
93+ let file = File::open(&path)
94+ .map_err(|e| format!("'{}': {}", path.to_string_lossy(), e))?;
95+ if file.metadata()?.is_dir() {
96+ return Err(format!("'{}' is a directory.", path.to_string_lossy()).into());
97+ }
98+ InputReader::new(BufReader::new(file))
99+ },
100+ }),
101+ InputKind::ThemePreviewFile => Ok(OpenedInput {
102+ kind: OpenedInputKind::ThemePreviewFile,
103+ metadata: self.metadata,
104+ reader: InputReader::new(THEME_PREVIEW_FILE),
105+ }),
106+ InputKind::CustomReader(reader) => Ok(OpenedInput {
107+ kind: OpenedInputKind::CustomReader,
108+ metadata: self.metadata,
109+ reader: InputReader::new(BufReader::new(reader)),
110+ }),
111+ }
112+ }
113+}
114+
115+impl<'a> OpenedInput<'a> {
116+ pub fn description(&self) -> InputDescription {
117+ if let Some(ref name) = self.metadata.user_provided_name {
118+ InputDescription {
96119 full: format!("file '{}'", name.to_string_lossy()),
97120 prefix: "File: ".to_owned(),
98121 name: name.to_string_lossy().into_owned(),
99− },
100− Input::FromReader(_, None) => InputDescription {
101− full: "reader".to_owned(),
102− prefix: "".to_owned(),
103− name: "READER".into(),
104− },
122+ }
123+ } else {
124+ match self.kind {
125+ OpenedInputKind::OrdinaryFile(ref path) => InputDescription {
126+ full: format!("file '{}'", path.to_string_lossy()),
127+ prefix: "File: ".to_owned(),
128+ name: path.to_string_lossy().into_owned(),
129+ },
130+ OpenedInputKind::StdIn => InputDescription {
131+ full: "STDIN".to_owned(),
132+ prefix: "".to_owned(),
133+ name: "STDIN".to_owned(),
134+ },
135+ OpenedInputKind::ThemePreviewFile => InputDescription {
136+ full: "".to_owned(),
137+ prefix: "".to_owned(),
138+ name: "".to_owned(),
139+ },
140+ OpenedInputKind::CustomReader => InputDescription {
141+ full: "reader".to_owned(),
142+ prefix: "".to_owned(),
143+ name: "READER".into(),
144+ },
145+ }
105146 }
106147 }
107148}
src/lib.rs+1−1
@@ -8,7 +8,7 @@ pub(crate) mod controller;
88mod decorations;
99mod diff;
1010pub mod errors;
11−pub(crate) mod input;
11+pub mod input;
1212mod less;
1313pub(crate) mod line_range;
1414mod output;
src/pretty_printer.rs+7−7
@@ -3,10 +3,10 @@ use std::io::Read;
33
44use crate::{
55 config::{
6− Config, HighlightedLineRanges, Input, LineRanges, OrdinaryFile, StyleComponents,
7− SyntaxMapping, WrappingMode,
6+ Config, HighlightedLineRanges, LineRanges, StyleComponents, SyntaxMapping, WrappingMode,
87 },
98 errors::Result,
9+ input::{Input, InputKind, OpenedInput},
1010 Controller, HighlightingAssets,
1111};
1212
@@ -35,8 +35,8 @@ impl<'a> PrettyPrinter<'a> {
3535
3636 /// Add a file which should be pretty-printed
3737 pub fn input_file(&mut self, path: &OsStr) -> &mut Self {
38− self.inputs
39− .push(Input::Ordinary(OrdinaryFile::from_path(path)));
38+ // self.inputs
39+ // .push(Input::Ordinary(OrdinaryFile::from_path(path)));
4040 self
4141 }
4242
@@ -47,15 +47,15 @@ impl<'a> PrettyPrinter<'a> {
4747 P: AsRef<OsStr>,
4848 {
4949 for path in paths {
50− self.inputs
51− .push(Input::Ordinary(OrdinaryFile::from_path(path.as_ref())));
50+ // self.inputs
51+ // .push(Input::Ordinary(OrdinaryFile::from_path(path.as_ref())));
5252 }
5353 self
5454 }
5555
5656 /// Add STDIN as an input
5757 pub fn input_stdin(&mut self) -> &mut Self {
58− self.inputs.push(Input::StdIn(None));
58+ // self.inputs.push(Input::StdIn(None));
5959 self
6060 }
6161
src/printer.rs+17−19
@@ -27,15 +27,15 @@ use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration}
2727#[cfg(feature = "git")]
2828use crate::diff::{get_git_diff, LineChanges};
2929use crate::errors::*;
30−use crate::input::{Input, InputDescription, InputReader};
30+use crate::input::{Input, InputDescription, InputKind, InputReader, OpenedInput, OpenedInputKind};
3131use crate::line_range::RangeCheckResult;
3232use crate::preprocessor::{expand_tabs, replace_nonprintable};
3333use crate::terminal::{as_terminal_escaped, to_ansi_color};
3434use crate::wrap::WrappingMode;
3535
3636pub trait Printer {
37− fn print_header(&mut self, handle: &mut dyn Write, input: &InputDescription) -> Result<()>;
38− fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>;
37+ fn print_header(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()>;
38+ fn print_footer(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()>;
3939
4040 fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>;
4141
@@ -57,11 +57,11 @@ impl SimplePrinter {
5757}
5858
5959impl Printer for SimplePrinter {
60− fn print_header(&mut self, _handle: &mut dyn Write, input: &InputDescription) -> Result<()> {
60+ fn print_header(&mut self, _handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> {
6161 Ok(())
6262 }
6363
64− fn print_footer(&mut self, _handle: &mut dyn Write) -> Result<()> {
64+ fn print_footer(&mut self, _handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> {
6565 Ok(())
6666 }
6767
@@ -101,8 +101,7 @@ impl<'a> InteractivePrinter<'a> {
101101 pub fn new(
102102 config: &'a Config,
103103 assets: &'a HighlightingAssets,
104− input: &Input,
105− reader: &mut InputReader,
104+ input: &mut OpenedInput,
106105 ) -> Self {
107106 let theme = assets.get_theme(&config.theme);
108107
@@ -150,7 +149,8 @@ impl<'a> InteractivePrinter<'a> {
150149 #[cfg(feature = "git")]
151150 let mut line_changes = None;
152151
153− let highlighter = if reader
152+ let highlighter = if input
153+ .reader
154154 .content_type
155155 .map_or(false, |c| c.is_binary() && !config.show_nonprintable)
156156 {
@@ -160,14 +160,14 @@ impl<'a> InteractivePrinter<'a> {
160160 #[cfg(feature = "git")]
161161 {
162162 if config.style_components.changes() {
163− if let Input::Ordinary(ofile) = input {
164− line_changes = get_git_diff(ofile.provided_path());
163+ if let OpenedInputKind::OrdinaryFile(ref path) = input.kind {
164+ line_changes = get_git_diff(path);
165165 }
166166 }
167167 }
168168
169169 // Determine the type of syntax for highlighting
170− let syntax = assets.get_syntax(config.language, input, reader, &config.syntax_mapping);
170+ let syntax = assets.get_syntax(config.language, input, &config.syntax_mapping);
171171 Some(HighlightLines::new(syntax, theme))
172172 };
173173
@@ -176,7 +176,7 @@ impl<'a> InteractivePrinter<'a> {
176176 colors,
177177 config,
178178 decorations,
179− content_type: reader.content_type,
179+ content_type: input.reader.content_type,
180180 ansi_prefix_sgr: String::new(),
181181 #[cfg(feature = "git")]
182182 line_changes,
@@ -230,11 +230,7 @@ impl<'a> InteractivePrinter<'a> {
230230}
231231
232232impl<'a> Printer for InteractivePrinter<'a> {
233− fn print_header(
234− &mut self,
235− handle: &mut dyn Write,
236− description: &InputDescription,
237− ) -> Result<()> {
233+ fn print_header(&mut self, handle: &mut dyn Write, input: &OpenedInput) -> Result<()> {
238234 if !self.config.style_components.header() {
239235 if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
240236 writeln!(
@@ -243,7 +239,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
243239 (but will be present if the output of 'bat' is piped). You can use 'bat -A' \
244240 to show the binary file contents.",
245241 Yellow.paint("[bat warning]"),
246− description.full,
242+ input.description().full,
247243 )?;
248244 } else {
249245 if self.config.style_components.grid() {
@@ -276,6 +272,8 @@ impl<'a> Printer for InteractivePrinter<'a> {
276272 _ => "",
277273 };
278274
275+ let description = input.description();
276+
279277 writeln!(
280278 handle,
281279 "{}{}{}",
@@ -295,7 +293,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
295293 Ok(())
296294 }
297295
298− fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()> {
296+ fn print_footer(&mut self, handle: &mut dyn Write, _input: &OpenedInput) -> Result<()> {
299297 if self.config.style_components.grid()
300298 && (self.content_type.map_or(false, |c| c.is_text()) || self.config.show_nonprintable)
301299 {