mirror of
https://github.com/sharkdp/bat.git
synced 2025-01-19 04:21:06 +00:00
Extract features to separate module
This commit is contained in:
parent
ee43377a9c
commit
b21fb6bca8
112
src/features.rs
Normal file
112
src/features.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use ansi_term::Colour::Green;
|
||||||
|
use app::Config;
|
||||||
|
use assets::HighlightingAssets;
|
||||||
|
use diff::get_git_diff;
|
||||||
|
use errors::*;
|
||||||
|
use output::OutputType;
|
||||||
|
use printer::Printer;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, BufRead, BufReader};
|
||||||
|
use syntect::easy::HighlightLines;
|
||||||
|
use syntect::highlighting::Theme;
|
||||||
|
use syntect::parsing::SyntaxDefinition;
|
||||||
|
|
||||||
|
pub fn list_languages(assets: &HighlightingAssets, term_width: usize) {
|
||||||
|
let mut languages = assets
|
||||||
|
.syntax_set
|
||||||
|
.syntaxes()
|
||||||
|
.iter()
|
||||||
|
.filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
languages.sort_by_key(|lang| lang.name.to_uppercase());
|
||||||
|
|
||||||
|
let longest = languages
|
||||||
|
.iter()
|
||||||
|
.map(|syntax| syntax.name.len())
|
||||||
|
.max()
|
||||||
|
.unwrap_or(32); // Fallback width if they have no language definitions.
|
||||||
|
|
||||||
|
let comma_separator = ", ";
|
||||||
|
let separator = " ";
|
||||||
|
// Line-wrapping for the possible file extension overflow.
|
||||||
|
let desired_width = term_width - longest - separator.len();
|
||||||
|
|
||||||
|
for lang in languages {
|
||||||
|
print!("{:width$}{}", lang.name, separator, width = longest);
|
||||||
|
|
||||||
|
// Number of characters on this line so far, wrap before `desired_width`
|
||||||
|
let mut num_chars = 0;
|
||||||
|
|
||||||
|
let mut extension = lang.file_extensions.iter().peekable();
|
||||||
|
while let Some(word) = extension.next() {
|
||||||
|
// If we can't fit this word in, then create a line break and align it in.
|
||||||
|
let new_chars = word.len() + comma_separator.len();
|
||||||
|
if num_chars + new_chars >= desired_width {
|
||||||
|
num_chars = 0;
|
||||||
|
print!("\n{:width$}{}", "", separator, width = longest);
|
||||||
|
}
|
||||||
|
|
||||||
|
num_chars += new_chars;
|
||||||
|
print!("{}", Green.paint(word.as_ref()));
|
||||||
|
if extension.peek().is_some() {
|
||||||
|
print!("{}", comma_separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_files(assets: &HighlightingAssets, config: &Config) -> Result<bool> {
|
||||||
|
let theme = assets.get_theme(config.theme.unwrap_or("Default"))?;
|
||||||
|
let mut output_type = OutputType::from_mode(config.paging_mode);
|
||||||
|
let handle = output_type.handle()?;
|
||||||
|
let mut printer = Printer::new(handle, &config);
|
||||||
|
let mut no_errors: bool = true;
|
||||||
|
|
||||||
|
for file in &config.files {
|
||||||
|
printer.line_changes = file.and_then(|filename| get_git_diff(filename));
|
||||||
|
let syntax = assets.get_syntax(config.language, *file);
|
||||||
|
|
||||||
|
let result = print_file(theme, &syntax, &mut printer, *file);
|
||||||
|
|
||||||
|
if let Err(error) = result {
|
||||||
|
handle_error(&error);
|
||||||
|
no_errors = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(no_errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_file(
|
||||||
|
theme: &Theme,
|
||||||
|
syntax: &SyntaxDefinition,
|
||||||
|
printer: &mut Printer,
|
||||||
|
filename: Option<&str>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let stdin = io::stdin(); // TODO: this is not always needed
|
||||||
|
|
||||||
|
let mut reader: Box<BufRead> = match filename {
|
||||||
|
None => Box::new(stdin.lock()),
|
||||||
|
Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut highlighter = HighlightLines::new(syntax, theme);
|
||||||
|
|
||||||
|
printer.print_header(filename)?;
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
while reader.read_until(b'\n', &mut buffer)? > 0 {
|
||||||
|
{
|
||||||
|
let line = String::from_utf8_lossy(&buffer);
|
||||||
|
let regions = highlighter.highlight(line.as_ref());
|
||||||
|
|
||||||
|
printer.print_line(®ions)?;
|
||||||
|
}
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
printer.print_footer()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
138
src/main.rs
138
src/main.rs
@ -21,27 +21,20 @@ mod app;
|
|||||||
mod assets;
|
mod assets;
|
||||||
mod decorations;
|
mod decorations;
|
||||||
mod diff;
|
mod diff;
|
||||||
|
mod features;
|
||||||
mod output;
|
mod output;
|
||||||
mod printer;
|
mod printer;
|
||||||
mod style;
|
mod style;
|
||||||
mod terminal;
|
mod terminal;
|
||||||
|
|
||||||
use std::fs::{self, File};
|
use std::fs;
|
||||||
use std::io::{self, BufRead, BufReader};
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use ansi_term::Colour::{Green, Red};
|
|
||||||
|
|
||||||
use syntect::easy::HighlightLines;
|
|
||||||
use syntect::highlighting::Theme;
|
|
||||||
use syntect::parsing::SyntaxDefinition;
|
|
||||||
|
|
||||||
use app::App;
|
use app::App;
|
||||||
use assets::{config_dir, syntax_set_path, theme_set_path, HighlightingAssets};
|
use assets::{config_dir, syntax_set_path, theme_set_path, HighlightingAssets};
|
||||||
use diff::get_git_diff;
|
use features::{list_languages, print_files};
|
||||||
use output::OutputType;
|
|
||||||
use printer::Printer;
|
|
||||||
|
|
||||||
mod errors {
|
mod errors {
|
||||||
error_chain! {
|
error_chain! {
|
||||||
@ -50,45 +43,26 @@ mod errors {
|
|||||||
Io(::std::io::Error);
|
Io(::std::io::Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_error(error: &Error) {
|
||||||
|
match error {
|
||||||
|
&Error(ErrorKind::Io(ref io_error), _)
|
||||||
|
if io_error.kind() == super::io::ErrorKind::BrokenPipe =>
|
||||||
|
{
|
||||||
|
super::process::exit(0);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
use ansi_term::Colour::Red;
|
||||||
|
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
fn print_file(
|
|
||||||
theme: &Theme,
|
|
||||||
syntax: &SyntaxDefinition,
|
|
||||||
printer: &mut Printer,
|
|
||||||
filename: Option<&str>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let stdin = io::stdin(); // TODO: this is not always needed
|
|
||||||
|
|
||||||
let mut reader: Box<BufRead> = match filename {
|
|
||||||
None => Box::new(stdin.lock()),
|
|
||||||
Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut highlighter = HighlightLines::new(syntax, theme);
|
|
||||||
|
|
||||||
printer.print_header(filename)?;
|
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
|
||||||
while reader.read_until(b'\n', &mut buffer)? > 0 {
|
|
||||||
{
|
|
||||||
let line = String::from_utf8_lossy(&buffer);
|
|
||||||
let regions = highlighter.highlight(line.as_ref());
|
|
||||||
|
|
||||||
printer.print_line(®ions)?;
|
|
||||||
}
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
printer.print_footer()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Some(true)` on full success and
|
/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Some(true)` on full success and
|
||||||
/// `Some(false)` if any intermediate errors occured (were printed).
|
/// `Some(false)` if any intermediate errors occurred (were printed).
|
||||||
fn run() -> Result<bool> {
|
fn run() -> Result<bool> {
|
||||||
let app = App::new();
|
let app = App::new();
|
||||||
|
|
||||||
@ -116,52 +90,10 @@ fn run() -> Result<bool> {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let config = app.config()?;
|
let config = app.config()?;
|
||||||
|
|
||||||
let assets = HighlightingAssets::new();
|
let assets = HighlightingAssets::new();
|
||||||
let theme = assets.get_theme(config.theme.unwrap_or("Default"))?;
|
|
||||||
|
|
||||||
if app.matches.is_present("list-languages") {
|
if app.matches.is_present("list-languages") {
|
||||||
let mut languages = assets.syntax_set.syntaxes().to_owned();
|
list_languages(&assets, config.term_width);
|
||||||
languages.sort_by_key(|lang| lang.name.to_uppercase());
|
|
||||||
|
|
||||||
let longest = languages
|
|
||||||
.iter()
|
|
||||||
.filter(|s| !s.hidden && !s.file_extensions.is_empty())
|
|
||||||
.map(|s| s.name.len())
|
|
||||||
.max()
|
|
||||||
.unwrap_or(32); // Fallback width if they have no language definitions.
|
|
||||||
|
|
||||||
let separator = " ";
|
|
||||||
for lang in languages {
|
|
||||||
if lang.hidden || lang.file_extensions.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
print!("{:width$}{}", lang.name, separator, width = longest);
|
|
||||||
|
|
||||||
// Line-wrapping for the possible file extension overflow.
|
|
||||||
let desired_width = config.term_width - longest - separator.len();
|
|
||||||
// Number of characters on this line so far, wrap before `desired_width`
|
|
||||||
let mut num_chars = 0;
|
|
||||||
|
|
||||||
let comma_separator = ", ";
|
|
||||||
let mut extension = lang.file_extensions.iter().peekable();
|
|
||||||
while let Some(word) = extension.next() {
|
|
||||||
// If we can't fit this word in, then create a line break and align it in.
|
|
||||||
let new_chars = word.len() + comma_separator.len();
|
|
||||||
if num_chars + new_chars >= desired_width {
|
|
||||||
num_chars = 0;
|
|
||||||
print!("\n{:width$}{}", "", separator, width = longest);
|
|
||||||
}
|
|
||||||
|
|
||||||
num_chars += new_chars;
|
|
||||||
print!("{}", Green.paint(word as &str));
|
|
||||||
if extension.peek().is_some() {
|
|
||||||
print!("{}", comma_separator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,39 +105,11 @@ fn run() -> Result<bool> {
|
|||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output_type = OutputType::from_mode(config.paging_mode);
|
print_files(&assets, &config)
|
||||||
let handle = output_type.handle()?;
|
|
||||||
let mut printer = Printer::new(handle, &config);
|
|
||||||
let mut no_errors: bool = true;
|
|
||||||
|
|
||||||
for file in &config.files {
|
|
||||||
printer.line_changes = file.and_then(|filename| get_git_diff(filename));
|
|
||||||
let syntax = assets.get_syntax(config.language, *file);
|
|
||||||
|
|
||||||
let result = print_file(theme, &syntax, &mut printer, *file);
|
|
||||||
|
|
||||||
if let Err(error) = result {
|
|
||||||
handle_error(&error);
|
|
||||||
no_errors = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(no_errors)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_error(error: &Error) {
|
|
||||||
match error {
|
|
||||||
&Error(ErrorKind::Io(ref io_error), _) if io_error.kind() == io::ErrorKind::BrokenPipe => {
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let result = run();
|
let result = run();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user