Skip to main content

strop_core/
range.rs

1//! Ranges with vim shape (0014): charwise ops carry the motion
2//! inclusivity (dfx vs dtx differ by it); linewise is line-shaped.
3
4use crate::id;
5
6/// How vim thinks about a range (0014): charwise ops carry the motion's
7/// inclusivity (dfx vs dtx differ by it); linewise is line-shaped.
8/// Blockwise lands with visual block — the enum is the extension point.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
10pub enum MotionShape {
11    Characterwise { inclusive: bool },
12    Linewise,
13}
14
15/// A half-open byte range `[start, end)` plus its vim shape. Fields are
16/// ByteOffset — the storage coordinate is typed end to end (0014).
17#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
18pub struct Range {
19    pub start: id::ByteOffset,
20    pub end: id::ByteOffset,
21    pub shape: MotionShape,
22}
23
24impl Range {
25    pub fn charwise(start: impl Into<id::ByteOffset>, end: impl Into<id::ByteOffset>) -> Self {
26        let (start, end) = (start.into(), end.into());
27        debug_assert!(start <= end);
28        Self {
29            start,
30            end,
31            shape: MotionShape::Characterwise { inclusive: false },
32        }
33    }
34    pub fn linewise(start: impl Into<id::ByteOffset>, end: impl Into<id::ByteOffset>) -> Self {
35        let (start, end) = (start.into(), end.into());
36        debug_assert!(start <= end);
37        Self {
38            start,
39            end,
40            shape: MotionShape::Linewise,
41        }
42    }
43    pub fn is_linewise(&self) -> bool {
44        matches!(self.shape, MotionShape::Linewise)
45    }
46    /// The resolver's inclusive flag folds into the shape (0014).
47    pub fn with_inclusive(mut self, inclusive: bool) -> Self {
48        if let MotionShape::Characterwise { inclusive: i } = &mut self.shape {
49            *i = inclusive;
50        }
51        self
52    }
53    pub fn inclusive(&self) -> bool {
54        matches!(self.shape, MotionShape::Characterwise { inclusive: true })
55    }
56    /// Length in bytes.
57    pub fn len(&self) -> usize {
58        self.end - self.start
59    }
60    pub fn is_empty(&self) -> bool {
61        self.start == self.end
62    }
63}