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}