@@ -1,24 +1,20 @@
1−use ansi_term::Style;
21use app::Config;
3−use diff::{LineChange, LineChanges};
2+use diff::LineChanges;
43use errors::*;
54use std::io::Write;
5+use std::vec::Vec;
6+use std::boxed::Box;
67use syntect::highlighting;
78use terminal::as_terminal_escaped;
89use style::OutputWrap;
10+use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
911use Colors;
1012
11−const LINE_NUMBER_WIDTH: usize = 4;
12−
13−struct PrintSegment {
14− size: usize,
15− text: String,
16−}
17−
1813pub struct Printer<'a> {
1914 handle: &'a mut Write,
2015 colors: Colors,
2116 config: &'a Config<'a>,
17+ decorations: Vec<Box<Decoration>>,
2218 panel_width: usize,
2319 pub line_changes: Option<LineChanges>,
2420}
@@ -31,21 +27,36 @@ impl<'a> Printer<'a> {
3127 Colors::plain()
3228 };
3329
34− // Create the instance.
35− let mut instance = Printer {
30+ // Create decorations.
31+ let mut decorations: Vec<Box<Decoration>> = Vec::new();
32+
33+ if config.output_components.numbers() {
34+ decorations.push(Box::new(LineNumberDecoration::new(&colors)));
35+ }
36+
37+ if config.output_components.changes() {
38+ decorations.push(Box::new(LineChangesDecoration::new(&colors)));
39+ }
40+
41+ let panel_width: usize =
42+ decorations.len() + decorations.iter().fold(0, |a, x| a + x.width());
43+
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)));
49+ }
50+
51+ // Create printer.
52+ Printer {
53+ panel_width,
3654 handle,
3755 colors,
3856 config,
39− panel_width: 0,
57+ decorations,
4058 line_changes: None,
41− };
42−
43− // Generate the panel (gutter) width.
44− let decorations = instance.line_decorations(0);
45− instance.panel_width = decorations.len() + decorations.iter().fold(0, |a, x| a + x.size);
46−
47− // Return the instance.
48− return instance;
59+ }
4960 }
5061
5162 pub fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
@@ -95,36 +106,20 @@ impl<'a> Printer<'a> {
95106 ) -> Result<()> {
96107 let mut cursor: usize = 0;
97108 let mut cursor_max: usize = self.config.term_width;
98−
99− // Line decoration.
100− let decorations = self.line_decorations(line_number);
101− let gutter_width = decorations.len() + decorations.iter().fold(0, |a, x| a + x.size);
102−
103− if gutter_width > 0 {
104− cursor_max -= gutter_width;
105− write!(
106− self.handle,
107− "{} ",
108− decorations
109− .iter()
110− .map(|seg| seg.text.to_owned())
111− .collect::<Vec<String>>()
112− .join(" ")
113− )?;
114− }
115−
116− // Grid border.
117− let border = if gutter_width > 0 && self.config.output_components.grid() {
118− self.line_border()
119− } else {
120− PrintSegment {
121− size: 0,
122− text: "".to_owned(),
109+ let mut panel_wrap: Option<String> = None;
110+
111+ // Line decorations.
112+ if self.panel_width > 0 {
113+ let decorations = self.decorations
114+ .iter()
115+ .map(|ref d| d.for_line(line_number, self))
116+ .collect::<Vec<_>>();
117+
118+ for deco in decorations {
119+ write!(self.handle, "{} ", deco.text)?;
120+ cursor_max -= deco.width + 1;
123121 }
124− };
125−
126− cursor_max -= border.size;
127− write!(self.handle, "{}", border.text)?;
122+ }
128123
129124 // Line contents.
130125 if self.config.output_wrap == OutputWrap::None {
@@ -140,7 +135,7 @@ impl<'a> Printer<'a> {
140135 style,
141136 text,
142137 true_color,
143− colored_output
138+ colored_output,
144139 ))
145140 .collect::<Vec<_>>()
146141 .join("")
@@ -171,6 +166,22 @@ impl<'a> Printer<'a> {
171166 break;
172167 }
173168
169+ // Generate wrap padding if not already generated.
170+ if panel_wrap.is_none() {
171+ panel_wrap = if self.panel_width > 0 {
172+ Some(format!(
173+ "{} ",
174+ self.decorations
175+ .iter()
176+ .map(|ref d| d.for_wrap(line_number, self).text)
177+ .collect::<Vec<String>>()
178+ .join(" ")
179+ ))
180+ } else {
181+ Some("".to_string())
182+ }
183+ }
184+
174185 // It wraps.
175186 let text = chars.by_ref().take(available).collect::<String>();
176187 cursor = 0;
@@ -178,15 +189,14 @@ impl<'a> Printer<'a> {
178189
179190 write!(
180191 self.handle,
181− "{}\n{}{}",
192+ "{}\n{}",
182193 as_terminal_escaped(
183194 style,
184195 &*text,
185196 self.config.true_color,
186197 self.config.colored_output,
187198 ),
188− " ".repeat(gutter_width),
189− border.text.to_owned()
199+ panel_wrap.clone().unwrap()
190200 )?;
191201 }
192202 }
@@ -197,56 +207,6 @@ impl<'a> Printer<'a> {
197207 Ok(())
198208 }
199209
200− fn line_decorations(&self, line_number: usize) -> Vec<PrintSegment> {
201− let mut decorations = Vec::new();
202−
203− if self.config.output_components.numbers() {
204− decorations.push(self.line_number(line_number));
205− }
206−
207− if self.config.output_components.changes() {
208− decorations.push(self.line_changes(line_number));
209− }
210−
211− return decorations;
212− }
213−
214− fn line_number(&self, line_number: usize) -> PrintSegment {
215− let plain: String = format!("{:width$}", line_number, width = LINE_NUMBER_WIDTH);
216− let color = self.colors.line_number.paint(plain.clone());
217−
218− return PrintSegment {
219− text: color.to_string(),
220− size: plain.len(),
221− };
222− }
223−
224− fn line_changes(&self, line_number: usize) -> PrintSegment {
225− let color = if let Some(ref changes) = self.line_changes {
226− match changes.get(&(line_number as u32)) {
227− Some(&LineChange::Added) => self.colors.git_added.paint("+"),
228− Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
229− Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
230− Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
231− _ => Style::default().paint(" "),
232− }
233− } else {
234− Style::default().paint(" ")
235− };
236−
237− return PrintSegment {
238− text: color.to_string(),
239− size: 1,
240− };
241− }
242−
243− fn line_border(&self) -> PrintSegment {
244− return PrintSegment {
245− text: self.colors.grid.paint("│ ").to_string(),
246− size: 2,
247− };
248− }
249−
250210 fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
251211 if self.panel_width == 0 {
252212 writeln!(