2018-04-30 11:09:24 +02:00
|
|
|
// `error_chain!` can recurse deeply
|
|
|
|
#![recursion_limit = "1024"]
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
extern crate error_chain;
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
extern crate clap;
|
|
|
|
|
2018-04-30 15:08:04 +02:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
|
|
|
|
2018-04-21 12:51:43 +02:00
|
|
|
extern crate ansi_term;
|
|
|
|
extern crate atty;
|
|
|
|
extern crate console;
|
2018-04-30 15:08:04 +02:00
|
|
|
extern crate directories;
|
2018-04-21 17:12:25 +02:00
|
|
|
extern crate git2;
|
2018-04-21 12:51:43 +02:00
|
|
|
extern crate syntect;
|
|
|
|
|
2018-05-10 12:36:09 +02:00
|
|
|
mod assets;
|
|
|
|
mod diff;
|
2018-05-07 01:32:00 +02:00
|
|
|
mod printer;
|
2018-04-23 23:56:47 +02:00
|
|
|
mod terminal;
|
|
|
|
|
2018-05-10 12:50:40 +02:00
|
|
|
use std::collections::HashSet;
|
2018-04-22 16:03:47 +02:00
|
|
|
use std::env;
|
2018-04-30 15:08:04 +02:00
|
|
|
use std::fs::{self, File};
|
2018-05-07 22:49:55 +02:00
|
|
|
use std::io::{self, BufRead, BufReader, Write};
|
2018-05-06 14:53:52 +02:00
|
|
|
use std::process::{self, Child, Command, Stdio};
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
use std::str::FromStr;
|
2018-04-21 12:51:43 +02:00
|
|
|
|
2018-05-08 23:22:27 +02:00
|
|
|
#[cfg(unix)]
|
|
|
|
use std::os::unix::fs::FileTypeExt;
|
|
|
|
|
2018-04-22 13:27:20 +02:00
|
|
|
use ansi_term::Colour::{Fixed, Green, Red, White, Yellow};
|
2018-04-22 14:05:43 +02:00
|
|
|
use ansi_term::Style;
|
2018-04-21 12:51:43 +02:00
|
|
|
use atty::Stream;
|
2018-05-08 19:48:10 +02:00
|
|
|
use clap::{App, AppSettings, Arg, ArgGroup, SubCommand};
|
2018-04-21 12:51:43 +02:00
|
|
|
|
2018-05-02 14:29:07 +02:00
|
|
|
use syntect::easy::HighlightLines;
|
2018-05-10 12:36:09 +02:00
|
|
|
use syntect::highlighting::Theme;
|
2018-04-21 12:51:43 +02:00
|
|
|
use syntect::parsing::SyntaxSet;
|
2018-04-23 23:56:47 +02:00
|
|
|
|
2018-05-10 12:36:09 +02:00
|
|
|
use assets::{config_dir, syntax_set_path, theme_set_path, HighlightingAssets};
|
|
|
|
use diff::get_git_diff;
|
2018-05-07 01:32:00 +02:00
|
|
|
use printer::Printer;
|
2018-04-23 23:56:47 +02:00
|
|
|
|
2018-04-30 11:09:24 +02:00
|
|
|
mod errors {
|
2018-05-03 20:34:23 +02:00
|
|
|
error_chain! {
|
2018-04-30 11:09:24 +02:00
|
|
|
foreign_links {
|
2018-05-09 21:09:01 +02:00
|
|
|
Clap(::clap::Error);
|
2018-04-30 11:09:24 +02:00
|
|
|
Io(::std::io::Error);
|
|
|
|
}
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
|
|
|
|
errors {
|
|
|
|
NoCorrectStylesSpecified {
|
|
|
|
description("no correct styles specified")
|
|
|
|
}
|
|
|
|
|
|
|
|
UnknownStyleName(name: String) {
|
|
|
|
description("unknown style name")
|
|
|
|
display("unknown style name: '{}'", name)
|
|
|
|
}
|
|
|
|
}
|
2018-04-30 11:09:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
use errors::*;
|
|
|
|
|
2018-05-09 22:31:10 +02:00
|
|
|
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
pub enum OutputComponent {
|
|
|
|
Changes,
|
|
|
|
Grid,
|
|
|
|
Header,
|
|
|
|
Numbers,
|
2018-05-03 20:34:23 +02:00
|
|
|
Full,
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
Plain,
|
|
|
|
}
|
|
|
|
|
2018-05-09 21:09:01 +02:00
|
|
|
impl OutputComponent {
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
fn components(&self) -> &'static [OutputComponent] {
|
|
|
|
match *self {
|
2018-05-09 21:09:01 +02:00
|
|
|
OutputComponent::Changes => &[OutputComponent::Changes],
|
|
|
|
OutputComponent::Grid => &[OutputComponent::Grid],
|
|
|
|
OutputComponent::Header => &[OutputComponent::Header],
|
|
|
|
OutputComponent::Numbers => &[OutputComponent::Numbers],
|
|
|
|
OutputComponent::Full => &[
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
OutputComponent::Changes,
|
|
|
|
OutputComponent::Grid,
|
|
|
|
OutputComponent::Header,
|
|
|
|
OutputComponent::Numbers,
|
|
|
|
],
|
2018-05-09 21:09:01 +02:00
|
|
|
OutputComponent::Plain => &[],
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-09 21:09:01 +02:00
|
|
|
impl FromStr for OutputComponent {
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
type Err = Error;
|
|
|
|
|
|
|
|
fn from_str(s: &str) -> Result<Self> {
|
|
|
|
match s {
|
2018-05-09 21:09:01 +02:00
|
|
|
"changes" => Ok(OutputComponent::Changes),
|
|
|
|
"grid" => Ok(OutputComponent::Grid),
|
|
|
|
"header" => Ok(OutputComponent::Header),
|
|
|
|
"numbers" => Ok(OutputComponent::Numbers),
|
|
|
|
"full" => Ok(OutputComponent::Full),
|
|
|
|
"plain" => Ok(OutputComponent::Plain),
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
_ => Err(ErrorKind::UnknownStyleName(s.to_owned()).into()),
|
|
|
|
}
|
|
|
|
}
|
2018-05-03 20:34:23 +02:00
|
|
|
}
|
|
|
|
|
2018-05-09 22:31:10 +02:00
|
|
|
pub struct OutputComponents(HashSet<OutputComponent>);
|
|
|
|
|
|
|
|
impl OutputComponents {
|
|
|
|
fn changes(&self) -> bool {
|
|
|
|
self.0.contains(&OutputComponent::Changes)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn grid(&self) -> bool {
|
|
|
|
self.0.contains(&OutputComponent::Grid)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn header(&self) -> bool {
|
|
|
|
self.0.contains(&OutputComponent::Header)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn numbers(&self) -> bool {
|
|
|
|
self.0.contains(&OutputComponent::Numbers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-07 01:32:00 +02:00
|
|
|
pub struct Options<'a> {
|
|
|
|
pub true_color: bool,
|
2018-05-09 22:31:10 +02:00
|
|
|
pub output_components: OutputComponents,
|
2018-05-07 01:32:00 +02:00
|
|
|
pub language: Option<&'a str>,
|
|
|
|
pub colored_output: bool,
|
|
|
|
pub paging: bool,
|
2018-05-08 12:01:56 -07:00
|
|
|
pub term_width: usize,
|
2018-05-03 18:12:03 +02:00
|
|
|
}
|
|
|
|
|
2018-05-07 22:49:55 +02:00
|
|
|
enum OutputType {
|
2018-05-03 18:12:03 +02:00
|
|
|
Pager(Child),
|
2018-05-07 22:49:55 +02:00
|
|
|
Stdout(io::Stdout),
|
2018-05-03 18:12:03 +02:00
|
|
|
}
|
|
|
|
|
2018-05-07 22:49:55 +02:00
|
|
|
impl OutputType {
|
2018-05-06 19:26:41 +02:00
|
|
|
fn pager() -> Result<Self> {
|
2018-05-03 18:12:03 +02:00
|
|
|
Ok(OutputType::Pager(Command::new("less")
|
|
|
|
.args(&["--quit-if-one-screen", "--RAW-CONTROL-CHARS", "--no-init"])
|
|
|
|
.stdin(Stdio::piped())
|
|
|
|
.spawn()
|
2018-05-06 14:53:52 +02:00
|
|
|
.chain_err(|| "Could not spawn pager")?))
|
2018-05-03 18:12:03 +02:00
|
|
|
}
|
|
|
|
|
2018-05-07 22:49:55 +02:00
|
|
|
fn stdout() -> Self {
|
|
|
|
OutputType::Stdout(io::stdout())
|
2018-05-03 18:12:03 +02:00
|
|
|
}
|
|
|
|
|
2018-05-06 19:26:41 +02:00
|
|
|
fn handle(&mut self) -> Result<&mut Write> {
|
2018-05-03 18:12:03 +02:00
|
|
|
Ok(match *self {
|
2018-05-06 14:53:52 +02:00
|
|
|
OutputType::Pager(ref mut command) => command
|
|
|
|
.stdin
|
|
|
|
.as_mut()
|
|
|
|
.chain_err(|| "Could not open stdin for pager")?,
|
2018-05-03 18:12:03 +02:00
|
|
|
OutputType::Stdout(ref mut handle) => handle,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-07 22:49:55 +02:00
|
|
|
impl Drop for OutputType {
|
2018-05-03 18:12:03 +02:00
|
|
|
fn drop(&mut self) {
|
|
|
|
if let OutputType::Pager(ref mut command) = *self {
|
|
|
|
let _ = command.wait();
|
|
|
|
}
|
|
|
|
}
|
2018-04-23 23:56:47 +02:00
|
|
|
}
|
2018-04-21 12:51:43 +02:00
|
|
|
|
2018-04-22 14:05:43 +02:00
|
|
|
const GRID_COLOR: u8 = 238;
|
2018-05-05 15:55:16 +02:00
|
|
|
const LINE_NUMBER_COLOR: u8 = 244;
|
|
|
|
|
|
|
|
#[derive(Default)]
|
2018-05-07 01:32:00 +02:00
|
|
|
pub struct Colors {
|
|
|
|
pub grid: Style,
|
|
|
|
pub filename: Style,
|
|
|
|
pub git_added: Style,
|
|
|
|
pub git_removed: Style,
|
|
|
|
pub git_modified: Style,
|
|
|
|
pub line_number: Style,
|
2018-05-05 15:55:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Colors {
|
|
|
|
fn plain() -> Self {
|
|
|
|
Colors::default()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn colored() -> Self {
|
|
|
|
Colors {
|
|
|
|
grid: Fixed(GRID_COLOR).normal(),
|
|
|
|
filename: White.bold(),
|
|
|
|
git_added: Green.normal(),
|
|
|
|
git_removed: Red.normal(),
|
|
|
|
git_modified: Yellow.normal(),
|
|
|
|
line_number: Fixed(LINE_NUMBER_COLOR).normal(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-22 13:53:04 +02:00
|
|
|
|
2018-05-07 19:59:40 +02:00
|
|
|
fn print_file(
|
2018-04-23 23:56:47 +02:00
|
|
|
options: &Options,
|
2018-04-22 13:27:20 +02:00
|
|
|
theme: &Theme,
|
2018-04-22 14:05:43 +02:00
|
|
|
syntax_set: &SyntaxSet,
|
2018-05-07 01:32:00 +02:00
|
|
|
printer: &mut Printer,
|
2018-05-07 19:59:40 +02:00
|
|
|
filename: Option<&str>,
|
2018-04-30 11:09:24 +02:00
|
|
|
) -> Result<()> {
|
2018-05-07 19:59:40 +02:00
|
|
|
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 syntax = match (options.language, filename) {
|
|
|
|
(Some(language), _) => syntax_set.find_syntax_by_token(language),
|
2018-05-08 23:22:27 +02:00
|
|
|
(None, Some(filename)) => {
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
let may_read_from_file = true;
|
|
|
|
|
|
|
|
// Do not peek at the file (to determine the syntax) if it is a FIFO because they can
|
|
|
|
// only be read once.
|
|
|
|
#[cfg(unix)]
|
|
|
|
let may_read_from_file = !fs::metadata(filename)?.file_type().is_fifo();
|
|
|
|
|
|
|
|
if may_read_from_file {
|
|
|
|
syntax_set.find_syntax_for_file(filename)?
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2018-05-07 19:59:40 +02:00
|
|
|
(None, None) => None,
|
2018-05-02 14:29:07 +02:00
|
|
|
};
|
2018-05-05 15:55:16 +02:00
|
|
|
|
2018-05-02 14:29:07 +02:00
|
|
|
let syntax = syntax.unwrap_or_else(|| syntax_set.find_syntax_plain_text());
|
|
|
|
let mut highlighter = HighlightLines::new(syntax, theme);
|
2018-04-21 12:51:43 +02:00
|
|
|
|
2018-05-07 19:59:40 +02:00
|
|
|
printer.print_header(filename)?;
|
2018-04-21 17:36:57 +02:00
|
|
|
|
2018-05-06 14:23:25 +02:00
|
|
|
let mut line_nr = 1;
|
|
|
|
let mut line_buffer = String::new();
|
|
|
|
loop {
|
|
|
|
line_buffer.clear();
|
|
|
|
let num_bytes = reader.read_line(&mut line_buffer);
|
|
|
|
|
|
|
|
let line = match num_bytes {
|
|
|
|
Ok(0) => {
|
|
|
|
break;
|
|
|
|
}
|
2018-05-08 23:49:59 +02:00
|
|
|
Ok(_) => {
|
2018-05-10 12:50:40 +02:00
|
|
|
if !line_buffer.ends_with('\n') {
|
2018-05-08 23:49:59 +02:00
|
|
|
line_buffer.push('\n');
|
|
|
|
}
|
|
|
|
&line_buffer
|
|
|
|
}
|
2018-05-06 14:23:25 +02:00
|
|
|
Err(_) => "<bat: INVALID UTF-8>\n",
|
|
|
|
};
|
|
|
|
|
|
|
|
let regions = highlighter.highlight(line);
|
2018-04-21 12:51:43 +02:00
|
|
|
|
2018-05-07 01:32:00 +02:00
|
|
|
printer.print_line(line_nr, ®ions)?;
|
2018-05-06 14:23:25 +02:00
|
|
|
|
|
|
|
line_nr += 1;
|
2018-04-21 12:51:43 +02:00
|
|
|
}
|
|
|
|
|
2018-05-07 01:32:00 +02:00
|
|
|
printer.print_footer()?;
|
2018-04-21 17:12:25 +02:00
|
|
|
|
2018-04-21 12:51:43 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-05-07 22:49:55 +02:00
|
|
|
fn get_output_type(paging: bool) -> OutputType {
|
2018-05-06 19:38:28 +02:00
|
|
|
if paging {
|
2018-05-07 22:49:55 +02:00
|
|
|
OutputType::pager().unwrap_or_else(|_| OutputType::stdout())
|
2018-05-06 19:26:41 +02:00
|
|
|
} else {
|
2018-05-07 22:49:55 +02:00
|
|
|
OutputType::stdout()
|
2018-05-06 19:26:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-30 15:08:04 +02:00
|
|
|
fn is_truecolor_terminal() -> bool {
|
|
|
|
env::var("COLORTERM")
|
|
|
|
.map(|colorterm| colorterm == "truecolor" || colorterm == "24bit")
|
|
|
|
.unwrap_or(false)
|
|
|
|
}
|
2018-04-22 16:03:47 +02:00
|
|
|
|
2018-04-30 15:08:04 +02:00
|
|
|
fn run() -> Result<()> {
|
2018-05-03 18:12:03 +02:00
|
|
|
let interactive_terminal = atty::is(Stream::Stdout);
|
|
|
|
|
|
|
|
let clap_color_setting = if interactive_terminal {
|
2018-04-21 12:51:43 +02:00
|
|
|
AppSettings::ColoredHelp
|
|
|
|
} else {
|
|
|
|
AppSettings::ColorNever
|
|
|
|
};
|
|
|
|
|
2018-04-30 15:08:04 +02:00
|
|
|
let app_matches = App::new(crate_name!())
|
2018-04-21 12:51:43 +02:00
|
|
|
.version(crate_version!())
|
2018-05-08 19:48:10 +02:00
|
|
|
.global_setting(clap_color_setting)
|
|
|
|
.global_setting(AppSettings::DeriveDisplayOrder)
|
|
|
|
.global_setting(AppSettings::UnifiedHelpMessage)
|
|
|
|
.global_setting(AppSettings::NextLineHelp)
|
|
|
|
.setting(AppSettings::InferSubcommands)
|
|
|
|
.setting(AppSettings::ArgsNegateSubcommands)
|
|
|
|
.setting(AppSettings::DisableHelpSubcommand)
|
|
|
|
.setting(AppSettings::VersionlessSubcommands)
|
2018-04-21 12:51:43 +02:00
|
|
|
.max_term_width(90)
|
2018-04-22 13:45:40 +02:00
|
|
|
.about(crate_description!())
|
2018-05-03 20:34:23 +02:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("language")
|
|
|
|
.short("l")
|
|
|
|
.long("language")
|
2018-05-06 18:23:43 +02:00
|
|
|
.help("Set the language for highlighting")
|
2018-05-03 20:34:23 +02:00
|
|
|
.takes_value(true),
|
2018-05-02 14:29:07 +02:00
|
|
|
)
|
2018-04-21 12:51:43 +02:00
|
|
|
.arg(
|
2018-04-22 13:45:40 +02:00
|
|
|
Arg::with_name("FILE")
|
|
|
|
.help("File(s) to print")
|
2018-04-21 12:51:43 +02:00
|
|
|
.multiple(true)
|
|
|
|
.empty_values(false),
|
|
|
|
)
|
2018-05-03 20:34:23 +02:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("style")
|
|
|
|
.long("style")
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
.use_delimiter(true)
|
|
|
|
.takes_value(true)
|
|
|
|
.possible_values(&["full", "plain", "changes", "header", "grid", "numbers"])
|
|
|
|
.default_value("full")
|
2018-05-05 15:55:16 +02:00
|
|
|
.help("Additional info to display along with content"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("color")
|
|
|
|
.long("color")
|
|
|
|
.takes_value(true)
|
|
|
|
.possible_values(&["auto", "never", "always"])
|
|
|
|
.default_value("auto")
|
2018-05-06 14:53:52 +02:00
|
|
|
.help("When to use colors"),
|
2018-05-03 20:34:23 +02:00
|
|
|
)
|
2018-05-06 19:38:28 +02:00
|
|
|
.arg(
|
|
|
|
Arg::with_name("paging")
|
|
|
|
.long("paging")
|
|
|
|
.takes_value(true)
|
|
|
|
.possible_values(&["auto", "never", "always"])
|
|
|
|
.default_value("auto")
|
2018-05-07 01:32:00 +02:00
|
|
|
.help("When to use the pager"),
|
2018-05-06 19:38:28 +02:00
|
|
|
)
|
2018-05-07 08:36:02 -07:00
|
|
|
.arg(
|
2018-05-07 13:00:23 -07:00
|
|
|
Arg::with_name("list-languages")
|
2018-05-07 08:36:02 -07:00
|
|
|
.long("list-languages")
|
2018-05-08 22:29:38 +02:00
|
|
|
.help("Displays supported languages"),
|
2018-05-07 08:36:02 -07:00
|
|
|
)
|
2018-04-30 15:08:04 +02:00
|
|
|
.subcommand(
|
2018-05-08 19:48:10 +02:00
|
|
|
SubCommand::with_name("cache")
|
|
|
|
.about("Modify the syntax-definition and theme cache")
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("init")
|
|
|
|
.long("init")
|
|
|
|
.short("i")
|
|
|
|
.help("Initialize the cache by loading from the config dir"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("clear")
|
|
|
|
.long("clear")
|
|
|
|
.short("c")
|
|
|
|
.help("Reset the cache"),
|
|
|
|
)
|
|
|
|
.arg(
|
|
|
|
Arg::with_name("config-dir")
|
|
|
|
.long("config-dir")
|
|
|
|
.short("d")
|
|
|
|
.help("Show the configuration directory"),
|
|
|
|
)
|
|
|
|
.group(
|
|
|
|
ArgGroup::with_name("cache-actions")
|
|
|
|
.args(&["init", "clear", "config-dir"])
|
|
|
|
.required(true),
|
|
|
|
),
|
2018-04-30 15:08:04 +02:00
|
|
|
)
|
2018-04-21 12:51:43 +02:00
|
|
|
.help_message("Print this help message.")
|
|
|
|
.version_message("Show version information.")
|
|
|
|
.get_matches();
|
|
|
|
|
2018-04-30 15:08:04 +02:00
|
|
|
match app_matches.subcommand() {
|
2018-05-08 19:48:10 +02:00
|
|
|
("cache", Some(cache_matches)) => {
|
|
|
|
if cache_matches.is_present("init") {
|
|
|
|
let assets = HighlightingAssets::from_files()?;
|
|
|
|
assets.save()?;
|
|
|
|
} else if cache_matches.is_present("clear") {
|
|
|
|
print!("Clearing theme set cache ... ");
|
2018-05-10 12:36:09 +02:00
|
|
|
fs::remove_file(theme_set_path())?;
|
2018-05-08 19:48:10 +02:00
|
|
|
println!("okay");
|
|
|
|
|
|
|
|
print!("Clearing syntax set cache ... ");
|
2018-05-10 12:36:09 +02:00
|
|
|
fs::remove_file(syntax_set_path())?;
|
2018-05-08 19:48:10 +02:00
|
|
|
println!("okay");
|
|
|
|
} else if cache_matches.is_present("config-dir") {
|
2018-05-10 12:36:09 +02:00
|
|
|
println!("{}", config_dir());
|
2018-05-08 19:48:10 +02:00
|
|
|
}
|
2018-04-30 15:08:04 +02:00
|
|
|
}
|
|
|
|
_ => {
|
2018-05-07 22:59:30 +02:00
|
|
|
let files: Vec<Option<&str>> = app_matches
|
|
|
|
.values_of("FILE")
|
|
|
|
.map(|values| {
|
|
|
|
values
|
|
|
|
.map(|filename| {
|
|
|
|
if filename == "-" {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(filename)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
})
|
|
|
|
.unwrap_or_else(|| vec![None]); // read from stdin (None) if no args are given
|
|
|
|
|
2018-05-09 21:09:01 +02:00
|
|
|
let output_components = values_t!(app_matches.values_of("style"), OutputComponent)?
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
.into_iter()
|
2018-05-09 21:09:01 +02:00
|
|
|
.map(|style| style.components())
|
2018-05-09 22:31:10 +02:00
|
|
|
.fold(HashSet::new(), |mut acc, components| {
|
|
|
|
acc.extend(components.iter().cloned());
|
2018-05-09 21:09:01 +02:00
|
|
|
acc
|
|
|
|
});
|
Make `--style` parameter more flexible
The `--style` parameter now accepts a comma-separated list of strings,
where every element defines either a single output component (`changes`,
`grid`, `header`, `numbers`) or a predefined style (`full`,
`line-numbers`, `plain`).
If available, bat picks the first predefined style in the user-supplied
style-list and ignores everything else. If no predefined style was
requested, the other parameters that are simple output components will
be used.
Examples:
--style changes,full,numbers
Will internally be reduced to only the predefined style `full`.
--style plain,full
Will internally be reduced to only the predefined style `plain`.
--style changes,numbers
Will not be reduced, because the list does not contain any predefined
styles.
(Note: if `grid` is requested but no other parameters, bat still creates
the left-most column with a width of `PANEL_WIDTH`. I didn't want to
introduce further logic in this PR that drops or adapts the width of the
left column.)
2018-05-06 20:15:46 +02:00
|
|
|
|
2018-04-30 15:08:04 +02:00
|
|
|
let options = Options {
|
|
|
|
true_color: is_truecolor_terminal(),
|
2018-05-09 22:31:10 +02:00
|
|
|
output_components: OutputComponents(output_components),
|
2018-05-02 14:29:07 +02:00
|
|
|
language: app_matches.value_of("language"),
|
2018-05-05 15:55:16 +02:00
|
|
|
colored_output: match app_matches.value_of("color") {
|
|
|
|
Some("always") => true,
|
|
|
|
Some("never") => false,
|
|
|
|
_ => interactive_terminal,
|
|
|
|
},
|
2018-05-06 19:38:28 +02:00
|
|
|
paging: match app_matches.value_of("paging") {
|
|
|
|
Some("always") => true,
|
|
|
|
Some("never") => false,
|
2018-05-07 22:59:30 +02:00
|
|
|
Some("auto") | _ => if files.contains(&None) {
|
|
|
|
// If we are reading from stdin, only enable paging if we write to an
|
|
|
|
// interactive terminal and if we do not *read* from an interactive
|
|
|
|
// terminal.
|
|
|
|
interactive_terminal && !atty::is(Stream::Stdin)
|
|
|
|
} else {
|
|
|
|
interactive_terminal
|
|
|
|
},
|
2018-05-07 01:32:00 +02:00
|
|
|
},
|
2018-05-08 12:01:56 -07:00
|
|
|
term_width: console::Term::stdout().size().1 as usize,
|
2018-04-30 15:08:04 +02:00
|
|
|
};
|
|
|
|
|
2018-05-10 12:36:09 +02:00
|
|
|
let assets = HighlightingAssets::new();
|
|
|
|
let theme = assets.default_theme()?;
|
2018-04-30 15:08:04 +02:00
|
|
|
|
2018-05-07 13:00:23 -07:00
|
|
|
if app_matches.is_present("list-languages") {
|
2018-05-07 09:25:47 -07:00
|
|
|
let languages = assets.syntax_set.syntaxes();
|
|
|
|
|
2018-05-08 22:13:37 +02:00
|
|
|
let longest = languages
|
|
|
|
.iter()
|
2018-05-10 12:50:40 +02:00
|
|
|
.filter(|s| !s.hidden && !s.file_extensions.is_empty())
|
2018-05-07 12:08:39 -07:00
|
|
|
.map(|s| s.name.len())
|
2018-05-07 13:00:23 -07:00
|
|
|
.max()
|
|
|
|
.unwrap_or(32); // Fallback width if they have no language definitions.
|
2018-05-07 09:25:47 -07:00
|
|
|
|
2018-05-08 22:29:38 +02:00
|
|
|
let separator = " ";
|
2018-05-07 12:08:39 -07:00
|
|
|
for lang in languages {
|
2018-05-10 12:50:40 +02:00
|
|
|
if lang.hidden || lang.file_extensions.is_empty() {
|
2018-05-08 11:50:56 -07:00
|
|
|
continue;
|
|
|
|
}
|
2018-05-07 14:42:58 -07:00
|
|
|
print!("{:width$}{}", lang.name, separator, width = longest);
|
|
|
|
|
|
|
|
// Line-wrapping for the possible file extension overflow.
|
2018-05-08 12:20:45 -07:00
|
|
|
let desired_width = options.term_width - longest - separator.len();
|
2018-05-07 14:42:58 -07:00
|
|
|
// Number of characters on this line so far, wrap before `desired_width`
|
|
|
|
let mut num_chars = 0;
|
|
|
|
|
2018-05-08 12:20:45 -07:00
|
|
|
let comma_separator = ", ";
|
2018-05-07 14:42:58 -07:00
|
|
|
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.
|
2018-05-08 22:13:37 +02:00
|
|
|
let new_chars = word.len() + comma_separator.len();
|
|
|
|
if num_chars + new_chars >= desired_width {
|
2018-05-07 14:42:58 -07:00
|
|
|
num_chars = 0;
|
|
|
|
print!("\n{:width$}{}", "", separator, width = longest);
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:13:37 +02:00
|
|
|
num_chars += new_chars;
|
2018-05-08 22:29:38 +02:00
|
|
|
print!("{}", Green.paint(word as &str));
|
2018-05-07 14:42:58 -07:00
|
|
|
if extension.peek().is_some() {
|
2018-05-08 12:20:45 -07:00
|
|
|
print!("{}", comma_separator);
|
2018-05-07 14:42:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
println!();
|
2018-05-07 09:25:47 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
2018-05-07 22:49:55 +02:00
|
|
|
let mut output_type = get_output_type(options.paging);
|
2018-05-07 19:59:40 +02:00
|
|
|
let handle = output_type.handle()?;
|
|
|
|
let mut printer = Printer::new(handle, &options);
|
|
|
|
|
|
|
|
for file in files {
|
|
|
|
printer.line_changes = file.and_then(|filename| get_git_diff(filename));
|
|
|
|
|
|
|
|
print_file(&options, theme, &assets.syntax_set, &mut printer, file)?;
|
2018-04-30 15:08:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let result = run();
|
2018-04-21 12:51:43 +02:00
|
|
|
|
2018-04-30 11:09:24 +02:00
|
|
|
if let Err(error) = result {
|
|
|
|
match error {
|
|
|
|
Error(ErrorKind::Io(ref io_error), _)
|
2018-05-06 14:53:52 +02:00
|
|
|
if io_error.kind() == io::ErrorKind::BrokenPipe => {}
|
2018-04-30 11:09:24 +02:00
|
|
|
_ => {
|
|
|
|
eprintln!("{}: {}", Red.paint("[bat error]"), error);
|
|
|
|
|
|
|
|
process::exit(1);
|
|
|
|
}
|
|
|
|
};
|
2018-04-21 12:51:43 +02:00
|
|
|
}
|
|
|
|
}
|