1use super::x::{XBackwards, XForwards};
2use super::{assert_ll_lines_equals, LLCursorAssignment, LLLine, Rc, XMatch};
3
4#[derive(Clone)]
28pub struct LLSelection {
29 pub(super) ll_line: Rc<LLLine>,
30 pub(super) start_idx: usize,
32 pub(super) end_idx: usize,
34}
35
36impl std::fmt::Debug for LLSelection {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 f.debug_struct("LLSelection")
39 .field("start_idx", &self.start_idx)
40 .field("end_idx", &self.end_idx)
41 .finish()
42 }
43}
44
45impl PartialEq for LLSelection {
46 fn eq(&self, other: &Self) -> bool {
47 self.start_idx == other.start_idx
48 && self.end_idx == other.end_idx
49 && Rc::ptr_eq(&self.ll_line, &other.ll_line)
50 }
51}
52
53impl LLSelection {
54 pub fn from_line(ll_line: Rc<LLLine>) -> Option<Self> {
56 let line_len = ll_line.ll_tokens.len();
57 if line_len > 0 {
58 Some(LLSelection {
59 ll_line,
60 end_idx: line_len - 1,
61 start_idx: 0,
62 })
63 } else {
64 None }
66 }
67
68 pub fn split_by<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Vec<LLSelection> {
69 let matches = self.find_by(matcher);
70
71 if matches.is_empty() {
72 return vec![self.clone()];
73 }
74
75 if matches.len() == 1 {
76 return matches[0]
77 .0
78 .start_idx
79 .checked_sub(1)
80 .and_then(|end_idx| self.selection_from(self.start_idx, end_idx))
81 .into_iter()
82 .chain(self.selection_from(matches[0].0.end_idx + 1, self.end_idx))
83 .collect();
84 }
85
86 let start_opt = matches[0]
87 .0
88 .start_idx
89 .checked_sub(1)
90 .and_then(|end_idx| self.selection_from(self.start_idx, end_idx));
91
92 let end_opt = self.selection_from(matches.last().unwrap().0.end_idx + 1, self.end_idx);
93 start_opt
94 .into_iter()
95 .chain(matches.windows(2).into_iter().filter_map(|m| {
96 m[1].0
97 .start_idx
98 .checked_sub(1)
99 .and_then(|end_idx| self.selection_from(m[0].0.end_idx + 1, end_idx))
100 }))
101 .chain(end_opt)
102 .collect()
103 }
104
105 pub fn find_by<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Vec<(LLSelection, M::Out)> {
106 (self.start_idx..=self.end_idx)
107 .map(|i| {
108 let forwards = XForwards { from_idx: i };
109
110 matcher
111 .go(&forwards, &self.ll_line)
112 .into_iter()
113 .map(move |(out, next_idx)| {
114 (
115 LLSelection {
116 start_idx: i,
117 end_idx: next_idx.0,
118 ll_line: self.ll_line.clone(),
119 },
120 out,
121 )
122 })
123 })
124 .flatten()
125 .collect()
126 }
127
128 pub fn find_first_by<'a, M: XMatch<'a>>(
129 &'a self,
130 matcher: &M,
131 ) -> Option<(LLSelection, M::Out)> {
132 self.find_by(matcher).into_iter().next()
133 }
134
135 pub fn find_by_forwards_and_backwards<'a, M: XMatch<'a>>(
136 &'a self,
137 matcher: &M,
138 ) -> Vec<(LLSelection, M::Out)> {
139 (self.start_idx..=self.end_idx)
140 .map(|i| {
141 let forwards = XForwards { from_idx: i };
142
143 matcher
144 .go(&forwards, &self.ll_line)
145 .into_iter()
146 .map(move |(out, next_idx)| {
147 (
148 LLSelection {
149 start_idx: i,
150 end_idx: next_idx.0,
151 ll_line: self.ll_line.clone(),
152 },
153 out,
154 )
155 })
156 .chain({
157 let backwards = XBackwards { from_idx: i };
158
159 matcher.go(&backwards, &self.ll_line).into_iter().map(
160 move |(out, next_idx)| {
161 (
162 LLSelection {
163 start_idx: next_idx.0,
164 end_idx: i,
165 ll_line: self.ll_line.clone(),
166 },
167 out,
168 )
169 },
170 )
171 })
172 })
173 .flatten()
174 .collect()
175 }
176
177 pub fn match_forwards<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Vec<(LLSelection, M::Out)> {
178 if self.end_idx + 1 == self.ll_line.ll_tokens.len() {
181 return Vec::new();
182 }
183
184 let forwards = XForwards {
185 from_idx: self.end_idx + 1,
186 };
187
188 matcher
189 .go(&forwards, &self.ll_line)
190 .into_iter()
191 .map(|(out, next_idx)| {
192 (
193 LLSelection {
194 start_idx: self.start_idx,
195 end_idx: next_idx.0,
196 ll_line: self.ll_line.clone(),
197 },
198 out,
199 )
200 })
201 .collect()
202 }
203
204 pub fn match_first_forwards<'a, M: XMatch<'a>>(
205 &'a self,
206 matcher: &M,
207 ) -> Option<(LLSelection, M::Out)> {
208 self.match_forwards(matcher).into_iter().next()
209 }
210
211 pub fn match_forwards_longest<'a, M: XMatch<'a>>(
212 &'a self,
213 _matcher: &M,
214 ) -> Option<(LLSelection, M::Out)> {
215 todo!()
216 }
217
218 pub fn match_forwards_shortest<'a, M: XMatch<'a>>(
219 &'a self,
220 _matcher: &M,
221 ) -> Option<(LLSelection, M::Out)> {
222 todo!()
223 }
224
225 pub fn match_backwards<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Vec<(LLSelection, M::Out)> {
226 if self.start_idx == 0 {
227 return Vec::new();
228 }
229
230 let backwards = XBackwards {
231 from_idx: self.start_idx - 1,
232 };
233
234 matcher
235 .go(&backwards, &self.ll_line)
236 .into_iter()
237 .map(|(out, next_idx)| {
238 (
239 LLSelection {
240 start_idx: next_idx.0,
241 end_idx: self.end_idx,
242 ll_line: self.ll_line.clone(),
243 },
244 out,
245 )
246 })
247 .collect()
248 }
249
250 pub fn match_first_backwards<'a, M: XMatch<'a>>(
251 &'a self,
252 matcher: &M,
253 ) -> Option<(LLSelection, M::Out)> {
254 self.match_backwards(matcher).into_iter().next()
255 }
256
257 pub fn after(&self) -> Option<LLSelection> {
258 let ll_line_end = self.ll_line.ll_tokens.len() - 1;
259
260 if self.start_idx + 1 == ll_line_end {
261 None
262 } else {
263 Some(LLSelection {
264 ll_line: self.ll_line.clone(),
265 start_idx: self.end_idx + 1,
266 end_idx: ll_line_end,
267 })
268 }
269 }
270
271 fn selection_from(&self, mut start_idx: usize, mut end_idx: usize) -> Option<LLSelection> {
272 start_idx = start_idx.max(self.start_idx);
273 end_idx = end_idx.min(self.end_idx);
274 if start_idx <= end_idx {
275 Some(LLSelection {
276 ll_line: self.ll_line.clone(),
277 end_idx,
278 start_idx,
279 })
280 } else {
281 None
282 }
283 }
284
285 pub fn split_with(&self, other_selection: &LLSelection) -> [Option<LLSelection>; 2] {
287 assert_ll_lines_equals(&self.ll_line, &other_selection.ll_line);
288
289 [
298 if other_selection.start_idx > 0 {
299 self.selection_from(self.start_idx, other_selection.start_idx - 1)
301 } else {
302 None
303 },
304 self.selection_from(other_selection.end_idx + 1, self.end_idx),
305 ]
306 }
307
308 fn trim_selection<'a, M: XMatch<'a>>(
310 &'a self,
311 matcher: &M,
312 from_start: bool,
313 from_end: bool,
314 ) -> Option<LLSelection> {
315 let mut new_start = self.start_idx;
316 let mut new_end = self.end_idx;
317
318 if from_start {
319 if let Some(first_match) = matcher
320 .go(
321 &XForwards {
322 from_idx: self.start_idx,
323 },
324 &self.ll_line,
325 )
326 .first()
327 {
328 new_start = (first_match.1).0 + 1;
329 }
330 }
331
332 if from_end {
333 if let Some(first_match) = matcher
334 .go(
335 &XBackwards {
336 from_idx: self.end_idx,
337 },
338 &self.ll_line,
339 )
340 .first()
341 {
342 new_end = (first_match.1)
343 .0
344 .checked_sub(1)
345 .unwrap_or((first_match.1).0);
346 }
347 }
348
349 if new_end >= self.start_idx && new_start <= new_end {
350 Some(LLSelection {
351 ll_line: self.ll_line.clone(),
352 start_idx: new_start,
353 end_idx: new_end,
354 })
355 } else {
356 None
357 }
358 }
359
360 pub fn trim_start<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Option<LLSelection> {
361 self.trim_selection(matcher, true, false)
362 }
363
364 pub fn trim_end<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Option<LLSelection> {
365 self.trim_selection(matcher, false, true)
366 }
367
368 pub fn trim<'a, M: XMatch<'a>>(&'a self, matcher: &M) -> Option<LLSelection> {
369 self.trim_selection(matcher, true, true)
370 }
371
372 pub fn finish_with_attr<Attr>(&self, value: Attr) -> LLCursorAssignment<Attr> {
373 LLCursorAssignment {
374 end_idx: self.end_idx,
375 start_idx: self.start_idx,
376 value,
377 }
378 }
379}