use std::fmt;
use std::iter::Peekable;
use std::ops::Range;
use crate::search::MatchRangeIter;
use crate::terminal;
use crate::terminal::{Style, Terminal};
use crate::truncatedstrview::TruncatedStrView;
pub const DEFAULT_STYLE: Style = Style::default();
pub const BOLD_STYLE: Style = Style {
bold: true,
..Style::default()
};
pub const BOLD_INVERTED_STYLE: Style = Style {
inverted: true,
bold: true,
..Style::default()
};
pub const GRAY_INVERTED_STYLE: Style = Style {
fg: terminal::LIGHT_BLACK,
inverted: true,
..Style::default()
};
pub const SEARCH_MATCH_HIGHLIGHTED: Style = Style {
fg: terminal::YELLOW,
inverted: true,
..Style::default()
};
pub const DIMMED_STYLE: Style = Style {
dimmed: true,
..Style::default()
};
pub const CURRENT_LINE_NUMBER: Style = Style {
fg: terminal::YELLOW,
..Style::default()
};
pub const PREVIEW_STYLES: (&Style, &Style) = (&DIMMED_STYLE, &GRAY_INVERTED_STYLE);
pub const BLUE_STYLE: Style = Style {
fg: terminal::LIGHT_BLUE,
..Style::default()
};
pub const INVERTED_BOLD_BLUE_STYLE: Style = Style {
bg: terminal::BLUE,
inverted: true,
bold: true,
..Style::default()
};
#[allow(clippy::too_many_arguments)]
pub fn highlight_truncated_str_view(
out: &mut dyn Terminal,
mut s: &str,
str_view: &TruncatedStrView,
mut str_range_start: Option<usize>,
style: &Style,
highlight_style: &Style,
matches_iter: &mut Option<&mut Peekable<MatchRangeIter<'_>>>,
focused_search_match: &Range<usize>,
) -> fmt::Result {
let mut leading_ellipsis = false;
let mut replacement_character = false;
let mut trailing_ellipsis = false;
if let Some(tr) = str_view.range {
leading_ellipsis = tr.print_leading_ellipsis();
replacement_character = tr.showing_replacement_character;
trailing_ellipsis = tr.print_trailing_ellipsis(s);
s = &s[tr.start..tr.end];
str_range_start = str_range_start.map(|start| start + tr.start);
}
if leading_ellipsis {
out.set_style(&DIMMED_STYLE)?;
out.write_char('…')?;
}
if replacement_character {
out.set_style(style)?;
out.write_char('�')?;
}
highlight_matches(
out,
s,
str_range_start,
style,
highlight_style,
matches_iter,
focused_search_match,
)?;
if trailing_ellipsis {
out.set_style(&DIMMED_STYLE)?;
out.write_char('…')?;
}
Ok(())
}
pub fn highlight_matches(
out: &mut dyn Terminal,
mut s: &str,
str_range_start: Option<usize>,
style: &Style,
highlight_style: &Style,
matches_iter: &mut Option<&mut Peekable<MatchRangeIter<'_>>>,
focused_search_match: &Range<usize>,
) -> fmt::Result {
if str_range_start.is_none() {
out.set_style(style)?;
write!(out, "{s}")?;
return Ok(());
}
let mut start_index = str_range_start.unwrap();
while !s.is_empty() {
let string_end = start_index + s.len();
let mut match_start = string_end;
let mut match_end = string_end;
let mut match_is_focused_match = false;
while let Some(range) = matches_iter.as_mut().and_then(|i| i.peek()) {
if start_index < range.end {
if *range == focused_search_match {
match_is_focused_match = true;
}
match_start = range.start.clamp(start_index, string_end);
match_end = range.end.clamp(start_index, string_end);
break;
}
matches_iter.as_mut().unwrap().next();
}
if start_index < match_start {
let print_end = match_start - start_index;
out.set_style(style)?;
write!(out, "{}", &s[..print_end])?;
}
if match_start < string_end {
if match_is_focused_match {
out.set_style(&BOLD_INVERTED_STYLE)?;
} else {
out.set_style(highlight_style)?;
}
let print_start = match_start - start_index;
let print_end = match_end - start_index;
write!(out, "{}", &s[print_start..print_end])?;
}
s = &s[(match_end - start_index)..];
start_index = match_end;
}
Ok(())
}