2020-11-25 00:24:04 +01:00
|
|
|
use std::convert::TryFrom;
|
2018-10-07 12:15:49 +02:00
|
|
|
use std::io::{self, Write};
|
2018-08-23 22:37:27 +02:00
|
|
|
|
2019-10-06 03:44:14 +02:00
|
|
|
use crate::assets::HighlightingAssets;
|
2020-04-23 23:39:30 +02:00
|
|
|
use crate::config::{Config, VisibleLines};
|
|
|
|
#[cfg(feature = "git")]
|
|
|
|
use crate::diff::{get_git_diff, LineChanges};
|
2020-04-22 21:45:47 +02:00
|
|
|
use crate::error::*;
|
2020-04-22 21:50:21 +02:00
|
|
|
use crate::input::{Input, InputReader, OpenedInput};
|
2020-04-23 23:39:30 +02:00
|
|
|
#[cfg(feature = "git")]
|
|
|
|
use crate::line_range::LineRange;
|
2019-10-06 03:44:14 +02:00
|
|
|
use crate::line_range::{LineRanges, RangeCheckResult};
|
|
|
|
use crate::output::OutputType;
|
2020-04-22 22:54:33 +02:00
|
|
|
#[cfg(feature = "paging")]
|
|
|
|
use crate::paging::PagingMode;
|
2019-10-06 03:44:14 +02:00
|
|
|
use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
|
2018-08-23 22:37:27 +02:00
|
|
|
|
|
|
|
pub struct Controller<'a> {
|
|
|
|
config: &'a Config<'a>,
|
|
|
|
assets: &'a HighlightingAssets,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'b> Controller<'b> {
|
|
|
|
pub fn new<'a>(config: &'a Config, assets: &'a HighlightingAssets) -> Controller<'a> {
|
|
|
|
Controller { config, assets }
|
|
|
|
}
|
|
|
|
|
2020-04-21 21:19:06 +02:00
|
|
|
pub fn run(&self, inputs: Vec<Input>) -> Result<bool> {
|
2020-04-21 21:14:44 +02:00
|
|
|
self.run_with_error_handler(inputs, default_error_handler)
|
2020-03-21 19:51:59 +01:00
|
|
|
}
|
|
|
|
|
2020-04-21 21:14:44 +02:00
|
|
|
pub fn run_with_error_handler(
|
|
|
|
&self,
|
2020-04-21 21:19:06 +02:00
|
|
|
inputs: Vec<Input>,
|
2020-04-25 12:25:25 +02:00
|
|
|
handle_error: impl Fn(&Error, &mut dyn Write),
|
2020-04-21 21:14:44 +02:00
|
|
|
) -> Result<bool> {
|
2020-03-30 22:18:41 +02:00
|
|
|
let mut output_type;
|
|
|
|
|
|
|
|
#[cfg(feature = "paging")]
|
|
|
|
{
|
2020-04-22 21:50:21 +02:00
|
|
|
use crate::input::InputKind;
|
2020-03-30 22:18:41 +02:00
|
|
|
use std::path::Path;
|
|
|
|
|
|
|
|
// Do not launch the pager if NONE of the input files exist
|
|
|
|
let mut paging_mode = self.config.paging_mode;
|
|
|
|
if self.config.paging_mode != PagingMode::Never {
|
2020-04-22 16:27:34 +02:00
|
|
|
let call_pager = inputs.iter().any(|ref input| {
|
|
|
|
if let InputKind::OrdinaryFile(ref path) = input.kind {
|
2020-04-24 08:46:01 +02:00
|
|
|
Path::new(path).exists()
|
2020-03-30 22:18:41 +02:00
|
|
|
} else {
|
2020-04-24 08:46:01 +02:00
|
|
|
true
|
2020-03-30 22:18:41 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
if !call_pager {
|
|
|
|
paging_mode = PagingMode::Never;
|
2018-12-14 21:44:48 +01:00
|
|
|
}
|
|
|
|
}
|
2020-10-07 17:28:25 +02:00
|
|
|
|
|
|
|
let wrapping_mode = self.config.wrapping_mode;
|
|
|
|
|
|
|
|
output_type = OutputType::from_mode(paging_mode, wrapping_mode, self.config.pager)?;
|
2020-03-30 22:18:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "paging"))]
|
|
|
|
{
|
|
|
|
output_type = OutputType::stdout();
|
2018-12-14 21:44:48 +01:00
|
|
|
}
|
|
|
|
|
2020-04-25 12:25:25 +02:00
|
|
|
let attached_to_pager = output_type.is_pager();
|
2020-11-25 00:24:04 +01:00
|
|
|
let rw_cycle_detected = !attached_to_pager && {
|
|
|
|
let identifiers: Vec<_> = inputs
|
|
|
|
.iter()
|
|
|
|
.flat_map(clircle::Identifier::try_from)
|
|
|
|
.collect();
|
|
|
|
clircle::stdout_among_inputs(&identifiers)
|
|
|
|
};
|
|
|
|
|
2018-08-23 22:37:27 +02:00
|
|
|
let writer = output_type.handle()?;
|
|
|
|
let mut no_errors: bool = true;
|
|
|
|
|
2020-04-25 12:25:25 +02:00
|
|
|
let stderr = io::stderr();
|
|
|
|
let print_error = |error: &Error, write: &mut dyn Write| {
|
|
|
|
if attached_to_pager {
|
|
|
|
handle_error(error, write);
|
|
|
|
} else {
|
|
|
|
handle_error(error, &mut stderr.lock());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2020-11-25 00:24:04 +01:00
|
|
|
if rw_cycle_detected {
|
|
|
|
print_error(&"The output file is also an input!".into(), writer);
|
|
|
|
return Ok(false);
|
|
|
|
}
|
|
|
|
|
2020-05-12 02:57:51 +02:00
|
|
|
for (index, input) in inputs.into_iter().enumerate() {
|
2020-04-22 16:27:34 +02:00
|
|
|
match input.open(io::stdin().lock()) {
|
2018-10-11 21:54:19 +02:00
|
|
|
Err(error) => {
|
2020-04-25 12:25:25 +02:00
|
|
|
print_error(&error, writer);
|
2018-10-11 21:54:19 +02:00
|
|
|
no_errors = false;
|
|
|
|
}
|
2020-04-22 16:27:34 +02:00
|
|
|
Ok(mut opened_input) => {
|
2020-04-23 23:39:30 +02:00
|
|
|
#[cfg(feature = "git")]
|
2020-04-24 16:06:04 +02:00
|
|
|
let line_changes = if self.config.visible_lines.diff_mode()
|
2020-04-23 23:39:30 +02:00
|
|
|
|| (!self.config.loop_through && self.config.style_components.changes())
|
|
|
|
{
|
2020-04-24 16:06:04 +02:00
|
|
|
match opened_input.kind {
|
|
|
|
crate::input::OpenedInputKind::OrdinaryFile(ref path) => {
|
|
|
|
let diff = get_git_diff(path);
|
|
|
|
|
|
|
|
// Skip files without Git modifications
|
|
|
|
if self.config.visible_lines.diff_mode()
|
|
|
|
&& diff
|
|
|
|
.as_ref()
|
|
|
|
.map(|changes| changes.is_empty())
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
diff
|
|
|
|
}
|
|
|
|
_ if self.config.visible_lines.diff_mode() => {
|
|
|
|
// Skip non-file inputs in diff mode
|
2020-04-23 23:39:30 +02:00
|
|
|
continue;
|
|
|
|
}
|
2020-04-24 16:06:04 +02:00
|
|
|
_ => None,
|
2020-04-23 23:39:30 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
|
|
|
|
2020-04-22 16:27:34 +02:00
|
|
|
let mut printer: Box<dyn Printer> = if self.config.loop_through {
|
2020-06-20 17:00:32 +02:00
|
|
|
Box::new(SimplePrinter::new(&self.config))
|
2018-10-11 21:54:19 +02:00
|
|
|
} else {
|
2020-04-22 16:27:34 +02:00
|
|
|
Box::new(InteractivePrinter::new(
|
2018-10-11 21:54:19 +02:00
|
|
|
&self.config,
|
|
|
|
&self.assets,
|
2020-04-22 16:27:34 +02:00
|
|
|
&mut opened_input,
|
2020-04-23 23:39:30 +02:00
|
|
|
#[cfg(feature = "git")]
|
|
|
|
&line_changes,
|
2020-05-16 11:42:29 +02:00
|
|
|
)?)
|
2018-10-11 21:54:19 +02:00
|
|
|
};
|
2018-10-07 13:26:50 +02:00
|
|
|
|
2020-04-23 23:39:30 +02:00
|
|
|
let result = self.print_file(
|
|
|
|
&mut *printer,
|
|
|
|
writer,
|
|
|
|
&mut opened_input,
|
2020-05-12 02:57:51 +02:00
|
|
|
index != 0,
|
2020-04-23 23:39:30 +02:00
|
|
|
#[cfg(feature = "git")]
|
|
|
|
&line_changes,
|
|
|
|
);
|
2020-04-22 16:27:34 +02:00
|
|
|
|
2018-10-11 21:54:19 +02:00
|
|
|
if let Err(error) = result {
|
2020-04-25 12:25:25 +02:00
|
|
|
print_error(&error, writer);
|
2018-10-11 21:54:19 +02:00
|
|
|
no_errors = false;
|
|
|
|
}
|
|
|
|
}
|
2018-08-23 22:37:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(no_errors)
|
|
|
|
}
|
|
|
|
|
2020-11-05 22:29:04 +01:00
|
|
|
fn print_file(
|
2018-08-23 23:13:24 +02:00
|
|
|
&self,
|
2020-04-22 16:27:34 +02:00
|
|
|
printer: &mut dyn Printer,
|
2019-08-02 09:14:57 +02:00
|
|
|
writer: &mut dyn Write,
|
2020-04-22 16:27:34 +02:00
|
|
|
input: &mut OpenedInput,
|
2020-05-12 02:57:51 +02:00
|
|
|
add_header_padding: bool,
|
2020-04-25 12:25:25 +02:00
|
|
|
#[cfg(feature = "git")] line_changes: &Option<LineChanges>,
|
2018-08-23 23:13:24 +02:00
|
|
|
) -> Result<()> {
|
2020-04-22 16:27:34 +02:00
|
|
|
if !input.reader.first_line.is_empty() || self.config.style_components.header() {
|
2020-05-12 02:57:51 +02:00
|
|
|
printer.print_header(writer, input, add_header_padding)?;
|
2020-02-26 20:53:58 +01:00
|
|
|
}
|
|
|
|
|
2020-04-22 16:27:34 +02:00
|
|
|
if !input.reader.first_line.is_empty() {
|
2020-04-23 23:39:30 +02:00
|
|
|
let line_ranges = match self.config.visible_lines {
|
|
|
|
VisibleLines::Ranges(ref line_ranges) => line_ranges.clone(),
|
|
|
|
#[cfg(feature = "git")]
|
|
|
|
VisibleLines::DiffContext(context) => {
|
|
|
|
let mut line_ranges: Vec<LineRange> = vec![];
|
|
|
|
|
|
|
|
if let Some(line_changes) = line_changes {
|
|
|
|
for line in line_changes.keys() {
|
|
|
|
let line = *line as usize;
|
|
|
|
line_ranges.push(LineRange::new(line - context, line + context));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LineRanges::from(line_ranges)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
self.print_file_ranges(printer, writer, &mut input.reader, &line_ranges)?;
|
2019-02-10 05:21:12 +01:00
|
|
|
}
|
2020-04-22 16:27:34 +02:00
|
|
|
printer.print_footer(writer, input)?;
|
2018-08-23 22:37:27 +02:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-04-22 16:27:34 +02:00
|
|
|
fn print_file_ranges(
|
2018-08-23 22:37:27 +02:00
|
|
|
&self,
|
2020-04-22 16:27:34 +02:00
|
|
|
printer: &mut dyn Printer,
|
2019-08-02 09:14:57 +02:00
|
|
|
writer: &mut dyn Write,
|
2020-04-22 16:27:34 +02:00
|
|
|
reader: &mut InputReader,
|
2018-10-20 00:10:10 +02:00
|
|
|
line_ranges: &LineRanges,
|
2018-08-23 22:37:27 +02:00
|
|
|
) -> Result<()> {
|
2018-08-23 23:13:24 +02:00
|
|
|
let mut line_buffer = Vec::new();
|
2018-08-23 22:37:27 +02:00
|
|
|
let mut line_number: usize = 1;
|
|
|
|
|
2019-05-25 03:24:13 +02:00
|
|
|
let mut first_range: bool = true;
|
|
|
|
let mut mid_range: bool = false;
|
|
|
|
|
2020-12-02 09:29:49 +01:00
|
|
|
let style_snip = self.config.style_components.snip();
|
|
|
|
|
2018-10-07 12:15:49 +02:00
|
|
|
while reader.read_line(&mut line_buffer)? {
|
2018-10-20 00:10:10 +02:00
|
|
|
match line_ranges.check(line_number) {
|
2020-03-21 16:51:38 +01:00
|
|
|
RangeCheckResult::BeforeOrBetweenRanges => {
|
2018-10-20 00:10:10 +02:00
|
|
|
// Call the printer in case we need to call the syntax highlighter
|
|
|
|
// for this line. However, set `out_of_range` to `true`.
|
|
|
|
printer.print_line(true, writer, line_number, &line_buffer)?;
|
2019-05-25 03:24:13 +02:00
|
|
|
mid_range = false;
|
2018-08-23 22:37:27 +02:00
|
|
|
}
|
2019-05-25 03:24:13 +02:00
|
|
|
|
2018-10-20 00:10:10 +02:00
|
|
|
RangeCheckResult::InRange => {
|
2020-12-02 09:29:49 +01:00
|
|
|
if style_snip {
|
2019-05-25 03:24:13 +02:00
|
|
|
if first_range {
|
|
|
|
first_range = false;
|
|
|
|
mid_range = true;
|
|
|
|
} else if !mid_range {
|
|
|
|
mid_range = true;
|
|
|
|
printer.print_snip(writer)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-07 10:09:10 +02:00
|
|
|
printer.print_line(false, writer, line_number, &line_buffer)?;
|
|
|
|
}
|
2018-10-20 00:10:10 +02:00
|
|
|
RangeCheckResult::AfterLastRange => {
|
|
|
|
break;
|
|
|
|
}
|
2018-08-23 22:37:27 +02:00
|
|
|
}
|
2018-10-07 10:09:10 +02:00
|
|
|
|
|
|
|
line_number += 1;
|
2018-08-23 23:13:24 +02:00
|
|
|
line_buffer.clear();
|
2018-08-23 22:37:27 +02:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|