on master help sign in
Add printer

This adds a separate struct that handles printing of the different tokens.

commit53d67e2b6ee4a674a8bb19793d891d1857f02167 authorEzinwa Okpoechi <brainmaestro@outlook.com> dateMon 7 May 2018, 01:32 (9 years ago) parentb625d07
2 files changed+172−111
src/main.rs+27−111 src/printer.rs+145
src/main.rs+27−111
@@ -17,6 +17,7 @@ extern crate directories;
1717extern crate git2;
1818extern crate syntect;
1919
20+mod printer;
2021mod terminal;
2122
2223use std::collections::HashMap;
@@ -30,7 +31,6 @@ use ansi_term::Colour::{Fixed, Green, Red, White, Yellow};
3031use ansi_term::Style;
3132use atty::Stream;
3233use clap::{App, AppSettings, Arg, SubCommand};
33−use console::Term;
3434use directories::ProjectDirs;
3535use git2::{DiffOptions, IntoCString, Repository};
3636
@@ -39,7 +39,7 @@ use syntect::easy::HighlightLines;
3939use syntect::highlighting::{Theme, ThemeSet};
4040use syntect::parsing::SyntaxSet;
4141
42−use terminal::as_terminal_escaped;
42+use printer::Printer;
4343
4444lazy_static! {
4545 static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", crate_name!());
@@ -55,18 +55,18 @@ mod errors {
5555
5656use errors::*;
5757
58−enum OptionsStyle {
58+pub enum OptionsStyle {
5959 Plain,
6060 LineNumbers,
6161 Full,
6262}
6363
64−struct Options<'a> {
65− true_color: bool,
66− style: OptionsStyle,
67− language: Option<&'a str>,
68− colored_output: bool,
69− paging: bool,
64+pub struct Options<'a> {
65+ pub true_color: bool,
66+ pub style: OptionsStyle,
67+ pub language: Option<&'a str>,
68+ pub colored_output: bool,
69+ pub paging: bool,
7070}
7171
7272enum OutputType<'a> {
@@ -107,27 +107,26 @@ impl<'a> Drop for OutputType<'a> {
107107}
108108
109109#[derive(Copy, Clone, Debug)]
110−enum LineChange {
110+pub enum LineChange {
111111 Added,
112112 RemovedAbove,
113113 RemovedBelow,
114114 Modified,
115115}
116116
117−type LineChanges = HashMap<u32, LineChange>;
117+pub type LineChanges = HashMap<u32, LineChange>;
118118
119−const PANEL_WIDTH: usize = 7;
120119const GRID_COLOR: u8 = 238;
121120const LINE_NUMBER_COLOR: u8 = 244;
122121
123122#[derive(Default)]
124−struct Colors {
125− grid: Style,
126− filename: Style,
127− git_added: Style,
128− git_removed: Style,
129− git_modified: Style,
130− line_number: Style,
123+pub struct Colors {
124+ pub grid: Style,
125+ pub filename: Style,
126+ pub git_added: Style,
127+ pub git_removed: Style,
128+ pub git_modified: Style,
129+ pub line_number: Style,
131130}
132131
133132impl Colors {
@@ -147,27 +146,12 @@ impl Colors {
147146 }
148147}
149148
150−fn print_horizontal_line(
151− handle: &mut Write,
152− grid_color: &Style,
153− grid_char: char,
154− term_width: usize,
155−) -> Result<()> {
156− let hline = "─".repeat(term_width - (PANEL_WIDTH + 1));
157− let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline);
158−
159− writeln!(handle, "{}", grid_color.paint(hline))?;
160−
161− Ok(())
162−}
163−
164149fn print_file<P: AsRef<Path>>(
165150 options: &Options,
166151 theme: &Theme,
167152 syntax_set: &SyntaxSet,
168− handle: &mut Write,
153+ printer: &mut Printer,
169154 filename: P,
170− line_changes: &Option<LineChanges>,
171155) -> Result<()> {
172156 let mut reader = BufReader::new(File::open(filename.as_ref())?);
173157 let syntax = match options.language {
@@ -178,33 +162,7 @@ fn print_file<P: AsRef<Path>>(
178162 let syntax = syntax.unwrap_or_else(|| syntax_set.find_syntax_plain_text());
179163 let mut highlighter = HighlightLines::new(syntax, theme);
180164
181− let term = Term::stdout();
182− let (_, term_width) = term.size();
183− let term_width = term_width as usize;
184−
185− let colors = if options.colored_output {
186− Colors::colored()
187− } else {
188− Colors::plain()
189− };
190−
191− // Show file name and bars for all but plain style
192− match options.style {
193− OptionsStyle::LineNumbers | OptionsStyle::Full => {
194− print_horizontal_line(handle, &colors.grid, '┬', term_width)?;
195−
196− writeln!(
197− handle,
198− "{}{} File {}",
199− " ".repeat(PANEL_WIDTH),
200− colors.grid.paint("│"),
201− colors.filename.paint(filename.as_ref().to_string_lossy())
202− )?;
203−
204− print_horizontal_line(handle, &colors.grid, '┼', term_width)?;
205− }
206− OptionsStyle::Plain => {}
207− };
165+ printer.print_header(filename.as_ref().to_string_lossy().as_ref())?;
208166
209167 let mut line_nr = 1;
210168 let mut line_buffer = String::new();
@@ -222,49 +180,12 @@ fn print_file<P: AsRef<Path>>(
222180
223181 let regions = highlighter.highlight(line);
224182
225− let line_change = if let Some(ref changes) = *line_changes {
226− match changes.get(&(line_nr as u32)) {
227− Some(&LineChange::Added) => colors.git_added.paint("+"),
228− Some(&LineChange::RemovedAbove) => colors.git_removed.paint("‾"),
229− Some(&LineChange::RemovedBelow) => colors.git_removed.paint("_"),
230− Some(&LineChange::Modified) => colors.git_modified.paint("~"),
231− _ => Style::default().paint(" "),
232− }
233− } else {
234− Style::default().paint(" ")
235− };
236−
237− match options.style {
238− // Show only content for plain style
239− OptionsStyle::Plain => write!(
240− handle,
241− "{}",
242− as_terminal_escaped(&regions, options.true_color, options.colored_output)
243− )?,
244− _ => write!(
245− handle,
246− "{} {} {} {}",
247− colors.line_number.paint(format!("{:4}", line_nr)),
248− // Show git modification markers only for full style
249− match options.style {
250− OptionsStyle::Full => line_change,
251− _ => Style::default().paint(" "),
252− },
253− colors.grid.paint("│"),
254− as_terminal_escaped(&regions, options.true_color, options.colored_output)
255− )?,
256− }
183+ printer.print_line(line_nr, &regions)?;
257184
258185 line_nr += 1;
259186 }
260187
261− // Show bars for all but plain style
262− match options.style {
263− OptionsStyle::LineNumbers | OptionsStyle::Full => {
264− print_horizontal_line(handle, &colors.grid, '┴', term_width)?
265− }
266− OptionsStyle::Plain => {}
267− };
188+ printer.print_footer()?;
268189
269190 Ok(())
270191}
@@ -514,7 +435,7 @@ fn run() -> Result<()> {
514435 .takes_value(true)
515436 .possible_values(&["auto", "never", "always"])
516437 .default_value("auto")
517− .help("When to use the pager")
438+ .help("When to use the pager"),
518439 )
519440 .subcommand(
520441 SubCommand::with_name("init-cache")
@@ -552,7 +473,7 @@ fn run() -> Result<()> {
552473 Some("always") => true,
553474 Some("never") => false,
554475 Some("auto") | _ => interactive_terminal,
555− }
476+ },
556477 };
557478
558479 let assets =
@@ -569,16 +490,11 @@ fn run() -> Result<()> {
569490 let stdout = io::stdout();
570491 let mut output_type = get_output_type(&stdout, options.paging);
571492 let handle = output_type.handle()?;
493+ let mut printer = Printer::new(handle, &options);
572494 for file in files {
573495 let line_changes = get_git_diff(&file.to_string());
574− print_file(
575− &options,
576− theme,
577− &assets.syntax_set,
578− handle,
579− file,
580− &line_changes,
581− )?;
496+ printer.line_changes = line_changes;
497+ print_file(&options, theme, &assets.syntax_set, &mut printer, file)?;
582498 }
583499 }
584500 }
src/printer.rsnew file+145
@@ -0,0 +1,145 @@
1+use ansi_term::Style;
2+use console::Term;
3+use errors::*;
4+use std::io::Write;
5+use syntect::highlighting;
6+use terminal::as_terminal_escaped;
7+use {Colors, LineChange, LineChanges, Options, OptionsStyle};
8+
9+const PANEL_WIDTH: usize = 7;
10+
11+pub struct Printer<'a> {
12+ handle: &'a mut Write,
13+ colors: Colors,
14+ term_width: usize,
15+ options: &'a Options<'a>,
16+ pub line_changes: Option<LineChanges>,
17+}
18+
19+impl<'a> Printer<'a> {
20+ pub fn new(handle: &'a mut Write, options: &'a Options) -> Self {
21+ let (_, term_width) = Term::stdout().size();
22+ let term_width = term_width as usize;
23+
24+ let colors = if options.colored_output {
25+ Colors::colored()
26+ } else {
27+ Colors::plain()
28+ };
29+
30+ Printer {
31+ handle,
32+ colors,
33+ term_width,
34+ options,
35+ line_changes: None,
36+ }
37+ }
38+
39+ pub fn print_header(&mut self, filename: &str) -> Result<()> {
40+ match self.options.style {
41+ OptionsStyle::Full => {}
42+ _ => return Ok(()),
43+ }
44+
45+ self.print_horizontal_line('┬')?;
46+
47+ writeln!(
48+ self.handle,
49+ "{}{} File {}",
50+ " ".repeat(PANEL_WIDTH),
51+ self.colors.grid.paint("│"),
52+ self.colors.filename.paint(filename)
53+ )?;
54+
55+ self.print_horizontal_line('┼')
56+ }
57+
58+ pub fn print_footer(&mut self) -> Result<()> {
59+ if let OptionsStyle::Full = self.options.style {
60+ self.print_horizontal_line('┴')
61+ } else {
62+ Ok(())
63+ }
64+ }
65+
66+ pub fn print_line(
67+ &mut self,
68+ line_number: usize,
69+ regions: &[(highlighting::Style, &str)],
70+ ) -> Result<()> {
71+ let decorations = vec![
72+ self.print_line_number(line_number),
73+ self.print_git_marker(line_number),
74+ self.print_line_border(),
75+ Some(as_terminal_escaped(
76+ &regions,
77+ self.options.true_color,
78+ self.options.colored_output,
79+ )),
80+ ];
81+
82+ write!(
83+ self.handle,
84+ "{}",
85+ decorations
86+ .into_iter()
87+ .filter_map(|dec| dec)
88+ .collect::<Vec<_>>()
89+ .join(" ")
90+ )?;
91+
92+ Ok(())
93+ }
94+
95+ fn print_line_number(&self, line_number: usize) -> Option<String> {
96+ if let OptionsStyle::Plain = self.options.style {
97+ return None;
98+ }
99+
100+ Some(
101+ self.colors
102+ .line_number
103+ .paint(format!("{:4}", line_number))
104+ .to_string(),
105+ )
106+ }
107+
108+ fn print_git_marker(&self, line_number: usize) -> Option<String> {
109+ match self.options.style {
110+ OptionsStyle::Full => {}
111+ _ => return None,
112+ }
113+
114+ let marker = if let Some(ref changes) = self.line_changes {
115+ match changes.get(&(line_number as u32)) {
116+ Some(&LineChange::Added) => self.colors.git_added.paint("+"),
117+ Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
118+ Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
119+ Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
120+ _ => Style::default().paint(" "),
121+ }
122+ } else {
123+ Style::default().paint(" ")
124+ };
125+
126+ Some(marker.to_string())
127+ }
128+
129+ fn print_line_border(&self) -> Option<String> {
130+ if let OptionsStyle::Plain = self.options.style {
131+ return None;
132+ }
133+
134+ Some(self.colors.grid.paint("│").to_string())
135+ }
136+
137+ fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
138+ let hline = "─".repeat(self.term_width - (PANEL_WIDTH + 1));
139+ let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline);
140+
141+ writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
142+
143+ Ok(())
144+ }
145+}