mirror of
https://github.com/sharkdp/bat.git
synced 2024-11-07 16:34:13 +01:00
Split decorations into a separate file and optimized them a bit.
This commit is contained in:
parent
9214a4a4f0
commit
b327127f37
146
src/decorations.rs
Normal file
146
src/decorations.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use ansi_term::Style;
|
||||
use diff::LineChange;
|
||||
use printer::Printer;
|
||||
use Colors;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DecorationText {
|
||||
pub width: usize,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub trait Decoration {
|
||||
fn for_line(&self, line_number: usize, printer: &Printer) -> DecorationText;
|
||||
fn for_wrap(&self, line_number: usize, printer: &Printer) -> DecorationText;
|
||||
fn width(&self) -> usize;
|
||||
}
|
||||
|
||||
// Line number decoration.
|
||||
pub struct LineNumberDecoration {
|
||||
color: Style,
|
||||
cached_wrap: DecorationText,
|
||||
cached_wrap_invalid_at: usize,
|
||||
}
|
||||
|
||||
impl LineNumberDecoration {
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
LineNumberDecoration {
|
||||
color: colors.line_number,
|
||||
cached_wrap_invalid_at: 10000,
|
||||
cached_wrap: DecorationText {
|
||||
text: colors.line_number.paint(" ".repeat(4)).to_string(),
|
||||
width: 4,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoration for LineNumberDecoration {
|
||||
fn for_line(&self, line_number: usize, _printer: &Printer) -> DecorationText {
|
||||
let plain: String = format!("{:4}", line_number);
|
||||
DecorationText {
|
||||
width: plain.len(),
|
||||
text: self.color.paint(plain).to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
fn for_wrap(&self, line_number: usize, _printer: &Printer) -> DecorationText {
|
||||
if line_number > self.cached_wrap_invalid_at {
|
||||
let new_width = self.cached_wrap.width + 1;
|
||||
return DecorationText {
|
||||
text: self.color.paint(" ".repeat(new_width)).to_string(),
|
||||
width: new_width,
|
||||
};
|
||||
}
|
||||
|
||||
self.cached_wrap.clone()
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
// Line changes decoration.
|
||||
pub struct LineChangesDecoration {
|
||||
cached_none: DecorationText,
|
||||
cached_added: DecorationText,
|
||||
cached_removed_above: DecorationText,
|
||||
cached_removed_below: DecorationText,
|
||||
cached_modified: DecorationText,
|
||||
}
|
||||
|
||||
impl LineChangesDecoration {
|
||||
#[inline]
|
||||
fn generate_cached(style: Style, text: &str) -> DecorationText {
|
||||
DecorationText {
|
||||
text: style.paint(text).to_string(),
|
||||
width: text.chars().count(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
LineChangesDecoration {
|
||||
cached_none: Self::generate_cached(Style::default(), " "),
|
||||
cached_added: Self::generate_cached(colors.git_added, "+"),
|
||||
cached_removed_above: Self::generate_cached(colors.git_removed, "‾"),
|
||||
cached_removed_below: Self::generate_cached(colors.git_removed, "_"),
|
||||
cached_modified: Self::generate_cached(colors.git_modified, "~"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoration for LineChangesDecoration {
|
||||
fn for_line(&self, line_number: usize, printer: &Printer) -> DecorationText {
|
||||
if let Some(ref changes) = printer.line_changes {
|
||||
match changes.get(&(line_number as u32)) {
|
||||
Some(&LineChange::Added) => self.cached_added.clone(),
|
||||
Some(&LineChange::RemovedAbove) => self.cached_removed_above.clone(),
|
||||
Some(&LineChange::RemovedBelow) => self.cached_removed_below.clone(),
|
||||
Some(&LineChange::Modified) => self.cached_modified.clone(),
|
||||
_ => self.cached_none.clone(),
|
||||
}
|
||||
} else {
|
||||
self.cached_none.clone()
|
||||
}
|
||||
// let status = printer.line_changes.and_then(|ref changes| changes.get(&(line_number as u32)));
|
||||
}
|
||||
|
||||
fn for_wrap(&self, _line_number: usize, _printer: &Printer) -> DecorationText {
|
||||
self.cached_none.clone()
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
self.cached_none.width
|
||||
}
|
||||
}
|
||||
|
||||
// Grid border decoration.
|
||||
pub struct GridBorderDecoration {
|
||||
cached: DecorationText,
|
||||
}
|
||||
|
||||
impl GridBorderDecoration {
|
||||
pub fn new(colors: &Colors) -> Self {
|
||||
GridBorderDecoration {
|
||||
cached: DecorationText {
|
||||
text: colors.grid.paint("│").to_string(),
|
||||
width: 1,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoration for GridBorderDecoration {
|
||||
fn for_line(&self, _line_number: usize, _printer: &Printer) -> DecorationText {
|
||||
self.cached.clone()
|
||||
}
|
||||
|
||||
fn for_wrap(&self, _line_number: usize, _printer: &Printer) -> DecorationText {
|
||||
self.cached.clone()
|
||||
}
|
||||
|
||||
fn width(&self) -> usize {
|
||||
self.cached.width
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ mod diff;
|
||||
mod printer;
|
||||
mod style;
|
||||
mod terminal;
|
||||
mod decorations;
|
||||
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, BufRead, BufReader, Write};
|
||||
|
162
src/printer.rs
162
src/printer.rs
@ -1,24 +1,20 @@
|
||||
use ansi_term::Style;
|
||||
use app::Config;
|
||||
use diff::{LineChange, LineChanges};
|
||||
use diff::LineChanges;
|
||||
use errors::*;
|
||||
use std::io::Write;
|
||||
use std::vec::Vec;
|
||||
use std::boxed::Box;
|
||||
use syntect::highlighting;
|
||||
use terminal::as_terminal_escaped;
|
||||
use style::OutputWrap;
|
||||
use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
|
||||
use Colors;
|
||||
|
||||
const LINE_NUMBER_WIDTH: usize = 4;
|
||||
|
||||
struct PrintSegment {
|
||||
size: usize,
|
||||
text: String,
|
||||
}
|
||||
|
||||
pub struct Printer<'a> {
|
||||
handle: &'a mut Write,
|
||||
colors: Colors,
|
||||
config: &'a Config<'a>,
|
||||
decorations: Vec<Box<Decoration>>,
|
||||
panel_width: usize,
|
||||
pub line_changes: Option<LineChanges>,
|
||||
}
|
||||
@ -31,21 +27,36 @@ impl<'a> Printer<'a> {
|
||||
Colors::plain()
|
||||
};
|
||||
|
||||
// Create the instance.
|
||||
let mut instance = Printer {
|
||||
// Create decorations.
|
||||
let mut decorations: Vec<Box<Decoration>> = Vec::new();
|
||||
|
||||
if config.output_components.numbers() {
|
||||
decorations.push(Box::new(LineNumberDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
if config.output_components.changes() {
|
||||
decorations.push(Box::new(LineChangesDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
let panel_width: usize =
|
||||
decorations.len() + decorations.iter().fold(0, |a, x| a + x.width());
|
||||
|
||||
// The grid border decoration isn't added until after the panel_width calculation, since the
|
||||
// print_horizontal_line, print_header, and print_footer functions all assume the panel
|
||||
// width is without the grid border.
|
||||
if config.output_components.grid() && decorations.len() > 0 {
|
||||
decorations.push(Box::new(GridBorderDecoration::new(&colors)));
|
||||
}
|
||||
|
||||
// Create printer.
|
||||
Printer {
|
||||
panel_width,
|
||||
handle,
|
||||
colors,
|
||||
config,
|
||||
panel_width: 0,
|
||||
decorations,
|
||||
line_changes: None,
|
||||
};
|
||||
|
||||
// Generate the panel (gutter) width.
|
||||
let decorations = instance.line_decorations(0);
|
||||
instance.panel_width = decorations.len() + decorations.iter().fold(0, |a, x| a + x.size);
|
||||
|
||||
// Return the instance.
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
|
||||
@ -95,36 +106,20 @@ impl<'a> Printer<'a> {
|
||||
) -> Result<()> {
|
||||
let mut cursor: usize = 0;
|
||||
let mut cursor_max: usize = self.config.term_width;
|
||||
let mut panel_wrap: Option<String> = None;
|
||||
|
||||
// Line decoration.
|
||||
let decorations = self.line_decorations(line_number);
|
||||
let gutter_width = decorations.len() + decorations.iter().fold(0, |a, x| a + x.size);
|
||||
// Line decorations.
|
||||
if self.panel_width > 0 {
|
||||
let decorations = self.decorations
|
||||
.iter()
|
||||
.map(|ref d| d.for_line(line_number, self))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if gutter_width > 0 {
|
||||
cursor_max -= gutter_width;
|
||||
write!(
|
||||
self.handle,
|
||||
"{} ",
|
||||
decorations
|
||||
.iter()
|
||||
.map(|seg| seg.text.to_owned())
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
)?;
|
||||
}
|
||||
|
||||
// Grid border.
|
||||
let border = if gutter_width > 0 && self.config.output_components.grid() {
|
||||
self.line_border()
|
||||
} else {
|
||||
PrintSegment {
|
||||
size: 0,
|
||||
text: "".to_owned(),
|
||||
for deco in decorations {
|
||||
write!(self.handle, "{} ", deco.text)?;
|
||||
cursor_max -= deco.width + 1;
|
||||
}
|
||||
};
|
||||
|
||||
cursor_max -= border.size;
|
||||
write!(self.handle, "{}", border.text)?;
|
||||
}
|
||||
|
||||
// Line contents.
|
||||
if self.config.output_wrap == OutputWrap::None {
|
||||
@ -140,7 +135,7 @@ impl<'a> Printer<'a> {
|
||||
style,
|
||||
text,
|
||||
true_color,
|
||||
colored_output
|
||||
colored_output,
|
||||
))
|
||||
.collect::<Vec<_>>()
|
||||
.join("")
|
||||
@ -171,6 +166,22 @@ impl<'a> Printer<'a> {
|
||||
break;
|
||||
}
|
||||
|
||||
// Generate wrap padding if not already generated.
|
||||
if panel_wrap.is_none() {
|
||||
panel_wrap = if self.panel_width > 0 {
|
||||
Some(format!(
|
||||
"{} ",
|
||||
self.decorations
|
||||
.iter()
|
||||
.map(|ref d| d.for_wrap(line_number, self).text)
|
||||
.collect::<Vec<String>>()
|
||||
.join(" ")
|
||||
))
|
||||
} else {
|
||||
Some("".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// It wraps.
|
||||
let text = chars.by_ref().take(available).collect::<String>();
|
||||
cursor = 0;
|
||||
@ -178,15 +189,14 @@ impl<'a> Printer<'a> {
|
||||
|
||||
write!(
|
||||
self.handle,
|
||||
"{}\n{}{}",
|
||||
"{}\n{}",
|
||||
as_terminal_escaped(
|
||||
style,
|
||||
&*text,
|
||||
self.config.true_color,
|
||||
self.config.colored_output,
|
||||
),
|
||||
" ".repeat(gutter_width),
|
||||
border.text.to_owned()
|
||||
panel_wrap.clone().unwrap()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
@ -197,56 +207,6 @@ impl<'a> Printer<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn line_decorations(&self, line_number: usize) -> Vec<PrintSegment> {
|
||||
let mut decorations = Vec::new();
|
||||
|
||||
if self.config.output_components.numbers() {
|
||||
decorations.push(self.line_number(line_number));
|
||||
}
|
||||
|
||||
if self.config.output_components.changes() {
|
||||
decorations.push(self.line_changes(line_number));
|
||||
}
|
||||
|
||||
return decorations;
|
||||
}
|
||||
|
||||
fn line_number(&self, line_number: usize) -> PrintSegment {
|
||||
let plain: String = format!("{:width$}", line_number, width = LINE_NUMBER_WIDTH);
|
||||
let color = self.colors.line_number.paint(plain.clone());
|
||||
|
||||
return PrintSegment {
|
||||
text: color.to_string(),
|
||||
size: plain.len(),
|
||||
};
|
||||
}
|
||||
|
||||
fn line_changes(&self, line_number: usize) -> PrintSegment {
|
||||
let color = if let Some(ref changes) = self.line_changes {
|
||||
match changes.get(&(line_number as u32)) {
|
||||
Some(&LineChange::Added) => self.colors.git_added.paint("+"),
|
||||
Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
|
||||
Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
|
||||
Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
|
||||
_ => Style::default().paint(" "),
|
||||
}
|
||||
} else {
|
||||
Style::default().paint(" ")
|
||||
};
|
||||
|
||||
return PrintSegment {
|
||||
text: color.to_string(),
|
||||
size: 1,
|
||||
};
|
||||
}
|
||||
|
||||
fn line_border(&self) -> PrintSegment {
|
||||
return PrintSegment {
|
||||
text: self.colors.grid.paint("│ ").to_string(),
|
||||
size: 2,
|
||||
};
|
||||
}
|
||||
|
||||
fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
|
||||
if self.panel_width == 0 {
|
||||
writeln!(
|
||||
|
Loading…
Reference in New Issue
Block a user