on master help sign in
Split into Controller and Printer
commit246cf79dbd78665cdeccd5d2a141eb2b3548d582 authorsharkdp <davidpeter@web.de> dateThu 23 Aug 2018, 22:37 (8 years ago) parentea955c7
4 files changed+191−197
src/controller.rs+92 src/features.rs−153 src/main.rs+58−8 src/printer.rs+41−36
src/controller.rsnew file+92
@@ -0,0 +1,92 @@
1+use std::fs::File;
2+use std::io::{self, BufRead, BufReader, Write};
3+
4+use app::Config;
5+use assets::HighlightingAssets;
6+use errors::*;
7+use line_range::LineRange;
8+use output::OutputType;
9+use printer::{InteractivePrinter, Printer};
10+
11+pub struct Controller<'a> {
12+ config: &'a Config<'a>,
13+ assets: &'a HighlightingAssets,
14+}
15+
16+impl<'b> Controller<'b> {
17+ pub fn new<'a>(config: &'a Config, assets: &'a HighlightingAssets) -> Controller<'a> {
18+ Controller { config, assets }
19+ }
20+
21+ pub fn run(&self) -> Result<bool> {
22+ let mut output_type = OutputType::from_mode(self.config.paging_mode);
23+ let writer = output_type.handle()?;
24+ let mut no_errors: bool = true;
25+
26+ for file in &self.config.files {
27+ let result = self.print_file(writer, *file);
28+
29+ if let Err(error) = result {
30+ handle_error(&error);
31+ no_errors = false;
32+ }
33+ }
34+
35+ Ok(no_errors)
36+ }
37+
38+ fn print_file(&self, writer: &mut Write, filename: Option<&str>) -> Result<()> {
39+ let mut printer = InteractivePrinter::new(&self.config, &self.assets, filename);
40+
41+ let stdin = io::stdin();
42+ {
43+ let reader: Box<BufRead> = match filename {
44+ None => Box::new(stdin.lock()),
45+ Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
46+ };
47+
48+ printer.print_header(writer, filename)?;
49+ self.print_file_ranges(&mut printer, writer, reader, &self.config.line_range)?;
50+ printer.print_footer(writer)?;
51+ }
52+ Ok(())
53+ }
54+
55+ fn print_file_ranges<'a, P: Printer>(
56+ &self,
57+ printer: &mut P,
58+ writer: &mut Write,
59+ mut reader: Box<BufRead + 'a>,
60+ line_ranges: &Option<LineRange>,
61+ ) -> Result<()> {
62+ let mut buffer = Vec::new();
63+
64+ let mut line_number: usize = 1;
65+
66+ while reader.read_until(b'\n', &mut buffer)? > 0 {
67+ {
68+ let line = String::from_utf8_lossy(&buffer);
69+
70+ match line_ranges {
71+ &Some(ref range) => {
72+ if line_number < range.lower {
73+ // skip line
74+ } else if line_number > range.upper {
75+ // no more lines in range
76+ break;
77+ } else {
78+ printer.print_line(writer, line_number, &line)?;
79+ }
80+ }
81+ &None => {
82+ printer.print_line(writer, line_number, &line)?;
83+ }
84+ }
85+
86+ line_number += 1;
87+ }
88+ buffer.clear();
89+ }
90+ Ok(())
91+ }
92+}
src/features.rsdeleted−153
@@ -1,153 +0,0 @@
1−use std::fs::File;
2−use std::io::{self, BufRead, BufReader};
3−
4−use ansi_term::Colour::Green;
5−
6−use syntect::easy::HighlightLines;
7−use syntect::highlighting::Theme;
8−use syntect::parsing::SyntaxDefinition;
9−
10−use app::Config;
11−use assets::HighlightingAssets;
12−use diff::get_git_diff;
13−use errors::*;
14−use line_range::LineRange;
15−use output::OutputType;
16−use printer::{InteractivePrinter, Printer};
17−
18−pub fn list_languages(assets: &HighlightingAssets, term_width: usize) {
19− let mut languages = assets
20− .syntax_set
21− .syntaxes()
22− .iter()
23− .filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
24− .collect::<Vec<_>>();
25− languages.sort_by_key(|lang| lang.name.to_uppercase());
26−
27− let longest = languages
28− .iter()
29− .map(|syntax| syntax.name.len())
30− .max()
31− .unwrap_or(32); // Fallback width if they have no language definitions.
32−
33− let comma_separator = ", ";
34− let separator = " ";
35− // Line-wrapping for the possible file extension overflow.
36− let desired_width = term_width - longest - separator.len();
37−
38− for lang in languages {
39− print!("{:width$}{}", lang.name, separator, width = longest);
40−
41− // Number of characters on this line so far, wrap before `desired_width`
42− let mut num_chars = 0;
43−
44− let mut extension = lang.file_extensions.iter().peekable();
45− while let Some(word) = extension.next() {
46− // If we can't fit this word in, then create a line break and align it in.
47− let new_chars = word.len() + comma_separator.len();
48− if num_chars + new_chars >= desired_width {
49− num_chars = 0;
50− print!("\n{:width$}{}", "", separator, width = longest);
51− }
52−
53− num_chars += new_chars;
54− print!("{}", Green.paint(&word[..]));
55− if extension.peek().is_some() {
56− print!("{}", comma_separator);
57− }
58− }
59− println!();
60− }
61−}
62−
63−pub fn list_themes(assets: &HighlightingAssets) {
64− let themes = &assets.theme_set.themes;
65− for (theme, _) in themes.iter() {
66− println!("{}", theme);
67− }
68−}
69−
70−pub fn print_files(assets: &HighlightingAssets, config: &Config) -> Result<bool> {
71− let theme = assets.get_theme(&config.theme);
72−
73− let mut output_type = OutputType::from_mode(config.paging_mode);
74− let handle = output_type.handle()?;
75− let mut printer = InteractivePrinter::new(handle, &config, &theme);
76− let mut no_errors: bool = true;
77−
78− for file in &config.files {
79− printer.ansi_prefix_sgr.clear();
80− printer.line_changes = file.and_then(|filename| get_git_diff(filename));
81− let syntax = assets.get_syntax(config.language, *file);
82−
83− let result = print_file(config, theme, &syntax, &mut printer, *file);
84−
85− if let Err(error) = result {
86− handle_error(&error);
87− no_errors = false;
88− }
89− }
90−
91− Ok(no_errors)
92−}
93−
94−fn print_file<P: Printer>(
95− config: &Config,
96− theme: &Theme,
97− syntax: &SyntaxDefinition,
98− printer: &mut P,
99− filename: Option<&str>,
100−) -> Result<()> {
101− let stdin = io::stdin();
102− {
103− let reader: Box<BufRead> = match filename {
104− None => Box::new(stdin.lock()),
105− Some(filename) => Box::new(BufReader::new(File::open(filename)?)),
106− };
107−
108− let highlighter = HighlightLines::new(syntax, theme);
109−
110− printer.print_header(filename)?;
111− print_file_ranges(printer, reader, highlighter, &config.line_range)?;
112− printer.print_footer()?;
113− }
114− Ok(())
115−}
116−
117−fn print_file_ranges<'a, P: Printer>(
118− printer: &mut P,
119− mut reader: Box<BufRead + 'a>,
120− mut highlighter: HighlightLines,
121− line_ranges: &Option<LineRange>,
122−) -> Result<()> {
123− let mut buffer = Vec::new();
124−
125− let mut line_number: usize = 1;
126−
127− while reader.read_until(b'\n', &mut buffer)? > 0 {
128− {
129− let line = String::from_utf8_lossy(&buffer);
130− let regions = highlighter.highlight(line.as_ref());
131−
132− match line_ranges {
133− &Some(ref range) => {
134− if line_number < range.lower {
135− // skip line
136− } else if line_number > range.upper {
137− // no more lines in range
138− break;
139− } else {
140− printer.print_line(line_number, &regions)?;
141− }
142− }
143− &None => {
144− printer.print_line(line_number, &regions)?;
145− }
146− }
147−
148− line_number += 1;
149− }
150− buffer.clear();
151− }
152− Ok(())
153−}
src/main.rs+58−8
@@ -19,9 +19,9 @@ extern crate syntect;
1919
2020mod app;
2121mod assets;
22+mod controller;
2223mod decorations;
2324mod diff;
24−mod features;
2525mod line_range;
2626mod output;
2727mod printer;
@@ -32,9 +32,11 @@ use std::io;
3232use std::path::Path;
3333use std::process;
3434
35+use ansi_term::Colour::Green;
36+
3537use app::App;
3638use assets::{clear_assets, config_dir, HighlightingAssets};
37−use features::{list_languages, list_themes, print_files};
39+use controller::Controller;
3840
3941mod errors {
4042 error_chain! {
@@ -81,6 +83,58 @@ fn run_cache_subcommand(matches: &clap::ArgMatches) -> Result<()> {
8183 Ok(())
8284}
8385
86+pub fn list_languages(assets: &HighlightingAssets, term_width: usize) {
87+ let mut languages = assets
88+ .syntax_set
89+ .syntaxes()
90+ .iter()
91+ .filter(|syntax| !syntax.hidden && !syntax.file_extensions.is_empty())
92+ .collect::<Vec<_>>();
93+ languages.sort_by_key(|lang| lang.name.to_uppercase());
94+
95+ let longest = languages
96+ .iter()
97+ .map(|syntax| syntax.name.len())
98+ .max()
99+ .unwrap_or(32); // Fallback width if they have no language definitions.
100+
101+ let comma_separator = ", ";
102+ let separator = " ";
103+ // Line-wrapping for the possible file extension overflow.
104+ let desired_width = term_width - longest - separator.len();
105+
106+ for lang in languages {
107+ print!("{:width$}{}", lang.name, separator, width = longest);
108+
109+ // Number of characters on this line so far, wrap before `desired_width`
110+ let mut num_chars = 0;
111+
112+ let mut extension = lang.file_extensions.iter().peekable();
113+ while let Some(word) = extension.next() {
114+ // If we can't fit this word in, then create a line break and align it in.
115+ let new_chars = word.len() + comma_separator.len();
116+ if num_chars + new_chars >= desired_width {
117+ num_chars = 0;
118+ print!("\n{:width$}{}", "", separator, width = longest);
119+ }
120+
121+ num_chars += new_chars;
122+ print!("{}", Green.paint(&word[..]));
123+ if extension.peek().is_some() {
124+ print!("{}", comma_separator);
125+ }
126+ }
127+ println!();
128+ }
129+}
130+
131+pub fn list_themes(assets: &HighlightingAssets) {
132+ let themes = &assets.theme_set.themes;
133+ for (theme, _) in themes.iter() {
134+ println!("{}", theme);
135+ }
136+}
137+
84138/// Returns `Err(..)` upon fatal errors. Otherwise, returns `Some(true)` on full success and
85139/// `Some(false)` if any intermediate errors occurred (were printed).
86140fn run() -> Result<bool> {
@@ -104,12 +158,8 @@ fn run() -> Result<bool> {
104158
105159 Ok(true)
106160 } else {
107− if config.loop_through {
108− // TODO
109− print_files(&assets, &config)
110− } else {
111− print_files(&assets, &config)
112− }
161+ let controller = Controller::new(&config, &assets);
162+ controller.run()
113163 }
114164 }
115165 }
src/printer.rs+41−36
@@ -7,37 +7,38 @@ use ansi_term::Style;
77
88use console::AnsiCodeIterator;
99
10−use syntect::highlighting::{self, Theme};
10+use syntect::easy::HighlightLines;
11+use syntect::highlighting::Theme;
1112
1213use app::Config;
14+use assets::HighlightingAssets;
1315use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
16+use diff::get_git_diff;
1417use diff::LineChanges;
1518use errors::*;
1619use style::OutputWrap;
1720use terminal::{as_terminal_escaped, to_ansi_color};
1821
1922pub trait Printer {
20− fn print_header(&mut self, filename: Option<&str>) -> Result<()>;
21− fn print_footer(&mut self) -> Result<()>;
22− fn print_line(
23− &mut self,
24− line_number: usize,
25− regions: &[(highlighting::Style, &str)],
26− ) -> Result<()>;
23+ fn print_header(&mut self, handle: &mut Write, filename: Option<&str>) -> Result<()>;
24+ fn print_footer(&mut self, handle: &mut Write) -> Result<()>;
25+ fn print_line(&mut self, handle: &mut Write, line_number: usize, line: &str) -> Result<()>;
2726}
2827
2928pub struct InteractivePrinter<'a> {
30− handle: &'a mut Write,
3129 colors: Colors,
32− pub config: &'a Config<'a>,
30+ config: &'a Config<'a>,
3331 decorations: Vec<Box<Decoration>>,
3432 panel_width: usize,
35− pub ansi_prefix_sgr: String,
33+ ansi_prefix_sgr: String,
3634 pub line_changes: Option<LineChanges>,
35+ highlighter: HighlightLines<'a>,
3736}
3837
3938impl<'a> InteractivePrinter<'a> {
40− pub fn new(handle: &'a mut Write, config: &'a Config, theme: &Theme) -> Self {
39+ pub fn new(config: &'a Config, assets: &'a HighlightingAssets, filename: Option<&str>) -> Self {
40+ let theme = assets.get_theme(&config.theme);
41+
4142 let colors = if config.colored_output {
4243 Colors::colored(theme, config.true_color)
4344 } else {
@@ -74,29 +75,35 @@ impl<'a> InteractivePrinter<'a> {
7475 panel_width = 0;
7576 }
7677
77− // Create printer.
78+ // Get the Git modifications
79+ let line_changes = filename.and_then(|file| get_git_diff(file));
80+
81+ // Determine the type of syntax for highlighting
82+ let syntax = assets.get_syntax(config.language, filename);
83+ let highlighter = HighlightLines::new(syntax, theme);
84+
7885 InteractivePrinter {
7986 panel_width,
80− handle,
8187 colors,
8288 config,
8389 decorations,
8490 ansi_prefix_sgr: String::new(),
85− line_changes: None,
91+ line_changes,
92+ highlighter,
8693 }
8794 }
8895
89− fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
96+ fn print_horizontal_line(&mut self, handle: &mut Write, grid_char: char) -> Result<()> {
9097 if self.panel_width == 0 {
9198 writeln!(
92− self.handle,
99+ handle,
93100 "{}",
94101 self.colors.grid.paint("─".repeat(self.config.term_width))
95102 )?;
96103 } else {
97104 let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
98105 let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
99− writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
106+ writeln!(handle, "{}", self.colors.grid.paint(hline))?;
100107 }
101108
102109 Ok(())
@@ -104,16 +111,16 @@ impl<'a> InteractivePrinter<'a> {
104111}
105112
106113impl<'a> Printer for InteractivePrinter<'a> {
107− fn print_header(&mut self, filename: Option<&str>) -> Result<()> {
114+ fn print_header(&mut self, handle: &mut Write, filename: Option<&str>) -> Result<()> {
108115 if !self.config.output_components.header() {
109116 return Ok(());
110117 }
111118
112119 if self.config.output_components.grid() {
113− self.print_horizontal_line('┬')?;
120+ self.print_horizontal_line(handle, '┬')?;
114121
115122 write!(
116− self.handle,
123+ handle,
117124 "{}{}",
118125 " ".repeat(self.panel_width),
119126 self.colors
@@ -121,36 +128,34 @@ impl<'a> Printer for InteractivePrinter<'a> {
121128 .paint(if self.panel_width > 0 { "│ " } else { "" }),
122129 )?;
123130 } else {
124− write!(self.handle, "{}", " ".repeat(self.panel_width))?;
131+ write!(handle, "{}", " ".repeat(self.panel_width))?;
125132 }
126133
127134 writeln!(
128− self.handle,
135+ handle,
129136 "{}{}",
130137 filename.map_or("", |_| "File: "),
131138 self.colors.filename.paint(filename.unwrap_or("STDIN"))
132139 )?;
133140
134141 if self.config.output_components.grid() {
135− self.print_horizontal_line('┼')?;
142+ self.print_horizontal_line(handle, '┼')?;
136143 }
137144
138145 Ok(())
139146 }
140147
141− fn print_footer(&mut self) -> Result<()> {
148+ fn print_footer(&mut self, handle: &mut Write) -> Result<()> {
142149 if self.config.output_components.grid() {
143− self.print_horizontal_line('┴')
150+ self.print_horizontal_line(handle, '┴')
144151 } else {
145152 Ok(())
146153 }
147154 }
148155
149− fn print_line(
150− &mut self,
151− line_number: usize,
152− regions: &[(highlighting::Style, &str)],
153− ) -> Result<()> {
156+ fn print_line(&mut self, handle: &mut Write, line_number: usize, line: &str) -> Result<()> {
157+ let regions = self.highlighter.highlight(line.as_ref());
158+
154159 let mut cursor: usize = 0;
155160 let mut cursor_max: usize = self.config.term_width;
156161 let mut panel_wrap: Option<String> = None;
@@ -164,7 +169,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
164169 .collect::<Vec<_>>();
165170
166171 for deco in decorations {
167− write!(self.handle, "{} ", deco.text)?;
172+ write!(handle, "{} ", deco.text)?;
168173 cursor_max -= deco.width + 1;
169174 }
170175 }
@@ -175,7 +180,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
175180 let colored_output = self.config.colored_output;
176181
177182 write!(
178− self.handle,
183+ handle,
179184 "{}",
180185 regions
181186 .iter()
@@ -222,7 +227,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
222227 cursor += remaining;
223228
224229 write!(
225− self.handle,
230+ handle,
226231 "{}",
227232 as_terminal_escaped(
228233 style,
@@ -260,7 +265,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
260265 remaining -= available;
261266
262267 write!(
263− self.handle,
268+ handle,
264269 "{}\n{}",
265270 as_terminal_escaped(
266271 style,
@@ -282,7 +287,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
282287 }
283288 }
284289
285− write!(self.handle, "\n")?;
290+ write!(handle, "\n")?;
286291 }
287292
288293 Ok(())