Skip to main content

twrite_core/
selection.rs

1use std::ops::Range;
2
3/// Represents a text selection using an anchor and a head.
4///
5/// The anchor is the position where the selection started, while the head
6/// is the current cursor position. When they differ, the selection extends
7/// between these two positions.
8///
9/// Both positions are byte offsets into the document.
10///
11/// # Examples
12///
13/// A collapsed selection, where the cursor is at byte offset 10:
14///
15/// ```
16/// # use twrite_core::Selection;
17/// let selection = Selection::point(10);
18/// assert!(selection.is_empty());
19/// ```
20///
21/// A selection from byte offset 10 to 20:
22///
23/// ```
24/// # use twrite_core::Selection;
25/// let selection = Selection::range(10, 20);
26/// assert_eq!(selection.byte_range(), 10..20);
27/// ```
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Selection {
30    /// The byte offset where the selection started.
31    ///
32    /// This remains fixed while the selection is extended or contracted
33    /// by moving the head.
34    pub anchor: usize,
35
36    /// The current cursor position, represented as a byte offset.
37    ///
38    /// Moving the head changes the extent and direction of the selection
39    /// while the anchor remains fixed.
40    pub head: usize,
41}
42
43impl Selection {
44    /// Creates a collapsed selection with the cursor at `offset`.
45    ///
46    /// Both the anchor and head are initialized to the same position.
47    pub const fn point(offset: usize) -> Self {
48        Self {
49            anchor: offset,
50            head: offset,
51        }
52    }
53
54    /// Creates a selection from an anchor position to a head position.
55    ///
56    /// The positions are not reordered, so `anchor` may be greater than
57    /// `head`. Use [`Self::byte_range`] to obtain the normalized range.
58    pub const fn range(anchor: usize, head: usize) -> Self {
59        Self { anchor, head }
60    }
61
62    /// Returns `true` if the selection is collapsed.
63    ///
64    /// A collapsed selection has the anchor and head at the same position.
65    pub fn is_empty(&self) -> bool {
66        self.anchor == self.head
67    }
68
69    /// Returns the normalized byte range covered by the selection.
70    ///
71    /// The returned range always starts at the smaller of the anchor and
72    /// head and ends at the larger, regardless of the direction in which
73    /// the selection was made.
74    pub fn byte_range(&self) -> Range<usize> {
75        self.anchor.min(self.head)..self.anchor.max(self.head)
76    }
77}