1use crate::{buffer::Buffer, dot::Cur};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26pub struct Range {
27 pub start: Cur,
28 pub end: Cur,
29 pub start_active: bool,
30}
31
32impl Range {
33 pub const BOF: Self = Range {
35 start: Cur { idx: 0 },
36 end: Cur { idx: 0 },
37 start_active: false,
38 };
39
40 pub(crate) fn from_cursors(c1: Cur, c2: Cur, c1_was_active: bool) -> Self {
41 let (start, end, start_active) = if c1 <= c2 {
42 (c1, c2, c1_was_active)
43 } else if c1_was_active {
44 (c2, c1, false)
45 } else {
46 (c2, c1, true)
47 };
48
49 Self {
50 start,
51 end,
52 start_active,
53 }
54 }
55
56 pub fn as_string_addr(&self, b: &Buffer) -> String {
57 format!(
58 "{},{}",
59 self.start.as_string_addr(b),
60 self.end.as_string_addr(b)
61 )
62 }
63
64 pub(crate) fn contains(&self, cur: &Cur) -> bool {
65 cur.idx >= self.start.idx && cur.idx <= self.end.idx
66 }
67
68 pub(crate) fn intersects_range(&self, other: &Range) -> bool {
69 self.start.idx <= other.end.idx && other.start.idx <= self.end.idx
70 }
71
72 #[must_use]
74 pub(super) fn extend_to_line_start(mut self, b: &Buffer) -> Self {
75 self.start = self.start.move_to_line_start(b);
76 self
77 }
78
79 #[must_use]
81 pub(super) fn extend_to_line_end(mut self, b: &Buffer) -> Self {
82 self.end = self.end.move_to_line_end(b);
83 self
84 }
85
86 pub fn flip(&mut self) {
87 self.start_active = !self.start_active;
88 }
89
90 pub fn active_cursor(&self) -> Cur {
91 if self.start_active {
92 self.start
93 } else {
94 self.end
95 }
96 }
97
98 pub fn set_active_cursor(&mut self, c: Cur) {
99 if c == self.start && c == self.end {
100 return;
101 }
102
103 if self.start_active {
104 if c >= self.end {
105 self.start = self.end;
106 self.end = c;
107 self.start_active = false;
108 } else {
109 self.start = c;
110 }
111 } else if c <= self.start {
112 self.end = self.start;
113 self.start = c;
114 self.start_active = true;
115 } else {
116 self.end = c;
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use simple_test_case::test_case;
125
126 fn c(idx: usize) -> Cur {
127 Cur { idx }
128 }
129
130 fn r(start: usize, end: usize, start_active: bool) -> Range {
131 Range {
132 start: c(start),
133 end: c(end),
134 start_active,
135 }
136 }
137
138 #[test_case(r(2, 2, true), c(0), r(0, 2, true); "null range start active new before")]
139 #[test_case(r(2, 2, true), c(2), r(2, 2, true); "null range start active new equal")]
140 #[test_case(r(2, 2, true), c(5), r(2, 5, false); "null range start active new after")]
141 #[test_case(r(2, 2, false), c(0), r(0, 2, true); "null range end active new before")]
142 #[test_case(r(2, 2, false), c(2), r(2, 2, false); "null range end active new equal")]
143 #[test_case(r(2, 2, false), c(5), r(2, 5, false); "null range end active new after")]
144 #[test_case(r(3, 5, true), c(1), r(1, 5, true); "start active new before")]
145 #[test_case(r(3, 5, true), c(3), r(3, 5, true); "start active new at start")]
146 #[test_case(r(3, 5, true), c(4), r(4, 5, true); "start active new in between")]
147 #[test_case(r(3, 5, true), c(5), r(5, 5, false); "start active new at end")]
148 #[test_case(r(3, 5, true), c(7), r(5, 7, false); "start active new after")]
149 #[test_case(r(3, 5, false), c(1), r(1, 3, true); "end active new before")]
150 #[test_case(r(3, 5, false), c(3), r(3, 3, true); "end active new at start")]
151 #[test_case(r(3, 5, false), c(4), r(3, 4, false); "end active new in between")]
152 #[test_case(r(3, 5, false), c(5), r(3, 5, false); "end active new at end")]
153 #[test_case(r(3, 5, false), c(7), r(3, 7, false); "end active new after")]
154 #[test]
155 fn set_active_cursor_works(mut rng: Range, cur: Cur, expected: Range) {
156 rng.set_active_cursor(cur);
157 assert_eq!(rng, expected);
158 }
159}