@@ -1,18 +1,21 @@
1−use ansi_term::Style;
21use app::Config;
3−use diff::{LineChange, LineChanges};
2+use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
3+use diff::LineChanges;
44use errors::*;
5+use std::boxed::Box;
56use std::io::Write;
7+use std::vec::Vec;
8+use style::OutputWrap;
69use syntect::highlighting;
710use terminal::as_terminal_escaped;
811use Colors;
912
10−const PANEL_WIDTH: usize = 7;
11−
1213pub struct Printer<'a> {
1314 handle: &'a mut Write,
1415 colors: Colors,
1516 config: &'a Config<'a>,
17+ decorations: Vec<Box<Decoration>>,
18+ panel_width: usize,
1619 pub line_changes: Option<LineChanges>,
1720}
1821
@@ -24,10 +27,43 @@ impl<'a> Printer<'a> {
2427 Colors::plain()
2528 };
2629
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 mut 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+ // Disable the panel if the terminal is too small (i.e. can't fit 5 characters with the
52+ // panel showing).
53+ if config.term_width
54+ < (decorations.len() + decorations.iter().fold(0, |a, x| a + x.width())) + 5
55+ {
56+ decorations.clear();
57+ panel_width = 0;
58+ }
59+
60+ // Create printer.
2761 Printer {
62+ panel_width,
2863 handle,
2964 colors,
3065 config,
66+ decorations,
3167 line_changes: None,
3268 }
3369 }
@@ -43,8 +79,10 @@ impl<'a> Printer<'a> {
4379 write!(
4480 self.handle,
4581 "{}{} ",
46− " ".repeat(PANEL_WIDTH),
47− self.colors.grid.paint("│"),
82+ " ".repeat(self.panel_width),
83+ self.colors
84+ .grid
85+ .paint(if self.panel_width > 0 { "│" } else { "" }),
4886 )?;
4987 }
5088
@@ -75,85 +113,123 @@ impl<'a> Printer<'a> {
75113 line_number: usize,
76114 regions: &[(highlighting::Style, &str)],
77115 ) -> Result<()> {
78− let decorations = vec![
79− self.print_line_number(line_number),
80− self.print_git_marker(line_number),
81− self.print_line_border(),
82− Some(as_terminal_escaped(
83− ®ions,
84− self.config.true_color,
85− self.config.colored_output,
86− )),
87− ];
88−
89− let grid_requested = self.config.output_components.grid();
90− write!(
91− self.handle,
92− "{}",
93− decorations
94− .into_iter()
95− .filter_map(|dec| if grid_requested {
96− Some(dec.unwrap_or_else(|| " ".to_owned()))
97− } else {
98− dec
99− })
100− .collect::<Vec<_>>()
101− .join(" ")
102− )?;
116+ let mut cursor: usize = 0;
117+ let mut cursor_max: usize = self.config.term_width;
118+ let mut panel_wrap: Option<String> = None;
119+
120+ // Line decorations.
121+ if self.panel_width > 0 {
122+ let decorations = self
123+ .decorations
124+ .iter()
125+ .map(|ref d| d.generate(line_number, false, self))
126+ .collect::<Vec<_>>();
127+
128+ for deco in decorations {
129+ write!(self.handle, "{} ", deco.text)?;
130+ cursor_max -= deco.width + 1;
131+ }
132+ }
103133
104− Ok(())
105− }
134+ // Line contents.
135+ if self.config.output_wrap == OutputWrap::None {
136+ let true_color = self.config.true_color;
137+ let colored_output = self.config.colored_output;
106138
107− fn print_line_number(&self, line_number: usize) -> Option<String> {
108− if self.config.output_components.numbers() {
109− Some(
110− self.colors
111− .line_number
112− .paint(format!("{:4}", line_number))
113− .to_string(),
114− )
115− } else if self.config.output_components.grid() {
116− Some(" ".to_owned())
139+ write!(
140+ self.handle,
141+ "{}",
142+ regions
143+ .iter()
144+ .map(|&(style, text)| as_terminal_escaped(
145+ style,
146+ text,
147+ true_color,
148+ colored_output,
149+ ))
150+ .collect::<Vec<_>>()
151+ .join("")
152+ )?;
117153 } else {
118− None
119− }
120− }
154+ for &(style, text) in regions.iter() {
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();
158+
159+ while remaining > 0 {
160+ let available = cursor_max - cursor;
161+
162+ // It fits.
163+ if remaining <= available {
164+ let text = chars.by_ref().take(remaining).collect::<String>();
165+ cursor += remaining;
166+
167+ write!(
168+ self.handle,
169+ "{}",
170+ as_terminal_escaped(
171+ style,
172+ &*text,
173+ self.config.true_color,
174+ self.config.colored_output,
175+ )
176+ )?;
177+ break;
178+ }
121179
122− fn print_git_marker(&self, line_number: usize) -> Option<String> {
123− if self.config.output_components.changes() {
124− Some(
125− if let Some(ref changes) = self.line_changes {
126− match changes.get(&(line_number as u32)) {
127− Some(&LineChange::Added) => self.colors.git_added.paint("+"),
128− Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
129− Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
130− Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
131− _ => Style::default().paint(" "),
180+ // Generate wrap padding if not already generated.
181+ if panel_wrap.is_none() {
182+ panel_wrap = if self.panel_width > 0 {
183+ Some(format!(
184+ "{} ",
185+ self.decorations
186+ .iter()
187+ .map(|ref d| d.generate(line_number, true, self).text)
188+ .collect::<Vec<String>>()
189+ .join(" ")
190+ ))
191+ } else {
192+ Some("".to_string())
193+ }
132194 }
133− } else {
134− Style::default().paint(" ")
135− }.to_string(),
136− )
137− } else if self.config.output_components.grid() {
138− Some(" ".to_owned())
139− } else {
140− None
141− }
142− }
143195
144− fn print_line_border(&self) -> Option<String> {
145− if self.config.output_components.grid() {
146− Some(self.colors.grid.paint("│").to_string())
147− } else {
148− None
196+ // It wraps.
197+ let text = chars.by_ref().take(available).collect::<String>();
198+ cursor = 0;
199+ remaining -= available;
200+
201+ write!(
202+ self.handle,
203+ "{}\n{}",
204+ as_terminal_escaped(
205+ style,
206+ &*text,
207+ self.config.true_color,
208+ self.config.colored_output,
209+ ),
210+ panel_wrap.clone().unwrap()
211+ )?;
212+ }
213+ }
214+
215+ write!(self.handle, "\n")?;
149216 }
217+
218+ Ok(())
150219 }
151220
152221 fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
153− let hline = "─".repeat(self.config.term_width - (PANEL_WIDTH + 1));
154− let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline);
155−
156− writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
222+ if self.panel_width == 0 {
223+ writeln!(
224+ self.handle,
225+ "{}",
226+ self.colors.grid.paint("─".repeat(self.config.term_width))
227+ )?;
228+ } else {
229+ let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
230+ let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
231+ writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
232+ }
157233
158234 Ok(())
159235 }