Add printer

This adds a separate struct that handles printing of the different
tokens.
This commit is contained in:
Ezinwa Okpoechi 2018-05-07 01:32:00 +02:00 committed by David Peter
parent b625d07c34
commit 53d67e2b6e
2 changed files with 172 additions and 111 deletions

View file

@ -17,6 +17,7 @@ extern crate directories;
extern crate git2;
extern crate syntect;
mod printer;
mod terminal;
use std::collections::HashMap;
@ -30,7 +31,6 @@ use ansi_term::Colour::{Fixed, Green, Red, White, Yellow};
use ansi_term::Style;
use atty::Stream;
use clap::{App, AppSettings, Arg, SubCommand};
use console::Term;
use directories::ProjectDirs;
use git2::{DiffOptions, IntoCString, Repository};
@ -39,7 +39,7 @@ use syntect::easy::HighlightLines;
use syntect::highlighting::{Theme, ThemeSet};
use syntect::parsing::SyntaxSet;
use terminal::as_terminal_escaped;
use printer::Printer;
lazy_static! {
static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", crate_name!());
@ -55,18 +55,18 @@ mod errors {
use errors::*;
enum OptionsStyle {
pub enum OptionsStyle {
Plain,
LineNumbers,
Full,
}
struct Options<'a> {
true_color: bool,
style: OptionsStyle,
language: Option<&'a str>,
colored_output: bool,
paging: bool,
pub struct Options<'a> {
pub true_color: bool,
pub style: OptionsStyle,
pub language: Option<&'a str>,
pub colored_output: bool,
pub paging: bool,
}
enum OutputType<'a> {
@ -107,27 +107,26 @@ impl<'a> Drop for OutputType<'a> {
}
#[derive(Copy, Clone, Debug)]
enum LineChange {
pub enum LineChange {
Added,
RemovedAbove,
RemovedBelow,
Modified,
}
type LineChanges = HashMap<u32, LineChange>;
pub type LineChanges = HashMap<u32, LineChange>;
const PANEL_WIDTH: usize = 7;
const GRID_COLOR: u8 = 238;
const LINE_NUMBER_COLOR: u8 = 244;
#[derive(Default)]
struct Colors {
grid: Style,
filename: Style,
git_added: Style,
git_removed: Style,
git_modified: Style,
line_number: Style,
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,
}
impl Colors {
@ -147,27 +146,12 @@ impl Colors {
}
}
fn print_horizontal_line(
handle: &mut Write,
grid_color: &Style,
grid_char: char,
term_width: usize,
) -> Result<()> {
let hline = "".repeat(term_width - (PANEL_WIDTH + 1));
let hline = format!("{}{}{}", "".repeat(PANEL_WIDTH), grid_char, hline);
writeln!(handle, "{}", grid_color.paint(hline))?;
Ok(())
}
fn print_file<P: AsRef<Path>>(
options: &Options,
theme: &Theme,
syntax_set: &SyntaxSet,
handle: &mut Write,
printer: &mut Printer,
filename: P,
line_changes: &Option<LineChanges>,
) -> Result<()> {
let mut reader = BufReader::new(File::open(filename.as_ref())?);
let syntax = match options.language {
@ -178,33 +162,7 @@ fn print_file<P: AsRef<Path>>(
let syntax = syntax.unwrap_or_else(|| syntax_set.find_syntax_plain_text());
let mut highlighter = HighlightLines::new(syntax, theme);
let term = Term::stdout();
let (_, term_width) = term.size();
let term_width = term_width as usize;
let colors = if options.colored_output {
Colors::colored()
} else {
Colors::plain()
};
// Show file name and bars for all but plain style
match options.style {
OptionsStyle::LineNumbers | OptionsStyle::Full => {
print_horizontal_line(handle, &colors.grid, '┬', term_width)?;
writeln!(
handle,
"{}{} File {}",
" ".repeat(PANEL_WIDTH),
colors.grid.paint(""),
colors.filename.paint(filename.as_ref().to_string_lossy())
)?;
print_horizontal_line(handle, &colors.grid, '┼', term_width)?;
}
OptionsStyle::Plain => {}
};
printer.print_header(filename.as_ref().to_string_lossy().as_ref())?;
let mut line_nr = 1;
let mut line_buffer = String::new();
@ -222,49 +180,12 @@ fn print_file<P: AsRef<Path>>(
let regions = highlighter.highlight(line);
let line_change = if let Some(ref changes) = *line_changes {
match changes.get(&(line_nr as u32)) {
Some(&LineChange::Added) => colors.git_added.paint("+"),
Some(&LineChange::RemovedAbove) => colors.git_removed.paint(""),
Some(&LineChange::RemovedBelow) => colors.git_removed.paint("_"),
Some(&LineChange::Modified) => colors.git_modified.paint("~"),
_ => Style::default().paint(" "),
}
} else {
Style::default().paint(" ")
};
match options.style {
// Show only content for plain style
OptionsStyle::Plain => write!(
handle,
"{}",
as_terminal_escaped(&regions, options.true_color, options.colored_output)
)?,
_ => write!(
handle,
"{} {} {} {}",
colors.line_number.paint(format!("{:4}", line_nr)),
// Show git modification markers only for full style
match options.style {
OptionsStyle::Full => line_change,
_ => Style::default().paint(" "),
},
colors.grid.paint(""),
as_terminal_escaped(&regions, options.true_color, options.colored_output)
)?,
}
printer.print_line(line_nr, &regions)?;
line_nr += 1;
}
// Show bars for all but plain style
match options.style {
OptionsStyle::LineNumbers | OptionsStyle::Full => {
print_horizontal_line(handle, &colors.grid, '┴', term_width)?
}
OptionsStyle::Plain => {}
};
printer.print_footer()?;
Ok(())
}
@ -514,7 +435,7 @@ fn run() -> Result<()> {
.takes_value(true)
.possible_values(&["auto", "never", "always"])
.default_value("auto")
.help("When to use the pager")
.help("When to use the pager"),
)
.subcommand(
SubCommand::with_name("init-cache")
@ -552,7 +473,7 @@ fn run() -> Result<()> {
Some("always") => true,
Some("never") => false,
Some("auto") | _ => interactive_terminal,
}
},
};
let assets =
@ -569,16 +490,11 @@ fn run() -> Result<()> {
let stdout = io::stdout();
let mut output_type = get_output_type(&stdout, options.paging);
let handle = output_type.handle()?;
let mut printer = Printer::new(handle, &options);
for file in files {
let line_changes = get_git_diff(&file.to_string());
print_file(
&options,
theme,
&assets.syntax_set,
handle,
file,
&line_changes,
)?;
printer.line_changes = line_changes;
print_file(&options, theme, &assets.syntax_set, &mut printer, file)?;
}
}
}

145
src/printer.rs Normal file
View file

@ -0,0 +1,145 @@
use ansi_term::Style;
use console::Term;
use errors::*;
use std::io::Write;
use syntect::highlighting;
use terminal::as_terminal_escaped;
use {Colors, LineChange, LineChanges, Options, OptionsStyle};
const PANEL_WIDTH: usize = 7;
pub struct Printer<'a> {
handle: &'a mut Write,
colors: Colors,
term_width: usize,
options: &'a Options<'a>,
pub line_changes: Option<LineChanges>,
}
impl<'a> Printer<'a> {
pub fn new(handle: &'a mut Write, options: &'a Options) -> Self {
let (_, term_width) = Term::stdout().size();
let term_width = term_width as usize;
let colors = if options.colored_output {
Colors::colored()
} else {
Colors::plain()
};
Printer {
handle,
colors,
term_width,
options,
line_changes: None,
}
}
pub fn print_header(&mut self, filename: &str) -> Result<()> {
match self.options.style {
OptionsStyle::Full => {}
_ => return Ok(()),
}
self.print_horizontal_line('┬')?;
writeln!(
self.handle,
"{}{} File {}",
" ".repeat(PANEL_WIDTH),
self.colors.grid.paint(""),
self.colors.filename.paint(filename)
)?;
self.print_horizontal_line('┼')
}
pub fn print_footer(&mut self) -> Result<()> {
if let OptionsStyle::Full = self.options.style {
self.print_horizontal_line('┴')
} else {
Ok(())
}
}
pub fn print_line(
&mut self,
line_number: usize,
regions: &[(highlighting::Style, &str)],
) -> Result<()> {
let decorations = vec![
self.print_line_number(line_number),
self.print_git_marker(line_number),
self.print_line_border(),
Some(as_terminal_escaped(
&regions,
self.options.true_color,
self.options.colored_output,
)),
];
write!(
self.handle,
"{}",
decorations
.into_iter()
.filter_map(|dec| dec)
.collect::<Vec<_>>()
.join(" ")
)?;
Ok(())
}
fn print_line_number(&self, line_number: usize) -> Option<String> {
if let OptionsStyle::Plain = self.options.style {
return None;
}
Some(
self.colors
.line_number
.paint(format!("{:4}", line_number))
.to_string(),
)
}
fn print_git_marker(&self, line_number: usize) -> Option<String> {
match self.options.style {
OptionsStyle::Full => {}
_ => return None,
}
let marker = 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(" ")
};
Some(marker.to_string())
}
fn print_line_border(&self) -> Option<String> {
if let OptionsStyle::Plain = self.options.style {
return None;
}
Some(self.colors.grid.paint("").to_string())
}
fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
let hline = "".repeat(self.term_width - (PANEL_WIDTH + 1));
let hline = format!("{}{}{}", "".repeat(PANEL_WIDTH), grid_char, hline);
writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
Ok(())
}
}