use std::io::Write;
use ansi_term::Colour::{Blue, Yellow};
use console::strip_ansi_codes;
use unicode_segmentation::UnicodeSegmentation;
use crate::bat::assets::HighlightingAssets;
use crate::cli;
use crate::config::Config;
use crate::draw;
use crate::paint::Painter;
use crate::parse;
use crate::style;
#[derive(Debug, PartialEq)]
pub enum State {
CommitMeta, // In commit metadata section
FileMeta, // In diff metadata section, between (possible) commit metadata and first hunk
HunkMeta, // In hunk metadata line
HunkZero, // In hunk; unchanged line
HunkMinus, // In hunk; removed line
HunkPlus, // In hunk; added line
Unknown,
}
#[derive(Debug, PartialEq)]
pub enum Source {
GitDiff, // Coming from a `git diff` command
DiffUnified, // Coming from a `diff -u` command
Unknown,
}
impl State {
fn is_in_hunk(&self) -> bool {
match *self {
State::HunkMeta | State::HunkZero | State::HunkMinus | State::HunkPlus => true,
_ => false,
}
}
}
// Possible transitions, with actions on entry:
//
//
// | from \ to | CommitMeta | FileMeta | HunkMeta | HunkZero | HunkMinus | HunkPlus |
// |-------------+-------------+-------------+-------------+-------------+-------------+----------|
// | CommitMeta | emit | emit | | | | |
// | FileMeta | | emit | emit | | | |
// | HunkMeta | | | | emit | push | push |
// | HunkZero | emit | emit | emit | emit | push | push |
// | HunkMinus | flush, emit | flush, emit | flush, emit | flush, emit | push | push |
// | HunkPlus | flush, emit | flush, emit | flush, emit | flush, emit | flush, push | push |
pub fn delta<I>(
lines: I,
config: &Config,
assets: &HighlightingAssets,
writer: &mut dyn Write,
) -> std::io::Result<()>
where
I: Iterator<Item = String>,
{
let mut lines_peekable = lines.peekable();
let mut painter = Painter::new(writer, config, assets);
let mut minus_file = "".to_string();
let mut plus_file;
let mut state = State::Unknown;
let source = detect_source(&mut lines_peekable);
for raw_line in lines_peekable {
if source == Source::Unknown {
writeln!(painter.writer, "{}", raw_line)?;
continue;
}
let line = strip_ansi_codes(&raw_line).to_string();
if line.starts_with("commit ") {
painter.paint_buffered_lines();
state = State::CommitMeta;
if config.opt.commit_style != cli::SectionStyle::Plain {
painter.emit()?;
handle_commit_meta_header_line(&mut painter, &raw_line, config)?;
continue;
}
} else if line.starts_with("diff ") {
painter.paint_buffered_lines();
state = State::FileMeta;
painter.set_syntax(parse::get_file_extension_from_diff_line(&line));
} else if (state == State::FileMeta || source == Source::DiffUnified)
// FIXME: For unified diff input, removal ("-") of a line starting with "--" (e.g. a
// Haskell or SQL comment) will be confused with the "---" file metadata marker.
&& (line.starts_with("--- ") || line.starts_with("rename from "))
&& config.opt.file_style != cli::SectionStyle::Plain
{
if source == Source::DiffUnified {
state = State::FileMeta;
painter.set_syntax(parse::get_file_extension_from_marker_line(&line));
}
minus_file = parse::get_file_path_from_file_meta_line(&line, source == Source::GitDiff);
} else if (line.starts_with("+++ ") || line.starts_with("rename to "))
&& config.opt.file_style != cli::SectionStyle::Plain
{
plus_file = parse::get_file_path_from_file_meta_line(&line, source == Source::GitDiff);
painter.emit()?;
handle_file_meta_header_line(
&mut painter,
&minus_file,
&plus_file,
config,
so