Skip to main content

hjkl_buffer/
span.rs

1/// One styled byte range on a buffer row.
2///
3/// Style spans are opaque-id tuples. The buffer does not own colors. The
4/// host (engine layer or terminal frontend) keeps the table mapping
5/// `style: u32` to a renderable type.
6///
7/// Byte ranges are **half-open**: `[start_byte, end_byte)`. They line up
8/// with the row's `String` so callers can slice without re-deriving
9/// indices.
10///
11/// The host installs spans via the engine's `Editor::install_syntax_spans`
12/// (or equivalent) after each edit. Spans are cleared for affected rows on
13/// every [`crate::Buffer::apply_edit`] call and must be re-installed by the
14/// host's syntax pipeline.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct Span {
17    pub start_byte: usize,
18    pub end_byte: usize,
19    /// Opaque style id resolved by the host's render layer.
20    pub style: u32,
21}
22
23impl Span {
24    pub const fn new(start_byte: usize, end_byte: usize, style: u32) -> Self {
25        Self {
26            start_byte,
27            end_byte,
28            style,
29        }
30    }
31
32    /// Width of the span in bytes; useful for render-cache fingerprints.
33    pub const fn len(self) -> usize {
34        self.end_byte.saturating_sub(self.start_byte)
35    }
36
37    pub const fn is_empty(self) -> bool {
38        self.end_byte <= self.start_byte
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::Span;
45
46    #[test]
47    fn len_and_is_empty() {
48        assert_eq!(Span::new(0, 5, 0).len(), 5);
49        assert!(Span::new(3, 3, 0).is_empty());
50        assert!(Span::new(7, 5, 0).is_empty());
51    }
52}