mirror of
https://github.com/sharkdp/bat.git
synced 2025-01-18 20:11:03 +00:00
Large refactoring towards a better builder structure
This commit is contained in:
parent
27974616bf
commit
057e4eced1
@ -1,36 +1,17 @@
|
||||
/// A very simple colorized `cat` clone, using `bat` as a library.
|
||||
/// See `src/bin/bat` for the full `bat` application.
|
||||
use bat::{
|
||||
config::{Config, InputFile, OrdinaryFile, StyleComponent, StyleComponents},
|
||||
Controller, HighlightingAssets,
|
||||
};
|
||||
use bat::{PrettyPrinter, StyleComponent, StyleComponents};
|
||||
use console::Term;
|
||||
use std::process;
|
||||
|
||||
fn main() {
|
||||
let files = std::env::args_os().skip(1).collect::<Vec<_>>();
|
||||
|
||||
if files.is_empty() {
|
||||
eprintln!("No input files specified");
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
let config = Config {
|
||||
term_width: Term::stdout().size().1 as usize,
|
||||
colored_output: true,
|
||||
true_color: true,
|
||||
style_components: StyleComponents::new(&[
|
||||
PrettyPrinter::new()
|
||||
.term_width(Term::stdout().size().1 as usize)
|
||||
.style_components(StyleComponents::new(&[
|
||||
StyleComponent::Header,
|
||||
StyleComponent::Grid,
|
||||
StyleComponent::Numbers,
|
||||
]),
|
||||
files: files
|
||||
.iter()
|
||||
.map(|file| InputFile::Ordinary(OrdinaryFile::from_path(file)))
|
||||
.collect(),
|
||||
..Default::default()
|
||||
};
|
||||
let assets = HighlightingAssets::from_binary();
|
||||
|
||||
Controller::new(&config, &assets).run().expect("no errors");
|
||||
]))
|
||||
.files(std::env::args_os().skip(1))
|
||||
.run()
|
||||
.expect("no errors");
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ impl HighlightingAssets {
|
||||
pub(crate) fn get_syntax(
|
||||
&self,
|
||||
language: Option<&str>,
|
||||
file: InputFile,
|
||||
file: &InputFile,
|
||||
reader: &mut InputFileReader,
|
||||
mapping: &SyntaxMapping,
|
||||
) -> &SyntaxReference {
|
||||
@ -216,7 +216,7 @@ impl HighlightingAssets {
|
||||
.ok()
|
||||
.and_then(|l| self.syntax_set.find_syntax_by_first_line(&l)),
|
||||
(None, InputFile::StdIn(Some(file_name))) => self
|
||||
.get_extension_syntax(file_name)
|
||||
.get_extension_syntax(&file_name)
|
||||
.or(self.get_first_line_syntax(reader)),
|
||||
(_, InputFile::ThemePreviewFile) => self.syntax_set.find_syntax_by_name("Rust"),
|
||||
};
|
||||
@ -246,17 +246,17 @@ impl HighlightingAssets {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::ffi::OsStr;
|
||||
use super::*;
|
||||
|
||||
use crate::inputfile::OrdinaryFile;
|
||||
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
use tempdir::TempDir;
|
||||
|
||||
use crate::assets::HighlightingAssets;
|
||||
use crate::inputfile::{InputFile, OrdinaryFile};
|
||||
use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
|
||||
struct SyntaxDetectionTest<'a> {
|
||||
assets: HighlightingAssets,
|
||||
pub syntax_mapping: SyntaxMapping<'a>,
|
||||
@ -283,7 +283,7 @@ mod tests {
|
||||
let input_file = InputFile::Ordinary(OrdinaryFile::from_path(file_path.as_os_str()));
|
||||
let syntax = self.assets.get_syntax(
|
||||
None,
|
||||
input_file,
|
||||
&input_file,
|
||||
&mut input_file.get_reader(io::stdin().lock()).unwrap(),
|
||||
&self.syntax_mapping,
|
||||
);
|
||||
@ -304,10 +304,10 @@ mod tests {
|
||||
}
|
||||
|
||||
fn syntax_for_stdin_with_content(&self, file_name: &str, content: &[u8]) -> String {
|
||||
let input_file = InputFile::StdIn(Some(OsStr::new(file_name)));
|
||||
let input_file = InputFile::StdIn(Some(OsString::from(file_name)));
|
||||
let syntax = self.assets.get_syntax(
|
||||
None,
|
||||
input_file,
|
||||
&input_file,
|
||||
&mut input_file.get_reader(content).unwrap(),
|
||||
&self.syntax_mapping,
|
||||
);
|
||||
|
@ -16,7 +16,7 @@ use console::Term;
|
||||
use bat::{
|
||||
config::{
|
||||
Config, HighlightedLineRanges, InputFile, LineRange, LineRanges, MappingTarget,
|
||||
OrdinaryFile, OutputWrap, PagingMode, StyleComponent, StyleComponents, SyntaxMapping,
|
||||
OrdinaryFile, PagingMode, StyleComponent, StyleComponents, SyntaxMapping, WrappingMode,
|
||||
},
|
||||
errors::*,
|
||||
HighlightingAssets,
|
||||
@ -143,22 +143,22 @@ impl App {
|
||||
}
|
||||
}),
|
||||
show_nonprintable: self.matches.is_present("show-all"),
|
||||
output_wrap: if self.interactive_output || maybe_term_width.is_some() {
|
||||
wrapping_mode: if self.interactive_output || maybe_term_width.is_some() {
|
||||
match self.matches.value_of("wrap") {
|
||||
Some("character") => OutputWrap::Character,
|
||||
Some("never") => OutputWrap::None,
|
||||
Some("character") => WrappingMode::Character,
|
||||
Some("never") => WrappingMode::NoWrapping,
|
||||
Some("auto") | _ => {
|
||||
if style_components.plain() {
|
||||
OutputWrap::None
|
||||
WrappingMode::NoWrapping
|
||||
} else {
|
||||
OutputWrap::Character
|
||||
WrappingMode::Character
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We don't have the tty width when piping to another program.
|
||||
// There's no point in wrapping when this is the case.
|
||||
OutputWrap::None
|
||||
WrappingMode::NoWrapping
|
||||
},
|
||||
colored_output: match self.matches.value_of("color") {
|
||||
Some("always") => true,
|
||||
@ -247,7 +247,9 @@ impl App {
|
||||
let files: Option<Vec<&OsStr>> = self.matches.values_of_os("FILE").map(|vs| vs.collect());
|
||||
|
||||
if files.is_none() {
|
||||
return Ok(vec![InputFile::StdIn(filenames_or_none.nth(0).unwrap())]);
|
||||
return Ok(vec![InputFile::StdIn(
|
||||
filenames_or_none.nth(0).unwrap().map(|f| f.to_owned()),
|
||||
)]);
|
||||
}
|
||||
let files_or_none: Box<dyn Iterator<Item = _>> = match files {
|
||||
Some(ref files) => Box::new(files.into_iter().map(|name| Some(*name))),
|
||||
@ -258,7 +260,7 @@ impl App {
|
||||
for (input, name) in files_or_none.zip(filenames_or_none) {
|
||||
if let Some(input) = input {
|
||||
if input.to_str().unwrap_or_default() == "-" {
|
||||
file_input.push(InputFile::StdIn(name));
|
||||
file_input.push(InputFile::StdIn(name.map(|n| n.to_owned())));
|
||||
} else {
|
||||
let mut ofile = OrdinaryFile::from_path(input);
|
||||
if let Some(path) = name {
|
||||
|
@ -3,7 +3,7 @@ pub use crate::inputfile::OrdinaryFile;
|
||||
pub use crate::line_range::{HighlightedLineRanges, LineRange, LineRanges};
|
||||
pub use crate::style::{StyleComponent, StyleComponents};
|
||||
pub use crate::syntax_mapping::{MappingTarget, SyntaxMapping};
|
||||
pub use crate::wrap::OutputWrap;
|
||||
pub use crate::wrap::WrappingMode;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[cfg(feature = "paging")]
|
||||
@ -23,7 +23,7 @@ impl Default for PagingMode {
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Config<'a> {
|
||||
/// List of files to print
|
||||
pub files: Vec<InputFile<'a>>,
|
||||
pub files: Vec<InputFile>,
|
||||
|
||||
/// The explicitly configured language, if any
|
||||
pub language: Option<&'a str>,
|
||||
@ -50,8 +50,8 @@ pub struct Config<'a> {
|
||||
/// Style elements (grid, line numbers, ...)
|
||||
pub style_components: StyleComponents,
|
||||
|
||||
/// Text wrapping mode
|
||||
pub output_wrap: OutputWrap,
|
||||
/// If and how text should be wrapped
|
||||
pub wrapping_mode: WrappingMode,
|
||||
|
||||
/// Pager or STDOUT
|
||||
#[cfg(feature = "paging")]
|
||||
|
@ -65,15 +65,15 @@ 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_file)
|
||||
self.print_file(reader, &mut printer, writer, input_file)
|
||||
} else {
|
||||
let mut printer = InteractivePrinter::new(
|
||||
&self.config,
|
||||
&self.assets,
|
||||
*input_file,
|
||||
input_file,
|
||||
&mut reader,
|
||||
);
|
||||
self.print_file(reader, &mut printer, writer, *input_file)
|
||||
self.print_file(reader, &mut printer, writer, input_file)
|
||||
};
|
||||
|
||||
if let Err(error) = result {
|
||||
@ -92,7 +92,7 @@ impl<'b> Controller<'b> {
|
||||
reader: InputFileReader,
|
||||
printer: &mut P,
|
||||
writer: &mut dyn Write,
|
||||
input_file: InputFile<'a>,
|
||||
input_file: &InputFile,
|
||||
) -> Result<()> {
|
||||
if !reader.first_line.is_empty() || self.config.style_components.header() {
|
||||
printer.print_header(writer, input_file)?;
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs::File;
|
||||
use std::io::{self, BufRead, BufReader};
|
||||
|
||||
@ -52,42 +52,44 @@ impl<'a> InputFileReader<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct OrdinaryFile<'a> {
|
||||
path: &'a OsStr,
|
||||
user_provided_path: Option<&'a OsStr>,
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct OrdinaryFile {
|
||||
path: OsString,
|
||||
user_provided_path: Option<OsString>,
|
||||
}
|
||||
|
||||
impl<'a> OrdinaryFile<'a> {
|
||||
pub fn from_path(path: &'a OsStr) -> OrdinaryFile<'a> {
|
||||
impl OrdinaryFile {
|
||||
pub fn from_path(path: &OsStr) -> OrdinaryFile {
|
||||
OrdinaryFile {
|
||||
path,
|
||||
path: path.to_os_string(),
|
||||
user_provided_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_provided_path(&mut self, user_provided_path: &'a OsStr) {
|
||||
self.user_provided_path = Some(user_provided_path);
|
||||
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(&self) -> &'a OsStr {
|
||||
self.user_provided_path.unwrap_or_else(|| self.path)
|
||||
pub(crate) fn provided_path<'a>(&'a self) -> &'a OsStr {
|
||||
self.user_provided_path
|
||||
.as_ref()
|
||||
.unwrap_or_else(|| &self.path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum InputFile<'a> {
|
||||
StdIn(Option<&'a OsStr>),
|
||||
Ordinary(OrdinaryFile<'a>),
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum InputFile {
|
||||
StdIn(Option<OsString>),
|
||||
Ordinary(OrdinaryFile),
|
||||
ThemePreviewFile,
|
||||
}
|
||||
|
||||
impl<'a> InputFile<'a> {
|
||||
pub(crate) fn get_reader<R: BufRead + 'a>(&self, stdin: R) -> Result<InputFileReader> {
|
||||
impl InputFile {
|
||||
pub(crate) fn get_reader<'a, R: BufRead + 'a>(&self, stdin: R) -> Result<InputFileReader<'a>> {
|
||||
match self {
|
||||
InputFile::StdIn(_) => Ok(InputFileReader::new(stdin)),
|
||||
InputFile::Ordinary(ofile) => {
|
||||
let file = File::open(ofile.path)
|
||||
let file = File::open(&ofile.path)
|
||||
.map_err(|e| format!("'{}': {}", ofile.path.to_string_lossy(), e))?;
|
||||
|
||||
if file.metadata()?.is_dir() {
|
||||
|
@ -25,3 +25,4 @@ pub use assets_metadata::AssetsMetadata;
|
||||
pub use controller::Controller;
|
||||
pub use pretty_printer::PrettyPrinter;
|
||||
pub use printer::{InteractivePrinter, Printer, SimplePrinter};
|
||||
pub use style::{StyleComponent, StyleComponents};
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use crate::{
|
||||
config::{Config, InputFile, OrdinaryFile},
|
||||
config::{Config, InputFile, OrdinaryFile, StyleComponents, WrappingMode},
|
||||
errors::Result,
|
||||
Controller, HighlightingAssets,
|
||||
};
|
||||
@ -24,19 +24,64 @@ impl<'a> PrettyPrinter<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&'a mut self, path: &'a OsStr) -> &'a mut Self {
|
||||
/// Add a file which should be pretty-printed
|
||||
pub fn file(&mut self, path: &OsStr) -> &mut Self {
|
||||
self.config
|
||||
.files
|
||||
.push(InputFile::Ordinary(OrdinaryFile::from_path(path)));
|
||||
self
|
||||
}
|
||||
|
||||
/// Add multiple files which should be pretty-printed
|
||||
pub fn files<I, P>(&mut self, paths: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator<Item = P>,
|
||||
P: AsRef<OsStr>,
|
||||
{
|
||||
for path in paths {
|
||||
self.config
|
||||
.files
|
||||
.push(InputFile::Ordinary(OrdinaryFile::from_path(path.as_ref())));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// The character width of the terminal (default: unlimited)
|
||||
pub fn term_width(&mut self, width: usize) -> &mut Self {
|
||||
self.config.term_width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// The width of tab characters (default: None - do not turn tabs to spaces)
|
||||
pub fn tab_width(&mut self, tab_width: Option<usize>) -> &mut Self {
|
||||
self.config.tab_width = tab_width.unwrap_or(0);
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not the output should be colorized (default: true)
|
||||
pub fn colored_output(&mut self, yes: bool) -> &mut Self {
|
||||
self.config.colored_output = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Whether or not to output 24bit colors (default: true)
|
||||
pub fn true_color(&mut self, yes: bool) -> &mut Self {
|
||||
self.config.true_color = yes;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure style elements (grid, line numbers, ...)
|
||||
pub fn style_components(&mut self, components: StyleComponents) -> &mut Self {
|
||||
self.config.style_components = components;
|
||||
self
|
||||
}
|
||||
|
||||
/// Text wrapping mode (default: do not wrap)
|
||||
pub fn wrapping_mode(&mut self, wrapping_mode: WrappingMode) -> &mut Self {
|
||||
self.config.wrapping_mode = wrapping_mode;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn run(&'a self) -> Result<bool> {
|
||||
let controller = Controller::new(&self.config, &self.assets);
|
||||
controller.run()
|
||||
|
@ -31,10 +31,10 @@ use crate::inputfile::{InputFile, InputFileReader};
|
||||
use crate::line_range::RangeCheckResult;
|
||||
use crate::preprocessor::{expand_tabs, replace_nonprintable};
|
||||
use crate::terminal::{as_terminal_escaped, to_ansi_color};
|
||||
use crate::wrap::OutputWrap;
|
||||
use crate::wrap::WrappingMode;
|
||||
|
||||
pub trait Printer {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()>;
|
||||
fn print_header(&mut self, handle: &mut dyn Write, file: &InputFile) -> 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: InputFile) -> Result<()> {
|
||||
fn print_header(&mut self, _handle: &mut dyn Write, _file: &InputFile) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
pub fn new(
|
||||
config: &'a Config,
|
||||
assets: &'a HighlightingAssets,
|
||||
file: InputFile,
|
||||
file: &InputFile,
|
||||
reader: &mut InputFileReader,
|
||||
) -> Self {
|
||||
let theme = assets.get_theme(&config.theme);
|
||||
@ -230,7 +230,7 @@ impl<'a> InteractivePrinter<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Printer for InteractivePrinter<'a> {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, file: InputFile) -> Result<()> {
|
||||
fn print_header(&mut self, handle: &mut dyn Write, file: &InputFile) -> Result<()> {
|
||||
if !self.config.style_components.header() {
|
||||
if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable {
|
||||
let input = match file {
|
||||
@ -415,7 +415,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
|
||||
}
|
||||
|
||||
// Line contents.
|
||||
if self.config.output_wrap == OutputWrap::None {
|
||||
if self.config.wrapping_mode == WrappingMode::NoWrapping {
|
||||
let true_color = self.config.true_color;
|
||||
let colored_output = self.config.colored_output;
|
||||
let italics = self.config.use_italic_text;
|
||||
|
10
src/wrap.rs
10
src/wrap.rs
@ -1,11 +1,11 @@
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub enum OutputWrap {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum WrappingMode {
|
||||
Character,
|
||||
None,
|
||||
NoWrapping,
|
||||
}
|
||||
|
||||
impl Default for OutputWrap {
|
||||
impl Default for WrappingMode {
|
||||
fn default() -> Self {
|
||||
OutputWrap::None
|
||||
WrappingMode::NoWrapping
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user