use crate::cell_renderer::Cell;
use crate::search::SearchMatch;
pub(crate) struct SearchHighlightParams<'a> {
pub cells: &'a mut [Cell],
pub cols: usize,
pub scroll_offset: usize,
pub scrollback_len: usize,
pub visible_lines: usize,
pub matches: &'a [SearchMatch],
pub current_match_idx: usize,
pub highlight_color: [u8; 4],
pub current_highlight_color: [u8; 4],
}
pub(crate) fn apply_search_highlights_to_cells(params: SearchHighlightParams<'_>) {
let SearchHighlightParams {
cells,
cols,
scroll_offset,
scrollback_len,
visible_lines,
matches,
current_match_idx,
highlight_color,
current_highlight_color,
} = params;
if matches.is_empty() {
return;
}
let total_lines = scrollback_len + visible_lines;
let visible_end = total_lines.saturating_sub(scroll_offset);
let visible_start = visible_end.saturating_sub(visible_lines);
for (match_idx, search_match) in matches.iter().enumerate() {
if search_match.line < visible_start || search_match.line >= visible_end {
continue;
}
let viewport_row = search_match.line - visible_start;
let color = if match_idx == current_match_idx {
current_highlight_color
} else {
highlight_color
};
for offset in 0..search_match.length {
let col = search_match.column + offset;
if col >= cols {
break;
}
let cell_idx = viewport_row * cols + col;
if cell_idx < cells.len() {
cells[cell_idx].bg_color = color;
}
}
}
}
pub(crate) fn get_all_searchable_lines(
term: &crate::terminal::TerminalManager,
visible_lines: usize,
) -> impl Iterator<Item = (usize, String)> {
let cols = term.dimensions().0;
let scrollback_len = term.scrollback_len();
let scrollback_lines = term.scrollback_as_cells();
let scrollback_iter = scrollback_lines
.into_iter()
.enumerate()
.map(move |(idx, cells)| {
let line = cells_row_to_string(&cells, cols);
(idx, line)
});
let screen_cells = term.get_cells_with_scrollback(0, None, false, None);
let current_lines = cells_to_lines(&screen_cells, cols, visible_lines);
let current_iter = current_lines
.into_iter()
.enumerate()
.map(move |(idx, line)| (scrollback_len + idx, line));
scrollback_iter.chain(current_iter)
}
fn cells_to_lines(cells: &[Cell], cols: usize, num_lines: usize) -> Vec<String> {
let mut lines = Vec::with_capacity(num_lines);
for row in 0..num_lines {
let row_start = row * cols;
let row_end = (row_start + cols).min(cells.len());
if row_start >= cells.len() {
lines.push(String::new());
continue;
}
let line = cells_row_to_string(&cells[row_start..row_end], cols);
lines.push(line);
}
lines
}
fn cells_row_to_string(cells: &[Cell], _cols: usize) -> String {
let line: String = cells
.iter()
.map(|cell| {
if cell.grapheme.is_empty() || cell.wide_char_spacer {
' '
} else {
cell.grapheme.chars().next().unwrap_or(' ')
}
})
.collect();
line.trim_end().to_string()
}