use unicode_width::UnicodeWidthStr;
use crate::{
formats::{StructureCandidateKind, structure_block_end},
transform::FormatKind,
};
use super::StructureViewport;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct CandidateVisibility {
fully_observed: bool,
end_line: Option<usize>,
}
impl CandidateVisibility {
fn unknown() -> Self {
Self {
fully_observed: false,
end_line: None,
}
}
fn fully_observed(end_line: usize) -> Self {
Self {
fully_observed: true,
end_line: Some(end_line),
}
}
fn partially_observed(end_line: Option<usize>) -> Self {
Self {
fully_observed: false,
end_line,
}
}
fn line_span(self, start_line: usize) -> Option<usize> {
self.end_line
.map(|end_line| end_line.saturating_sub(start_line).saturating_add(1))
}
}
pub(super) fn should_skip_candidate(
kind: StructureCandidateKind,
start_line: usize,
visibility: CandidateVisibility,
) -> bool {
visibility.fully_observed && !kind.is_landmark_when_visible(visibility.line_span(start_line))
}
pub(super) fn candidate_visibility(
format: FormatKind,
lines: &[String],
read_start: usize,
candidate_offset: usize,
line_count: usize,
line_count_exact: bool,
viewport: Option<StructureViewport>,
) -> CandidateVisibility {
let Some(viewport) = viewport else {
return CandidateVisibility::unknown();
};
let start_line = read_start + candidate_offset;
if start_line < viewport.top || start_line > viewport.bottom {
return CandidateVisibility::unknown();
}
if start_line == viewport.top && viewport.top_row_offset > 0 {
return CandidateVisibility::unknown();
}
let Some(end_line) = structure_block_end(
format,
lines,
read_start,
candidate_offset,
viewport.bottom,
line_count,
line_count_exact,
) else {
return CandidateVisibility::unknown();
};
if end_line > viewport.bottom {
return CandidateVisibility::partially_observed(Some(end_line));
}
if end_line == viewport.bottom && !viewport.bottom_line_end {
return CandidateVisibility::partially_observed(Some(end_line));
}
if block_is_horizontally_observed(lines, read_start, start_line, end_line, viewport) {
CandidateVisibility::fully_observed(end_line)
} else {
CandidateVisibility::partially_observed(Some(end_line))
}
}
fn block_is_horizontally_observed(
lines: &[String],
read_start: usize,
start_line: usize,
end_line: usize,
viewport: StructureViewport,
) -> bool {
if viewport.wrap {
return true;
}
if viewport.x > 0 || viewport.width == 0 {
return false;
}
let start_offset = start_line.saturating_sub(read_start);
let end_offset = end_line.saturating_sub(read_start);
lines
.get(start_offset..=end_offset)
.is_some_and(|block| block.iter().all(|line| line.width() <= viewport.width))
}