@@ -5,14 +5,21 @@ use errors::*;
55use std::io::Write;
66use syntect::highlighting;
77use terminal::as_terminal_escaped;
8+use style::OutputWrap;
89use Colors;
910
10−const PANEL_WIDTH: usize = 7;
11+const LINE_NUMBER_WIDTH: usize = 4;
12+
13+struct PrintSegment {
14+ size: usize,
15+ text: String
16+}
1117
1218pub struct Printer<'a> {
1319 handle: &'a mut Write,
1420 colors: Colors,
1521 config: &'a Config<'a>,
22+ panel_width: usize,
1623 pub line_changes: Option<LineChanges>,
1724}
1825
@@ -24,12 +31,27 @@ impl<'a> Printer<'a> {
2431 Colors::plain()
2532 };
2633
27− Printer {
34+ // Create the instance.
35+ let mut instance = Printer {
2836 handle,
2937 colors,
3038 config,
39+ panel_width: 0,
3140 line_changes: None,
32− }
41+ };
42+
43+ // Generate the panel (gutter) width.
44+ let decorations = instance.gen_decorations(0);
45+ instance.panel_width = decorations.len()
46+ + decorations.iter().fold(0, |a, x| a + x.size)
47+ + if config.output_components.grid() {
48+ 0
49+ } else {
50+ 0
51+ };
52+
53+ // Return the instance.
54+ return instance;
3355 }
3456
3557 pub fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
@@ -43,8 +65,12 @@ impl<'a> Printer<'a> {
4365 write!(
4466 self.handle,
4567 "{}{} ",
46− " ".repeat(PANEL_WIDTH),
47− self.colors.grid.paint("│"),
68+ " ".repeat(self.panel_width),
69+ self.colors.grid.paint(if self.panel_width > 0 {
70+ "│"
71+ } else {
72+ ""
73+ }),
4874 )?;
4975 }
5076
@@ -75,86 +101,162 @@ impl<'a> Printer<'a> {
75101 line_number: usize,
76102 regions: &[(highlighting::Style, &str)],
77103 ) -> 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− )?;
104+ let mut cursor:usize = 0;
105+ let mut cursor_max:usize = self.config.term_width - 2;
103106
104− Ok(())
105− }
107+ // Line decoration.
108+ let decorations = self.gen_decorations(line_number);
109+ let gutter_width = decorations.len() + decorations.iter().fold(0, |a, x| a + x.size);
106110
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())
111+ if gutter_width > 0 {
112+ cursor_max -= gutter_width;
113+ write!(self.handle, "{} ", decorations
114+ .iter()
115+ .map(|seg| seg.text.to_owned())
116+ .collect::<Vec<String>>()
117+ .join(" "))?;
118+ }
119+
120+ // Grid border.
121+ let border = if gutter_width > 0 && self.config.output_components.grid() {
122+ self.gen_border()
117123 } else {
118− None
124+ PrintSegment {
125+ size: 0,
126+ text: "".to_owned()
127+ }
128+ };
129+
130+ cursor_max -= border.size;
131+ write!(self.handle, "{} ", border.text)?;
132+
133+ // Line contents.
134+ for &(style, text) in regions.iter() {
135+ let mut chars = text.chars().filter(|c| *c != '\n');
136+ let mut remaining = chars.clone().count();
137+
138+ while remaining > 0 {
139+ let available = cursor_max - cursor;
140+
141+ // It fits.
142+ if remaining <= available {
143+ let text = chars.by_ref().take(remaining).collect::<String>();
144+ cursor += remaining;
145+
146+ write!(self.handle, "{}", as_terminal_escaped(
147+ style,
148+ &*text,
149+ self.config.true_color,
150+ self.config.colored_output
151+ ))?;
152+ break;
153+ }
154+
155+ // It wraps.
156+ if self.config.output_wrap == OutputWrap::Character {
157+ let text = chars.by_ref().take(available).collect::<String>();
158+ cursor = 0;
159+ remaining -= available;
160+
161+ write!(self.handle, "{}\n{}{} ", as_terminal_escaped(
162+ style,
163+ &*text,
164+ self.config.true_color,
165+ self.config.colored_output
166+ ), " ".repeat(gutter_width), border.text.to_owned())?;
167+
168+ continue;
169+ }
170+ }
119171 }
172+
173+ // Finished.
174+ write!(self.handle, "\n")?;
175+ return Ok(());
120176 }
121177
122− fn print_git_marker(&self, line_number: usize) -> Option<String> {
178+
179+
180+ #[doc = "
181+ Generates all the line decorations.
182+
183+ # Arguments
184+ * `line_number` - The line number.
185+ "]
186+ fn gen_decorations(&self, line_number: usize) -> Vec<PrintSegment> {
187+ let mut decorations = Vec::new();
188+
189+ if self.config.output_components.numbers() {
190+ decorations.push(self.gen_deco_line_number(line_number));
191+ }
192+
123193 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(" "),
132− }
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
194+ decorations.push(self.gen_deco_line_changes(line_number));
141195 }
196+
197+ return decorations;
142198 }
143199
144− fn print_line_border(&self) -> Option<String> {
145− if self.config.output_components.grid() {
146− Some(self.colors.grid.paint("│").to_string())
200+ #[doc = "
201+ Generates the decoration for displaying the line number.
202+
203+ # Arguments
204+ * `line_number` - The line number.
205+ "]
206+ fn gen_deco_line_number(&self, line_number: usize) -> PrintSegment {
207+ let plain:String = format!("{:width$}", line_number, width = LINE_NUMBER_WIDTH);
208+ let color = self.colors.line_number.paint(plain.to_owned());
209+
210+ return PrintSegment {
211+ text: color.to_string(),
212+ size: plain.len()
213+ }
214+ }
215+
216+ #[doc = "
217+ Generates the decoration for displaying the git changes.
218+
219+ # Arguments
220+ * `line_number` - The line number.
221+ "]
222+ fn gen_deco_line_changes(&self, line_number: usize) -> PrintSegment {
223+ let color = if let Some(ref changes) = self.line_changes {
224+ match changes.get(&(line_number as u32)) {
225+ Some(&LineChange::Added) => self.colors.git_added.paint("+"),
226+ Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
227+ Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
228+ Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
229+ _ => Style::default().paint(" "),
230+ }
147231 } else {
148− None
232+ Style::default().paint(" ")
233+ };
234+
235+ return PrintSegment {
236+ text: color.to_string(),
237+ size: 1
149238 }
150239 }
151240
152− 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);
241+ #[doc = "
242+ Generates the vertical grid border.
243+ "]
244+ fn gen_border(&self) -> PrintSegment {
245+ return PrintSegment {
246+ text: self.colors.grid.paint("│").to_string(),
247+ size: 2
248+ }
249+ }
155250
156− writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
251+ fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
252+ if self.panel_width == 0 {
253+ writeln!(self.handle, "{}", "─".repeat(self.config.term_width))?;
254+ } else {
255+ let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
256+ let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
257+ writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
258+ }
157259
158− Ok(())
260+ return Ok(());
159261 }
160262}