on master help sign in
Moved user_provided_filename to be contained within OrdinaryFile struct
commit04fa84aea7d02074bc650a552abb397b35c6720c authorKyle Criddle <criddle.kyle@gmail.com> dateSat 4 Apr 2020, 18:49 (7 years ago) parenta3f8140
9 files changed+143−127
examples/cat.rs+5−2 examples/simple.rs+5−2 src/assets.rs+18−33 src/bin/bat/app.rs+54−29 src/bin/bat/main.rs+5−2 src/config.rs+3−3 src/controller.rs+6−13 src/inputfile.rs+28−7 src/printer.rs+19−36
examples/cat.rs+5−2
@@ -1,7 +1,7 @@
11/// A very simple colorized `cat` clone, using `bat` as a library.
22/// See `src/bin/bat` for the full `bat` application.
33use bat::{
4− config::{Config, InputFile, StyleComponent, StyleComponents},
4+ config::{Config, InputFile, OrdinaryFile, StyleComponent, StyleComponents},
55 Controller, HighlightingAssets,
66};
77use console::Term;
@@ -24,7 +24,10 @@ fn main() {
2424 StyleComponent::Grid,
2525 StyleComponent::Numbers,
2626 ]),
27− files: files.iter().map(|file| InputFile::Ordinary(file)).collect(),
27+ files: files
28+ .iter()
29+ .map(|file| InputFile::Ordinary(OrdinaryFile::new(file, None)))
30+ .collect(),
2831 ..Default::default()
2932 };
3033 let assets = HighlightingAssets::from_binary();
examples/simple.rs+5−2
@@ -1,6 +1,6 @@
11/// A simple program that prints its own source code using the bat library
22use bat::{
3− config::{Config, InputFile},
3+ config::{Config, InputFile, OrdinaryFile},
44 Controller, HighlightingAssets,
55};
66use std::ffi::OsStr;
@@ -9,7 +9,10 @@ fn main() {
99 let path_to_this_file = OsStr::new(file!());
1010
1111 let config = Config {
12− files: vec![InputFile::Ordinary(path_to_this_file)],
12+ files: vec![InputFile::Ordinary(OrdinaryFile::new(
13+ path_to_this_file,
14+ None,
15+ ))],
1316 colored_output: true,
1417 true_color: true,
1518 ..Default::default()
src/assets.rs+18−33
@@ -177,14 +177,13 @@ impl HighlightingAssets {
177177 &self,
178178 language: Option<&str>,
179179 file: InputFile,
180− file_name: Option<&str>,
181180 reader: &mut InputFileReader,
182181 mapping: &SyntaxMapping,
183182 ) -> &SyntaxReference {
184− let syntax = match (language, file, file_name) {
185− (Some(language), _, _) => self.syntax_set.find_syntax_by_token(language),
186− (None, InputFile::Ordinary(file), _) => {
187− let path = Path::new(file);
183+ let syntax = match (language, file) {
184+ (Some(language), _) => self.syntax_set.find_syntax_by_token(language),
185+ (None, InputFile::Ordinary(ofile)) => {
186+ let path = Path::new(ofile.filename());
188187
189188 let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
190189 let extension = path.extension().and_then(|x| x.to_str()).unwrap_or("");
@@ -208,12 +207,12 @@ impl HighlightingAssets {
208207 None => ext_syntax.or(line_syntax),
209208 }
210209 }
211− (None, InputFile::StdIn, None) => String::from_utf8(reader.first_line.clone())
210+ (None, InputFile::StdIn(None)) => String::from_utf8(reader.first_line.clone())
212211 .ok()
213212 .and_then(|l| self.syntax_set.find_syntax_by_first_line(&l)),
214− (None, InputFile::StdIn, Some(file_name)) => self
213+ (None, InputFile::StdIn(Some(file_name))) => self
215214 .syntax_set
216− .find_syntax_by_extension(&file_name)
215+ .find_syntax_by_extension(file_name.to_str().unwrap())
217216 .or_else(|| {
218217 self.syntax_set.find_syntax_by_extension(
219218 Path::new(file_name)
@@ -225,7 +224,7 @@ impl HighlightingAssets {
225224 .or(String::from_utf8(reader.first_line.clone())
226225 .ok()
227226 .and_then(|l| self.syntax_set.find_syntax_by_first_line(&l))),
228− (_, InputFile::ThemePreviewFile, _) => self.syntax_set.find_syntax_by_name("Rust"),
227+ (_, InputFile::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
229228 };
230229
231230 syntax.unwrap_or_else(|| self.syntax_set.find_syntax_plain_text())
@@ -242,7 +241,7 @@ mod tests {
242241 use tempdir::TempDir;
243242
244243 use crate::assets::HighlightingAssets;
245− use crate::inputfile::InputFile;
244+ use crate::inputfile::{InputFile, OrdinaryFile};
246245 use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
247246
248247 struct SyntaxDetectionTest<'a> {
@@ -261,28 +260,17 @@ mod tests {
261260 }
262261 }
263262
264− fn syntax_for_file_with_content(
265− &self,
266− file_name: &str,
267− first_line: &str,
268− as_stdin: bool,
269− ) -> String {
263+ fn syntax_for_file_with_content(&self, file_name: &str, first_line: &str) -> String {
270264 let file_path = self.temp_dir.path().join(file_name);
271265 {
272266 let mut temp_file = File::create(&file_path).unwrap();
273267 writeln!(temp_file, "{}", first_line).unwrap();
274268 }
275269
276− let input_file = InputFile::Ordinary(OsStr::new(&file_path));
277− let (file, file_name) = if as_stdin {
278− (InputFile::StdIn, Some(file_name))
279− } else {
280− (input_file, None)
281− };
270+ let input_file = InputFile::Ordinary(OrdinaryFile::new(OsStr::new(&file_path), None));
282271 let syntax = self.assets.get_syntax(
283272 None,
284− file,
285− file_name,
273+ input_file,
286274 &mut input_file.get_reader(&io::stdin()).unwrap(),
287275 &self.syntax_mapping,
288276 );
@@ -291,7 +279,7 @@ mod tests {
291279 }
292280
293281 fn syntax_for_file(&self, file_name: &str) -> String {
294− self.syntax_for_file_with_content(file_name, "", false)
282+ self.syntax_for_file_with_content(file_name, "")
295283 }
296284 }
297285
@@ -325,15 +313,15 @@ mod tests {
325313 let test = SyntaxDetectionTest::new();
326314
327315 assert_eq!(
328− test.syntax_for_file_with_content("my_script", "#!/bin/bash", false),
316+ test.syntax_for_file_with_content("my_script", "#!/bin/bash"),
329317 "Bourne Again Shell (bash)"
330318 );
331319 assert_eq!(
332− test.syntax_for_file_with_content("build", "#!/bin/bash", false),
320+ test.syntax_for_file_with_content("build", "#!/bin/bash"),
333321 "Bourne Again Shell (bash)"
334322 );
335323 assert_eq!(
336− test.syntax_for_file_with_content("my_script", "<?php", false),
324+ test.syntax_for_file_with_content("my_script", "<?php"),
337325 "PHP"
338326 );
339327 }
@@ -365,13 +353,10 @@ mod tests {
365353 let test = SyntaxDetectionTest::new();
366354
367355 // from file extension
368− assert_eq!(
369− test.syntax_for_file_with_content("test.cpp", "", true),
370− "C++"
371− );
356+ assert_eq!(test.syntax_for_file_with_content("test.cpp", ""), "C++");
372357 // from first line (fallback)
373358 assert_eq!(
374− test.syntax_for_file_with_content("my_script", "#!/bin/bash", true),
359+ test.syntax_for_file_with_content("my_script", "#!/bin/bash"),
375360 "Bourne Again Shell (bash)"
376361 );
377362 }
src/bin/bat/app.rs+54−29
@@ -1,5 +1,6 @@
11use std::collections::HashSet;
22use std::env;
3+use std::ffi::OsStr;
34use std::str::FromStr;
45
56use atty::{self, Stream};
@@ -14,8 +15,8 @@ use console::Term;
1415
1516use bat::{
1617 config::{
17− Config, HighlightedLineRanges, InputFile, LineRange, LineRanges, MappingTarget, OutputWrap,
18− PagingMode, StyleComponent, StyleComponents, SyntaxMapping,
18+ Config, HighlightedLineRanges, InputFile, LineRange, LineRanges, MappingTarget,
19+ OrdinaryFile, OutputWrap, PagingMode, StyleComponent, StyleComponents, SyntaxMapping,
1920 },
2021 errors::*,
2122 HighlightingAssets,
@@ -73,7 +74,7 @@ impl App {
7374 }
7475
7576 pub fn config(&self) -> Result<Config> {
76− let files = self.files();
77+ let files = self.files()?;
7778 let style_components = self.style_components()?;
7879
7980 let paging_mode = match self.matches.value_of("paging") {
@@ -83,7 +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 files.contains(&InputFile::StdIn) {
87+ } else if files.contains(&InputFile::StdIn(None)) {
8788 // If we are reading from stdin, only enable paging if we write to an
8889 // interactive terminal and if we do not *read* from an interactive
8990 // terminal.
@@ -132,13 +133,6 @@ impl App {
132133 }
133134 });
134135
135− match self.matches.values_of("file-name") {
136− Some(ref filenames) if filenames.len() != files.len() => {
137− return Err(format!("{} {}", filenames.len(), files.len()).into());
138− }
139− _ => {}
140− }
141−
142136 Ok(Config {
143137 true_color: is_truecolor_terminal(),
144138 language: self.matches.value_of("language").or_else(|| {
@@ -225,28 +219,59 @@ impl App {
225219 .map(LineRanges::from)
226220 .map(|lr| HighlightedLineRanges(lr))
227221 .unwrap_or_default(),
228− filenames: self
229− .matches
230− .values_of("file-name")
231− .map(|values| values.collect()),
232222 })
233223 }
234224
235− fn files(&self) -> Vec<InputFile> {
236− self.matches
225+ fn files(&self) -> Result<Vec<InputFile>> {
226+ // verify equal length of file-names and input FILEs
227+ match self.matches.values_of("file-name") {
228+ Some(filenames)
229+ if self.matches.values_of_os("FILE").is_some()
230+ && filenames.len() != self.matches.values_of_os("FILE").unwrap().len() =>
231+ {
232+ return Err("Must be one file name per input type.".into());
233+ }
234+ _ => {}
235+ }
236+ let filenames: Option<Vec<&str>> = self
237+ .matches
238+ .values_of("file-name")
239+ .map(|values| values.collect());
240+
241+ let mut filenames_or_none: Box<dyn Iterator<Item = _>> = match filenames {
242+ Some(ref filenames) => {
243+ Box::new(filenames.into_iter().map(|name| Some(OsStr::new(*name))))
244+ }
245+ None => Box::new(std::iter::repeat(None)),
246+ };
247+ let files: Option<Vec<&str>> = self
248+ .matches
237249 .values_of_os("FILE")
238− .map(|values| {
239− values
240− .map(|filename| {
241− if filename == "-" {
242− InputFile::StdIn
243− } else {
244− InputFile::Ordinary(filename)
245− }
246− })
247− .collect()
248− })
249− .unwrap_or_else(|| vec![InputFile::StdIn])
250+ .map(|values| values.map(|fname| fname.to_str()).collect())
251+ .unwrap_or(None);
252+
253+ if files.is_none() {
254+ return Ok(vec![InputFile::StdIn(filenames_or_none.nth(0).unwrap())]);
255+ }
256+ let files_or_none: Box<dyn Iterator<Item = _>> = match files {
257+ Some(ref files) => Box::new(files.into_iter().map(|name| Some(OsStr::new(*name)))),
258+ None => Box::new(std::iter::repeat(None)),
259+ };
260+
261+ let mut file_input = Vec::new();
262+ for (input, name) in files_or_none.zip(filenames_or_none) {
263+ match input {
264+ Some(input) => {
265+ if input.to_str().unwrap() == "-" {
266+ file_input.push(InputFile::StdIn(name));
267+ } else {
268+ file_input.push(InputFile::Ordinary(OrdinaryFile::new(input, name)))
269+ }
270+ }
271+ None => {}
272+ }
273+ }
274+ return Ok(file_input);
250275 }
251276
252277 fn style_components(&self) -> Result<StyleComponents> {
src/bin/bat/main.rs+5−2
@@ -26,7 +26,7 @@ use bat::Controller;
2626use directories::PROJECT_DIRS;
2727
2828use bat::{
29− config::{Config, InputFile, StyleComponent, StyleComponents},
29+ config::{Config, InputFile, OrdinaryFile, StyleComponent, StyleComponents},
3030 errors::*,
3131 HighlightingAssets,
3232};
@@ -167,7 +167,10 @@ fn run() -> Result<bool> {
167167 Ok(true)
168168 } else {
169169 let mut config = app.config()?;
170− config.files = vec![InputFile::Ordinary(OsStr::new("cache"))];
170+ config.files = vec![InputFile::Ordinary(OrdinaryFile::new(
171+ OsStr::new("cache"),
172+ None,
173+ ))];
171174
172175 run_controller(&config)
173176 }
src/config.rs+3−3
@@ -1,4 +1,5 @@
11pub use crate::inputfile::InputFile;
2+pub use crate::inputfile::OrdinaryFile;
23pub use crate::line_range::{HighlightedLineRanges, LineRange, LineRanges};
34pub use crate::style::{StyleComponent, StyleComponents};
45pub use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
@@ -73,9 +74,8 @@ pub struct Config<'a> {
7374
7475 /// Ranges of lines which should be highlighted with a special background color
7576 pub highlighted_lines: HighlightedLineRanges,
76−
77− /// Names of files to display when printing
78− pub filenames: Option<Vec<&'a str>>,
77+ ///// Names of files to display when printing
78+ // pub filenames: Option<Vec<&'a str>>,
7979}
8080
8181#[test]
src/controller.rs+6−13
@@ -35,8 +35,8 @@ impl<'b> Controller<'b> {
3535 let mut paging_mode = self.config.paging_mode;
3636 if self.config.paging_mode != PagingMode::Never {
3737 let call_pager = self.config.files.iter().any(|file| {
38− if let InputFile::Ordinary(path) = file {
39− return Path::new(path).exists();
38+ if let InputFile::Ordinary(ofile) = file {
39+ return Path::new(ofile.filename()).exists();
4040 } else {
4141 return true;
4242 }
@@ -58,12 +58,7 @@ impl<'b> Controller<'b> {
5858
5959 let stdin = io::stdin();
6060
61− let filenames: Box<dyn Iterator<Item = _>> = match self.config.filenames {
62− Some(ref filenames) => Box::new(filenames.into_iter().map(|name| Some(*name))),
63− None => Box::new(std::iter::repeat(None)),
64− };
65−
66− for (input_file, file_name) in self.config.files.iter().zip(filenames) {
61+ for input_file in self.config.files.iter() {
6762 match input_file.get_reader(&stdin) {
6863 Err(error) => {
6964 handle_error(&error);
@@ -72,16 +67,15 @@ impl<'b> Controller<'b> {
7267 Ok(mut reader) => {
7368 let result = if self.config.loop_through {
7469 let mut printer = SimplePrinter::new();
75− self.print_file(reader, &mut printer, writer, *input_file, file_name)
70+ self.print_file(reader, &mut printer, writer, *input_file)
7671 } else {
7772 let mut printer = InteractivePrinter::new(
7873 &self.config,
7974 &self.assets,
8075 *input_file,
81− file_name,
8276 &mut reader,
8377 );
84− self.print_file(reader, &mut printer, writer, *input_file, file_name)
78+ self.print_file(reader, &mut printer, writer, *input_file)
8579 };
8680
8781 if let Err(error) = result {
@@ -101,10 +95,9 @@ impl<'b> Controller<'b> {
10195 printer: &mut P,
10296 writer: &mut dyn Write,
10397 input_file: InputFile<'a>,
104− file_name: Option<&str>,
10598 ) -> Result<()> {
10699 if !reader.first_line.is_empty() || self.config.style_components.header() {
107− printer.print_header(writer, input_file, file_name)?;
100+ printer.print_header(writer, input_file)?;
108101 }
109102
110103 if !reader.first_line.is_empty() {
src/inputfile.rs+28−7
@@ -52,23 +52,44 @@ impl<'a> InputFileReader<'a> {
5252 }
5353}
5454
55+#[derive(Debug, Clone, Copy, PartialEq)]
56+pub struct OrdinaryFile<'a> {
57+ filename: &'a OsStr,
58+ user_provided_name: Option<&'a OsStr>,
59+}
60+
61+impl<'a> OrdinaryFile<'a> {
62+ pub fn new(filename: &'a OsStr, user_provided_name: Option<&'a OsStr>) -> OrdinaryFile<'a> {
63+ OrdinaryFile {
64+ filename: filename,
65+ user_provided_name: user_provided_name,
66+ }
67+ }
68+
69+ pub fn filename(&self) -> &'a OsStr {
70+ self.user_provided_name.unwrap_or_else(|| self.filename)
71+ }
72+}
73+
5574#[derive(Debug, Clone, Copy, PartialEq)]
5675pub enum InputFile<'a> {
57− StdIn,
58− Ordinary(&'a OsStr),
76+ StdIn(Option<&'a OsStr>),
77+ Ordinary(OrdinaryFile<'a>),
5978 ThemePreviewFile,
6079}
6180
6281impl<'a> InputFile<'a> {
6382 pub(crate) fn get_reader(&self, stdin: &'a io::Stdin) -> Result<InputFileReader> {
6483 match self {
65− InputFile::StdIn => Ok(InputFileReader::new(stdin.lock())),
66− InputFile::Ordinary(filename) => {
67− let file = File::open(filename)
68− .map_err(|e| format!("'{}': {}", filename.to_string_lossy(), e))?;
84+ InputFile::StdIn(_) => Ok(InputFileReader::new(stdin.lock())),
85+ InputFile::Ordinary(ofile) => {
86+ let file = File::open(ofile.filename)
87+ .map_err(|e| format!("'{}': {}", ofile.filename.to_string_lossy(), e))?;
6988
7089 if file.metadata()?.is_dir() {
71− return Err(format!("'{}' is a directory.", filename.to_string_lossy()).into());
90+ return Err(
91+ format!("'{}' is a directory.", ofile.filename.to_string_lossy()).into(),
92+ );
7293 }
7394
7495 Ok(InputFileReader::new(BufReader::new(file)))
src/printer.rs+19−36
@@ -34,12 +34,7 @@ use crate::terminal::{as_terminal_escaped, to_ansi_color};
3434use crate::wrap::OutputWrap;
3535
3636pub trait Printer {
37− fn print_header(
38− &mut self,
39− handle: &mut dyn Write,
40− file: InputFile,
41− file_name: Option<&str>,
42− ) -> Result<()>;
37+ fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()>;
4338 fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>;
4439
4540 fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>;
@@ -62,12 +57,7 @@ impl SimplePrinter {
6257}
6358
6459impl Printer for SimplePrinter {
65− fn print_header(
66− &mut self,
67− _handle: &mut dyn Write,
68− _file: InputFile,
69− _file_name: Option<&str>,
70− ) -> Result<()> {
60+ fn print_header(&mut self, _handle: &mut dyn Write, _file: InputFile) -> Result<()> {
7161 Ok(())
7262 }
7363
@@ -112,7 +102,6 @@ impl<'a> InteractivePrinter<'a> {
112102 config: &'a Config,
113103 assets: &'a HighlightingAssets,
114104 file: InputFile,
115− file_name: Option<&str>,
116105 reader: &mut InputFileReader,
117106 ) -> Self {
118107 let theme = assets.get_theme(&config.theme);
@@ -171,20 +160,14 @@ impl<'a> InteractivePrinter<'a> {
171160 #[cfg(feature = "git")]
172161 {
173162 if config.style_components.changes() {
174− if let InputFile::Ordinary(filename) = file {
175− line_changes = get_git_diff(filename);
163+ if let InputFile::Ordinary(ofile) = file {
164+ line_changes = get_git_diff(ofile.filename());
176165 }
177166 }
178167 }
179168
180169 // Determine the type of syntax for highlighting
181− let syntax = assets.get_syntax(
182− config.language,
183− file,
184− file_name,
185− reader,
186− &config.syntax_mapping,
187− );
170+ let syntax = assets.get_syntax(config.language, file, reader, &config.syntax_mapping);
188171 Some(HighlightLines::new(syntax, theme))
189172 };
190173
@@ -247,20 +230,16 @@ impl<'a> InteractivePrinter<'a> {
247230}
248231
249232impl<'a> Printer for InteractivePrinter<'a> {
250− fn print_header(
251− &mut self,
252− handle: &mut dyn Write,
253− file: InputFile,
254− file_name: Option<&str>,
255− ) -> Result<()> {
233+ fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()> {
256234 if !self.config.style_components.header() {
257235 if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
258236 let input = match file {
259− InputFile::Ordinary(filename) => format!(
260− "file '{}'",
261− file_name.unwrap_or(&filename.to_string_lossy())
262− ),
263− _ => file_name.unwrap_or("STDIN").to_owned(),
237+ InputFile::Ordinary(ofile) => {
238+ format!("file '{}'", &ofile.filename().to_string_lossy())
239+ }
240+ InputFile::StdIn(Some(name)) => name.to_str().unwrap().to_string(),
241+ InputFile::StdIn(None) => "STDIN".to_owned(),
242+ InputFile::ThemePreviewFile => "".to_owned(),
264243 };
265244
266245 writeln!(
@@ -295,11 +274,15 @@ impl<'a> Printer for InteractivePrinter<'a> {
295274 }
296275
297276 let (prefix, name) = match file {
298− InputFile::Ordinary(filename) => (
277+ InputFile::Ordinary(ofile) => (
299278 "File: ",
300− Cow::from(file_name.unwrap_or(&filename.to_string_lossy()).to_owned()),
279+ Cow::from(ofile.filename().to_string_lossy().to_owned()),
301280 ),
302− _ => ("File: ", Cow::from(file_name.unwrap_or("STDIN").to_owned())),
281+ InputFile::StdIn(Some(name)) => {
282+ ("File: ", Cow::from(name.to_string_lossy().to_owned()))
283+ }
284+ InputFile::StdIn(None) => ("File: ", Cow::from("STDIN".to_owned())),
285+ InputFile::ThemePreviewFile => ("", Cow::from("")),
303286 };
304287
305288 let mode = match self.content_type {