use crate::style::Style;
use super::span::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ResolvedSpan {
pub start: usize,
pub end: usize,
pub style: Style,
}
pub(crate) fn merge_spans(input_len: usize, spans: &[Span]) -> Vec<ResolvedSpan> {
if spans.is_empty() {
return Vec::new();
}
let mut style_map: Vec<Option<(Style, u16)>> = vec![None; input_len];
for span in spans {
for slot in &mut style_map[span.start..span.end] {
match slot {
None => *slot = Some((span.style, span.priority)),
Some((_, existing_pri)) if span.priority < *existing_pri => {
*slot = Some((span.style, span.priority));
}
_ => {}
}
}
}
let mut result = Vec::new();
let mut i = 0;
while i < input_len {
if let Some((style, _)) = style_map[i] {
let start = i;
while i < input_len && style_map[i].is_some_and(|(s, _)| s == style) {
i += 1;
}
result.push(ResolvedSpan { start, end: i, style });
} else {
i += 1;
}
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use crate::style::Color;
fn red() -> Style {
Style::new().fg(Color::Red)
}
fn blue() -> Style {
Style::new().fg(Color::Blue)
}
fn yellow() -> Style {
Style::new().fg(Color::Yellow)
}
#[test]
fn empty_spans() {
let result = merge_spans(10, &[]);
assert!(result.is_empty());
}
fn resolved(start: usize, end: usize, style: Style) -> ResolvedSpan {
ResolvedSpan { start, end, style }
}
#[test]
fn single_span() {
let spans = [Span::new(2, 5, red(), 0)];
let result = merge_spans(10, &spans);
assert_eq!(result, vec![resolved(2, 5, red())]);
}
#[test]
fn non_overlapping_spans() {
let spans = [Span::new(0, 3, red(), 0), Span::new(5, 8, blue(), 1)];
let result = merge_spans(10, &spans);
assert_eq!(result, vec![resolved(0, 3, red()), resolved(5, 8, blue())]);
}
#[test]
fn overlapping_higher_priority_wins() {
let spans = [Span::new(0, 6, red(), 0), Span::new(3, 8, blue(), 1)];
let result = merge_spans(10, &spans);
assert_eq!(result, vec![resolved(0, 6, red()), resolved(6, 8, blue())]);
}
#[test]
fn lower_priority_fills_gaps() {
let spans = [Span::new(5, 7, red(), 0), Span::new(0, 10, yellow(), 1)];
let result = merge_spans(10, &spans);
assert_eq!(
result,
vec![
resolved(0, 5, yellow()),
resolved(5, 7, red()),
resolved(7, 10, yellow())
]
);
}
#[test]
fn adjacent_different_styles() {
let spans = [Span::new(0, 3, red(), 0), Span::new(3, 6, blue(), 0)];
let result = merge_spans(6, &spans);
assert_eq!(result, vec![resolved(0, 3, red()), resolved(3, 6, blue())]);
}
}