clankerdiff-ratatui 0.1.7

Embeddable Ratatui diff review widget
Documentation
use crate::annotation::{AnnotationBox, AnnotationKind};
use std::{collections::BTreeMap, ops::Range};

#[derive(Debug, Clone, Copy)]
pub(crate) enum AnnotationRow<'a> {
    Source(usize),
    Annotation {
        source: usize,
        annotation: &'a AnnotationBox,
        line: usize,
    },
}

#[derive(Debug, Default)]
pub(crate) struct AnnotationLayout {
    range: Range<usize>,
    annotations: Vec<(usize, usize, AnnotationBox)>,
    len: usize,
}

impl AnnotationLayout {
    pub(crate) fn new(range: Range<usize>, anchored: BTreeMap<usize, Vec<AnnotationBox>>) -> Self {
        let mut annotations = Vec::new();
        let mut extra = 0;
        for (source, boxes) in anchored {
            if !range.contains(&source) {
                continue;
            }
            for annotation in boxes {
                let visual = source - range.start + 1 + extra;
                extra += annotation.lines().len();
                annotations.push((source, visual, annotation));
            }
        }
        let len = range.len() + extra;
        Self {
            range,
            annotations,
            len,
        }
    }

    pub(crate) const fn len(&self) -> usize {
        self.len
    }
    pub(crate) const fn is_empty(&self) -> bool {
        self.len == 0
    }

    pub(crate) fn visual_offset_for_source(&self, source: usize) -> Option<usize> {
        self.range.contains(&source).then(|| {
            source - self.range.start
                + self
                    .annotations
                    .iter()
                    .take_while(|(anchor, _, _)| *anchor < source)
                    .map(|(_, _, annotation)| annotation.lines().len())
                    .sum::<usize>()
        })
    }

    pub(crate) fn focused_visual_row(&self, source: usize, draft: bool) -> Option<usize> {
        if draft {
            for (anchor, visual, annotation) in &self.annotations {
                if *anchor == source && annotation.kind() == AnnotationKind::Draft {
                    return Some(visual + annotation.cursor_line().unwrap_or(0));
                }
            }
        }
        self.visual_offset_for_source(source)
    }

    pub(crate) fn row(&self, visual: usize) -> Option<AnnotationRow<'_>> {
        if visual >= self.len {
            return None;
        }
        let mut extra = 0;
        for (source, start, annotation) in &self.annotations {
            if visual < *start {
                break;
            }
            if visual < start + annotation.lines().len() {
                return Some(AnnotationRow::Annotation {
                    source: *source,
                    annotation,
                    line: visual - start,
                });
            }
            extra += annotation.lines().len();
        }
        Some(AnnotationRow::Source(self.range.start + visual - extra))
    }
}