use std::borrow::Cow;
use lazy_static::lazy_static;
use regex::Regex;
use serde::Deserialize;
use unicode_segmentation::UnicodeSegmentation;
use crate::ansi;
use crate::delta::{State, StateMachine};
use crate::handlers::{self, ripgrep_json};
use crate::paint::{self, expand_tabs, BgShouldFill, StyleSectionSpecifier};
use crate::style::Style;
use crate::utils::process;
#[derive(Debug, PartialEq, Eq)]
pub struct GrepLine<'b> {
pub path: Cow<'b, str>,
pub line_number: Option<usize>,
pub line_type: LineType,
pub code: Cow<'b, str>,
pub submatches: Option<Vec<(usize, usize)>>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum LineType {
ContextHeader,
Context,
Match,
Ignore,
}
struct GrepOutputConfig {
add_navigate_marker_to_matches: bool,
render_context_header_as_hunk_header: bool,
pad_line_number: bool,
}
lazy_static! {
static ref OUTPUT_CONFIG: GrepOutputConfig = make_output_config();
}
impl<'a> StateMachine<'a> {
// If this is a line of git grep output then render it accordingly.
pub fn handle_grep_line(&mut self) -> std::io::Result<bool> {
self.painter.emit()?;
let mut handled_line = false;
let try_parse = matches!(&self.state, State::Grep | State::Unknown);
if try_parse {
if let Some(mut grep_line) = parse_grep_line(&self.line) {
if matches!(grep_line.line_type, LineType::Ignore) {
handled_line = true;
return Ok(handled_line);
}
// Emit syntax-highlighted code
// TODO: Determine the language less frequently, e.g. only when the file changes.
if let Some(lang) = handlers::diff_header::get_extension(&grep_line.path)
.or(self.config.default_language.as_deref())
{
self.painter.set_syntax(Some(lang));
self.painter.set_highlighter();
}
self.state = State::Grep;
match (
&grep_line.line_type,
OUTPUT_CONFIG.render_context_header_as_hunk_header,
) {
// Emit context header line
(LineType::ContextHeader, true) => handlers::hunk_header::write_hunk_header(
&grep_line.code,
&[(grep_line.line_number.unwrap_or(0), 0)],
&mut self.painter,
&self.line,
&grep_line.path,
self.config,
)?,
_ => {
if self.config.navigate {
write!(
self.painter.writer,
"{}",
match (
&grep_line.line_type,
OUTPUT_CONFIG.add_navigate_marker_to_matches
) {
(LineType::Match, true) => "• ",
(_, true) => " ",
_ => "",
}
)?
}
// Emit file & line-number
let separator = if