1use std::io::{self, Write};
3use crate::assets::HighlightingAssets;
4use crate::config::Config;
6use crate::input::{Input, InputReader, OpenedInput};
7use crate::line_range::{LineRanges, RangeCheckResult};
8use crate::output::OutputType;
9#[cfg(feature = "paging")]
10use crate::paging::PagingMode;
11use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
13pub struct Controller<'a> {
14 config: &'a Config<'a>,
15 assets: &'a HighlightingAssets,
18impl<'b> Controller<'b> {
19 pub fn new<'a>(config: &'a Config, assets: &'a HighlightingAssets) -> Controller<'a> {
20 Controller { config, assets }
23 pub fn run(&self, inputs: Vec<Input>) -> Result<bool> {
24 self.run_with_error_handler(inputs, default_error_handler)
27 pub fn run_with_error_handler(
30 handle_error: impl Fn(&Error),
34 #[cfg(feature = "paging")]
36 use crate::input::InputKind;
39 // Do not launch the pager if NONE of the input files exist
40 let mut paging_mode = self.config.paging_mode;
41 if self.config.paging_mode != PagingMode::Never {
42 let call_pager = inputs.iter().any(|ref input| {
43 if let InputKind::OrdinaryFile(ref path) = input.kind {
44 return Path::new(path).exists();
50 paging_mode = PagingMode::Never;
53 output_type = OutputType::from_mode(paging_mode, self.config.pager)?;
56 #[cfg(not(feature = "paging"))]
58 output_type = OutputType::stdout();
61 let writer = output_type.handle()?;
62 let mut no_errors: bool = true;
64 for input in inputs.into_iter() {
65 match input.open(io::stdin().lock()) {
70 Ok(mut opened_input) => {
71 let mut printer: Box<dyn Printer> = if self.config.loop_through {
72 Box::new(SimplePrinter::new())
74 Box::new(InteractivePrinter::new(
81 let result = self.print_file(&mut *printer, writer, &mut opened_input);
83 if let Err(error) = result {
96 printer: &mut dyn Printer,
97 writer: &mut dyn Write,
98 input: &mut OpenedInput,
100 if !input.reader.first_line.is_empty() || self.config.style_components.header() {
101 printer.print_header(writer, input)?;
104 if !input.reader.first_line.is_empty() {
105 self.print_file_ranges(printer, writer, &mut input.reader, &self.config.line_ranges)?;
107 printer.print_footer(writer, input)?;
112 fn print_file_ranges(
114 printer: &mut dyn Printer,
115 writer: &mut dyn Write,
116 reader: &mut InputReader,
117 line_ranges: &LineRanges,
119 let mut line_buffer = Vec::new();
120 let mut line_number: usize = 1;
122 let mut first_range: bool = true;
123 let mut mid_range: bool = false;
125 while reader.read_line(&mut line_buffer)? {
126 match line_ranges.check(line_number) {
127 RangeCheckResult::BeforeOrBetweenRanges => {
128 // Call the printer in case we need to call the syntax highlighter
129 // for this line. However, set `out_of_range` to `true`.
130 printer.print_line(true, writer, line_number, &line_buffer)?;
134 RangeCheckResult::InRange => {
135 if self.config.style_components.snip() {
139 } else if !mid_range {
141 printer.print_snip(writer)?;
145 printer.print_line(false, writer, line_number, &line_buffer)?;
147 RangeCheckResult::AfterLastRange => {
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list