@@ -1,18 +1,21 @@
2use app::Config;
1use app::Config;
3−use diff::{LineChange, LineChanges};
2+use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
4use errors::*;
4use errors::*;
5use std::io::Write;
6use std::io::Write;
6use syntect::highlighting;
9use syntect::highlighting;
7use terminal::as_terminal_escaped;
10use terminal::as_terminal_escaped;
8use Colors;
11use Colors;
10−const PANEL_WIDTH: usize = 7;
12pub struct Printer<'a> {
13pub struct Printer<'a> {
13 handle: &'a mut Write,
14 handle: &'a mut Write,
14 colors: Colors,
15 colors: Colors,
15 config: &'a Config<'a>,
16 config: &'a Config<'a>,
17+ decorations: Vec<Box<Decoration>>,
16 pub line_changes: Option<LineChanges>,
19 pub line_changes: Option<LineChanges>,
@@ -24,10 +27,43 @@ impl<'a> Printer<'a> {
24 Colors::plain()
27 Colors::plain()
30+ // Create decorations.
31+ let mut decorations: Vec<Box<Decoration>> = Vec::new();
33+ if config.output_components.numbers() {
34+ decorations.push(Box::new(LineNumberDecoration::new(&colors)));
37+ if config.output_components.changes() {
38+ decorations.push(Box::new(LineChangesDecoration::new(&colors)));
41+ let mut panel_width: usize =
42+ decorations.len() + decorations.iter().fold(0, |a, x| a + x.width());
44+ // The grid border decoration isn't added until after the panel_width calculation, since the
45+ // print_horizontal_line, print_header, and print_footer functions all assume the panel
46+ // width is without the grid border.
47+ if config.output_components.grid() && decorations.len() > 0 {
48+ decorations.push(Box::new(GridBorderDecoration::new(&colors)));
51+ // Disable the panel if the terminal is too small (i.e. can't fit 5 characters with the
54+ < (decorations.len() + decorations.iter().fold(0, |a, x| a + x.width())) + 5
31 line_changes: None,
67 line_changes: None,
@@ -43,8 +79,10 @@ impl<'a> Printer<'a> {
44 self.handle,
80 self.handle,
46− " ".repeat(PANEL_WIDTH),
82+ " ".repeat(self.panel_width),
47− self.colors.grid.paint("│"),
83+ self.colors
85+ .paint(if self.panel_width > 0 { "│" } else { "" }),
@@ -75,85 +113,123 @@ impl<'a> Printer<'a> {
75 line_number: usize,
113 line_number: usize,
76 regions: &[(highlighting::Style, &str)],
114 regions: &[(highlighting::Style, &str)],
77 ) -> Result<()> {
115 ) -> Result<()> {
78− let decorations = vec![
116+ let mut cursor: usize = 0;
79− self.print_line_number(line_number),
117+ let mut cursor_max: usize = self.config.term_width;
80− self.print_git_marker(line_number),
118+ let mut panel_wrap: Option<String> = None;
81− self.print_line_border(),
119+
82− Some(as_terminal_escaped(
84− self.config.true_color,
85− self.config.colored_output,
89− let grid_requested = self.config.output_components.grid();
93− decorations
120+ // Line decorations.
94− .into_iter()
121+ if self.panel_width > 0 {
95− .filter_map(|dec| if grid_requested {
122+ let decorations = self
96− Some(dec.unwrap_or_else(|| " ".to_owned()))
123+ .decorations
98− dec
125+ .map(|ref d| d.generate(line_number, false, self))
100− .collect::<Vec<_>>()
126+ .collect::<Vec<_>>();
102− )?;
128+ for deco in decorations {
129+ write!(self.handle, "{} ", deco.text)?;
130+ cursor_max -= deco.width + 1;
104− Ok(())
134+ // Line contents.
105− }
135+ if self.config.output_wrap == OutputWrap::None {
136+ let true_color = self.config.true_color;
137+ let colored_output = self.config.colored_output;
107− fn print_line_number(&self, line_number: usize) -> Option<String> {
139+ write!(
108− if self.config.output_components.numbers() {
140+ self.handle,
110− self.colors
142+ regions
111− .line_number
143+ .iter()
112− .paint(format!("{:4}", line_number))
144+ .map(|&(style, text)| as_terminal_escaped(
113− .to_string(),
145+ style,
115− } else if self.config.output_components.grid() {
150+ .collect::<Vec<_>>()
116− Some(" ".to_owned())
151+ .join("")
118− None
154+ for &(style, text) in regions.iter() {
119− }
155+ let text = text.trim_right_matches(|c| c == '\r' || c == '\n');
156+ let mut chars = text.chars();
157+ let mut remaining = text.chars().count();
159+ while remaining > 0 {
160+ let available = cursor_max - cursor;
163+ if remaining <= available {
164+ let text = chars.by_ref().take(remaining).collect::<String>();
165+ cursor += remaining;
170+ as_terminal_escaped(
173+ self.config.true_color,
174+ self.config.colored_output,
122− fn print_git_marker(&self, line_number: usize) -> Option<String> {
180+ // Generate wrap padding if not already generated.
123− if self.config.output_components.changes() {
181+ if panel_wrap.is_none() {
124− Some(
182+ panel_wrap = if self.panel_width > 0 {
125− if let Some(ref changes) = self.line_changes {
183+ Some(format!(
126− match changes.get(&(line_number as u32)) {
184+ "{} ",
127− Some(&LineChange::Added) => self.colors.git_added.paint("+"),
185+ self.decorations
128− Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
186+ .iter()
129− Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
187+ .map(|ref d| d.generate(line_number, true, self).text)
130− Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
188+ .collect::<Vec<String>>()
131− _ => Style::default().paint(" "),
189+ .join(" ")
192+ Some("".to_string())
134− Style::default().paint(" ")
137− } else if self.config.output_components.grid() {
138− Some(" ".to_owned())
144− fn print_line_border(&self) -> Option<String> {
196+ // It wraps.
145− if self.config.output_components.grid() {
197+ let text = chars.by_ref().take(available).collect::<String>();
146− Some(self.colors.grid.paint("│").to_string())
198+ cursor = 0;
147− } else {
199+ remaining -= available;
204+ as_terminal_escaped(
207+ self.config.true_color,
208+ self.config.colored_output,
210+ panel_wrap.clone().unwrap()
215+ write!(self.handle, "\n")?;
152 fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
221 fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
222+ if self.panel_width == 0 {
226+ self.colors.grid.paint("─".repeat(self.config.term_width))
153− let hline = "─".repeat(self.config.term_width - (PANEL_WIDTH + 1));
229+ let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
154− let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline);
230+ let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
156− writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
231+ writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;