use lazy_static::lazy_static; use regex::Regex; use std::io::Write; use ansi_term; use syntect::easy::HighlightLines; use syntect::highlighting::Style as SyntectStyle; use syntect::parsing::{SyntaxReference, SyntaxSet}; use crate::config::{self, delta_unreachable}; use crate::delta::State; use crate::edits; use crate::features::line_numbers; use crate::paint::superimpose_style_sections::superimpose_style_sections; use crate::style::Style; pub const ANSI_CSI_CLEAR_TO_EOL: &str = "\x1b[0K"; pub const ANSI_CSI_CLEAR_TO_BOL: &str = "\x1b[1K"; pub const ANSI_SGR_RESET: &str = "\x1b[0m"; pub struct Painter<'a> { pub minus_lines: Vec, pub plus_lines: Vec, pub writer: &'a mut dyn Write, pub syntax: &'a SyntaxReference, pub highlighter: HighlightLines<'a>, pub config: &'a config::Config, pub output_buffer: String, pub minus_line_number: usize, pub plus_line_number: usize, } impl<'a> Painter<'a> { pub fn new(writer: &'a mut dyn Write, config: &'a config::Config) -> Self { let default_syntax = Self::get_syntax(&config.syntax_set, None); // TODO: Avoid doing this. let dummy_highlighter = HighlightLines::new(default_syntax, &config.syntax_dummy_theme); Self { minus_lines: Vec::new(), plus_lines: Vec::new(), output_buffer: String::new(), syntax: default_syntax, highlighter: dummy_highlighter, writer, config, minus_line_number: 0, plus_line_number: 0, } } pub fn set_syntax(&mut self, extension: Option<&str>) { self.syntax = Painter::get_syntax(&self.config.syntax_set, extension); } fn get_syntax(syntax_set: &'a SyntaxSet, extension: Option<&str>) -> &'a SyntaxReference { if let Some(extension) = extension { if let Some(syntax) = syntax_set.find_syntax_by_extension(extension) { return syntax; } } return syntax_set .find_syntax_by_extension("txt") .unwrap_or_else(|| { delta_unreachable("Failed to find any language syntax definitions.") }); } pub fn set_highlighter(&mut self) { if let Some(ref syntax_theme) = self.config.syntax_theme { self.highlighter = HighlightLines::new(self.syntax, &syntax_theme) }; } pub fn paint_buffered_lines(&mut self) { let minus_line_syntax_style_sections = Self::get_syntax_style_sections_for_lines( &self.minus_lines, &State::HunkMinus, &mut self.highlighter, self.config, ); let plus_line_syntax_style_sections = Self::get_syntax_style_sections_for_lines( &self.plus_lines, &State::HunkPlus, &mut self.highlighter, self.config, ); let (minus_line_diff_style_sections, plus_line_diff_style_sections) = Self::get_diff_style_sections(&self.minus_lines, &self.plus_lines, self.config); let mut minus_line_numbers = Vec::new(); let mut plus_line_numbers = Vec::new(); for _line in &self.minus_lines { minus_line_numbers.push(Some((Some(self.minus_line_number), None))); self.minus_line_number += 1; } for _line in &self.plus_lines { plus_line_numbers.push(Some((None, Some(self.plus_line_number)))); self.plus_line_number += 1; } // TODO: lines and style sections contain identical line text if !self.minus_lines.is_empty() { Painter::paint_lines( minus_line_syntax_style_sections, minus_line_diff_style_sections, minus_line_numbers, &mut self.output_buffer, self.config, if self.config.keep_plus_minus_markers { "-" } else { "" }, self.config.minus_style, self.config.minus_non_emph_style, Some(self.config.minus_empty_line_marker_style), None, ); } if !self.plus_lines.is_empty() { Painter::paint_lines( plus_line_syntax_style_sections, plus_line_diff_style_sections, plus_line_numbers, &mut self.output_buffer, self.config, if self.config.keep_plus_minus_markers { "+" } else { "" }, self.config.plus_style, self.config.plus_non_emph_style, Some(self.config.plus_empty_line_marker_style), None, ); } self.minus_lines.clear(); self.plus_lines.clear(); } /// Superimpose background styles and foreground syntax /// highlighting styles, and write colored lines to output buffer. pub fn paint_lines( syntax_style_sections: Vec>, diff_style_sections: Vec>, line_number_sections: Vec, Option)>>, output_buffer: &mut String, config: &config::Config, prefix: &str, style: Style, // style for right fill if line contains no emph sections non_emph_style: Style, // style for right fill if line contains emph sections empty_line_style: Option