on master help sign in
Split decorations into a separate file and optimized them a bit.
commitb327127f373b104b46d062caa5b7016bc02c3a77 authoreth-p <32112321+eth-p@users.noreply.github.com> dateSun 13 May 2018, 18:44 (8 years ago) parent9214a4a
3 files changed+210−103
src/decorations.rs+146 src/main.rs+1 src/printer.rs+63−103
src/decorations.rsnew file+146
@@ -0,0 +1,146 @@
1+use ansi_term::Style;
2+use diff::LineChange;
3+use printer::Printer;
4+use Colors;
5+
6+#[derive(Clone)]
7+pub struct DecorationText {
8+ pub width: usize,
9+ pub text: String,
10+}
11+
12+pub trait Decoration {
13+ fn for_line(&self, line_number: usize, printer: &Printer) -> DecorationText;
14+ fn for_wrap(&self, line_number: usize, printer: &Printer) -> DecorationText;
15+ fn width(&self) -> usize;
16+}
17+
18+// Line number decoration.
19+pub struct LineNumberDecoration {
20+ color: Style,
21+ cached_wrap: DecorationText,
22+ cached_wrap_invalid_at: usize,
23+}
24+
25+impl LineNumberDecoration {
26+ pub fn new(colors: &Colors) -> Self {
27+ LineNumberDecoration {
28+ color: colors.line_number,
29+ cached_wrap_invalid_at: 10000,
30+ cached_wrap: DecorationText {
31+ text: colors.line_number.paint(" ".repeat(4)).to_string(),
32+ width: 4,
33+ },
34+ }
35+ }
36+}
37+
38+impl Decoration for LineNumberDecoration {
39+ fn for_line(&self, line_number: usize, _printer: &Printer) -> DecorationText {
40+ let plain: String = format!("{:4}", line_number);
41+ DecorationText {
42+ width: plain.len(),
43+ text: self.color.paint(plain).to_string(),
44+ }
45+ }
46+
47+ fn for_wrap(&self, line_number: usize, _printer: &Printer) -> DecorationText {
48+ if line_number > self.cached_wrap_invalid_at {
49+ let new_width = self.cached_wrap.width + 1;
50+ return DecorationText {
51+ text: self.color.paint(" ".repeat(new_width)).to_string(),
52+ width: new_width,
53+ };
54+ }
55+
56+ self.cached_wrap.clone()
57+ }
58+
59+ fn width(&self) -> usize {
60+ 4
61+ }
62+}
63+
64+// Line changes decoration.
65+pub struct LineChangesDecoration {
66+ cached_none: DecorationText,
67+ cached_added: DecorationText,
68+ cached_removed_above: DecorationText,
69+ cached_removed_below: DecorationText,
70+ cached_modified: DecorationText,
71+}
72+
73+impl LineChangesDecoration {
74+ #[inline]
75+ fn generate_cached(style: Style, text: &str) -> DecorationText {
76+ DecorationText {
77+ text: style.paint(text).to_string(),
78+ width: text.chars().count(),
79+ }
80+ }
81+
82+ pub fn new(colors: &Colors) -> Self {
83+ LineChangesDecoration {
84+ cached_none: Self::generate_cached(Style::default(), " "),
85+ cached_added: Self::generate_cached(colors.git_added, "+"),
86+ cached_removed_above: Self::generate_cached(colors.git_removed, "‾"),
87+ cached_removed_below: Self::generate_cached(colors.git_removed, "_"),
88+ cached_modified: Self::generate_cached(colors.git_modified, "~"),
89+ }
90+ }
91+}
92+
93+impl Decoration for LineChangesDecoration {
94+ fn for_line(&self, line_number: usize, printer: &Printer) -> DecorationText {
95+ if let Some(ref changes) = printer.line_changes {
96+ match changes.get(&(line_number as u32)) {
97+ Some(&LineChange::Added) => self.cached_added.clone(),
98+ Some(&LineChange::RemovedAbove) => self.cached_removed_above.clone(),
99+ Some(&LineChange::RemovedBelow) => self.cached_removed_below.clone(),
100+ Some(&LineChange::Modified) => self.cached_modified.clone(),
101+ _ => self.cached_none.clone(),
102+ }
103+ } else {
104+ self.cached_none.clone()
105+ }
106+ // let status = printer.line_changes.and_then(|ref changes| changes.get(&(line_number as u32)));
107+ }
108+
109+ fn for_wrap(&self, _line_number: usize, _printer: &Printer) -> DecorationText {
110+ self.cached_none.clone()
111+ }
112+
113+ fn width(&self) -> usize {
114+ self.cached_none.width
115+ }
116+}
117+
118+// Grid border decoration.
119+pub struct GridBorderDecoration {
120+ cached: DecorationText,
121+}
122+
123+impl GridBorderDecoration {
124+ pub fn new(colors: &Colors) -> Self {
125+ GridBorderDecoration {
126+ cached: DecorationText {
127+ text: colors.grid.paint("│").to_string(),
128+ width: 1,
129+ },
130+ }
131+ }
132+}
133+
134+impl Decoration for GridBorderDecoration {
135+ fn for_line(&self, _line_number: usize, _printer: &Printer) -> DecorationText {
136+ self.cached.clone()
137+ }
138+
139+ fn for_wrap(&self, _line_number: usize, _printer: &Printer) -> DecorationText {
140+ self.cached.clone()
141+ }
142+
143+ fn width(&self) -> usize {
144+ self.cached.width
145+ }
146+}
src/main.rs+1
@@ -23,6 +23,7 @@ mod diff;
2323mod printer;
2424mod style;
2525mod terminal;
26+mod decorations;
2627
2728use std::fs::{self, File};
2829use std::io::{self, BufRead, BufReader, Write};
src/printer.rs+63−103
@@ -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!(