1use std::cmp::Ordering;
6
7use iced_widget::{graphics::text::Paragraph, text_input::Value};
8
9#[derive(Debug, Default, Clone, Copy, PartialEq)]
11#[allow(missing_docs)]
12pub enum Direction {
13 Left,
14 #[default]
15 Right,
16}
17
18#[derive(Debug, Default, Clone, Copy, PartialEq)]
20pub struct Selection {
21 pub start: SelectionEnd,
23 pub end: SelectionEnd,
25 pub direction: Direction,
27 moving_line_index: Option<usize>,
28}
29
30#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
36#[allow(missing_docs)]
37pub struct SelectionEnd {
38 pub line: usize,
39 pub index: usize,
40}
41
42impl SelectionEnd {
43 pub fn new(line: usize, index: usize) -> Self {
45 Self { line, index }
46 }
47}
48
49impl PartialOrd for SelectionEnd {
50 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
51 Some(self.cmp(other))
52 }
53}
54
55impl Ord for SelectionEnd {
56 fn cmp(&self, other: &Self) -> Ordering {
57 self.line
58 .cmp(&other.line)
59 .then(self.index.cmp(&other.index))
60 }
61}
62
63impl Selection {
64 pub fn new() -> Self {
66 Self::default()
67 }
68
69 pub fn is_empty(&self) -> bool {
71 self.start == self.end
72 }
73
74 pub fn text(&self, paragraph: &Paragraph) -> String {
78 let Selection { start, end, .. } = *self;
79
80 let mut value = String::new();
81 let buffer_lines = ¶graph.buffer().lines;
82 let lines_total = end.line - start.line + 1;
83
84 for (idx, line) in buffer_lines
85 .iter()
86 .skip(start.line)
87 .enumerate()
88 .take(lines_total)
89 {
90 let text = Value::new(line.text());
91 let length = text.len();
92
93 if idx == 0 {
94 if lines_total == 1 {
95 value.push_str(
96 &text
97 .select(
98 start.index.min(length),
99 end.index.min(length),
100 )
101 .to_string(),
102 );
103 } else {
104 value.push_str(
105 &text
106 .select(start.index.min(length), length)
107 .to_string(),
108 );
109 value.push_str(line.ending().as_str());
110 }
111 } else if idx == lines_total - 1 {
112 value.push_str(&text.until(end.index.min(length)).to_string());
113 } else {
114 value.push_str(&text.to_string());
115 value.push_str(line.ending().as_str());
116 }
117 }
118
119 value
120 }
121
122 pub fn active_end(&self) -> SelectionEnd {
128 if self.direction == Direction::Right {
129 self.end
130 } else {
131 self.start
132 }
133 }
134
135 pub fn select_range(&mut self, start: SelectionEnd, end: SelectionEnd) {
155 self.start = start.min(end);
156 self.end = end.max(start);
157 }
158
159 pub fn change_selection(&mut self, new_end: SelectionEnd) {
198 let (start, end) = if self.direction == Direction::Right {
199 if new_end < self.start {
200 self.direction = Direction::Left;
201 }
202
203 (self.start, new_end)
204 } else {
205 if new_end > self.end {
206 self.direction = Direction::Right;
207 }
208
209 (new_end, self.end)
210 };
211
212 self.moving_line_index = None;
213 self.select_range(start, end);
214 }
215
216 pub fn change_selection_by_word(
219 &mut self,
220 new_end: SelectionEnd,
221 paragraph: &Paragraph,
222 ) {
223 let (base_word_start, base_word_end) = {
224 if self.direction == Direction::Right {
225 let value = Value::new(
226 paragraph.buffer().lines[self.start.line].text(),
227 );
228
229 let end = SelectionEnd::new(
230 self.start.line,
231 value.next_end_of_word(self.start.index),
232 );
233
234 (self.start, end)
235 } else {
236 let value =
237 Value::new(paragraph.buffer().lines[self.end.line].text());
238
239 let start = SelectionEnd::new(
240 self.end.line,
241 value.previous_start_of_word(self.end.index),
242 );
243
244 (start, self.end)
245 }
246 };
247
248 let value = Value::new(paragraph.buffer().lines[new_end.line].text());
249
250 let (start, end) = if new_end < self.start {
251 self.direction = Direction::Left;
252
253 let start = SelectionEnd::new(
254 new_end.line,
255 value.previous_start_of_word(new_end.index),
256 );
257
258 (start, base_word_end)
259 } else if new_end > self.end {
260 self.direction = Direction::Right;
261
262 let end = SelectionEnd::new(
263 new_end.line,
264 value.next_end_of_word(new_end.index),
265 );
266
267 (base_word_start, end)
268 } else if self.direction == Direction::Right {
269 let end = SelectionEnd::new(
270 new_end.line,
271 value.next_end_of_word(new_end.index),
272 );
273
274 (base_word_start, end.max(base_word_end))
275 } else {
276 let start = SelectionEnd::new(
277 new_end.line,
278 value.previous_start_of_word(new_end.index),
279 );
280
281 (start.min(base_word_start), base_word_end)
282 };
283
284 self.moving_line_index = None;
285 self.select_range(start, end);
286 }
287
288 pub fn change_selection_by_line(
291 &mut self,
292 new_line: usize,
293 paragraph: &Paragraph,
294 ) {
295 if self.active_end().line == new_line {
296 return;
297 }
298
299 let old_direction = self.direction;
300
301 if new_line < self.start.line {
302 self.direction = Direction::Left;
303 } else if new_line > self.end.line {
304 self.direction = Direction::Right;
305 }
306
307 let (start, end) = if self.direction == Direction::Right {
308 let value = Value::new(paragraph.buffer().lines[new_line].text());
309
310 let start = if self.direction == old_direction {
311 self.start
312 } else {
313 SelectionEnd::new(self.end.line, 0)
314 };
315
316 let end = SelectionEnd::new(new_line, value.len());
317
318 (start, end)
319 } else {
320 let start = SelectionEnd::new(new_line, 0);
321
322 let end = if self.direction == old_direction {
323 self.end
324 } else {
325 let value = Value::new(
326 paragraph.buffer().lines[self.start.line].text(),
327 );
328
329 SelectionEnd::new(self.start.line, value.len())
330 };
331
332 (start, end)
333 };
334
335 self.moving_line_index = None;
336 self.select_range(start, end);
337 }
338
339 pub fn select_word(
341 &mut self,
342 line: usize,
343 index: usize,
344 paragraph: &Paragraph,
345 ) {
346 let value = Value::new(paragraph.buffer().lines[line].text());
347
348 let start =
349 SelectionEnd::new(line, value.previous_start_of_word(index));
350 let end = SelectionEnd::new(line, value.next_end_of_word(index));
351
352 self.select_range(start, end);
353 }
354
355 pub fn select_left(&mut self, paragraph: &Paragraph) {
358 let mut active_end = self.active_end();
359
360 if active_end.index > 0 {
361 active_end.index -= 1;
362
363 self.change_selection(active_end);
364 } else if active_end.line > 0 {
365 active_end.line -= 1;
366
367 let value =
368 Value::new(paragraph.buffer().lines[active_end.line].text());
369 active_end.index = value.len();
370
371 self.change_selection(active_end);
372 }
373 }
374
375 pub fn select_right(&mut self, paragraph: &Paragraph) {
378 let mut active_end = self.active_end();
379
380 let lines = ¶graph.buffer().lines;
381 let value = Value::new(lines[active_end.line].text());
382
383 if active_end.index < value.len() {
384 active_end.index += 1;
385
386 self.change_selection(active_end);
387 } else if active_end.line < lines.len() - 1 {
388 active_end.line += 1;
389 active_end.index = 0;
390
391 self.change_selection(active_end);
392 }
393 }
394
395 pub fn select_up(&mut self, paragraph: &Paragraph) {
397 let mut active_end = self.active_end();
398
399 if active_end.line == 0 {
400 active_end.index = 0;
401
402 self.change_selection(active_end);
403 } else {
404 active_end.line -= 1;
405
406 let mut moving_line_index = None;
407
408 if let Some(index) = self.moving_line_index.take() {
409 active_end.index = index;
410 }
411
412 let value =
413 Value::new(paragraph.buffer().lines[active_end.line].text());
414 if active_end.index > value.len() {
415 moving_line_index = Some(active_end.index);
416 active_end.index = value.len();
417 }
418
419 self.change_selection(active_end);
420 self.moving_line_index = moving_line_index;
421 }
422 }
423
424 pub fn select_down(&mut self, paragraph: &Paragraph) {
426 let mut active_end = self.active_end();
427
428 let lines = ¶graph.buffer().lines;
429 let value = Value::new(lines[active_end.line].text());
430
431 if active_end.line == lines.len() - 1 {
432 active_end.index = value.len();
433
434 self.change_selection(active_end);
435 } else {
436 active_end.line += 1;
437
438 let mut moving_line_index = None;
439
440 if let Some(index) = self.moving_line_index.take() {
441 active_end.index = index;
442 }
443
444 let value =
445 Value::new(paragraph.buffer().lines[active_end.line].text());
446 if active_end.index > value.len() {
447 moving_line_index = Some(active_end.index);
448 active_end.index = value.len();
449 }
450
451 self.change_selection(active_end);
452 self.moving_line_index = moving_line_index;
453 }
454 }
455
456 pub fn select_left_by_words(&mut self, paragraph: &Paragraph) {
459 let mut active_end = self.active_end();
460
461 if active_end.index == 1 {
462 active_end.index = 0;
463
464 self.change_selection(active_end);
465 } else if active_end.index > 1 {
466 let value =
467 Value::new(paragraph.buffer().lines[active_end.line].text());
468 active_end.index = value.previous_start_of_word(active_end.index);
469
470 self.change_selection(active_end);
471 } else if active_end.line > 0 {
472 active_end.line -= 1;
473
474 let value =
475 Value::new(paragraph.buffer().lines[active_end.line].text());
476 active_end.index = value.previous_start_of_word(value.len());
477
478 self.change_selection(active_end);
479 }
480 }
481
482 pub fn select_right_by_words(&mut self, paragraph: &Paragraph) {
485 let mut active_end = self.active_end();
486
487 let lines = ¶graph.buffer().lines;
488 let value = Value::new(lines[active_end.line].text());
489
490 if value.len() - active_end.index == 1 {
491 active_end.index = value.len();
492
493 self.change_selection(active_end);
494 } else if active_end.index < value.len() {
495 active_end.index = value.next_end_of_word(active_end.index);
496
497 self.change_selection(active_end);
498 } else if active_end.line < lines.len() - 1 {
499 active_end.line += 1;
500
501 let value = Value::new(lines[active_end.line].text());
502 active_end.index = value.next_end_of_word(0);
503
504 self.change_selection(active_end);
505 }
506 }
507
508 pub fn select_line_beginning(&mut self) {
510 let mut active_end = self.active_end();
511
512 if active_end.index > 0 {
513 active_end.index = 0;
514
515 self.change_selection(active_end);
516 }
517 }
518
519 pub fn select_line_end(&mut self, paragraph: &Paragraph) {
521 let mut active_end = self.active_end();
522
523 let value =
524 Value::new(paragraph.buffer().lines[active_end.line].text());
525
526 if active_end.index < value.len() {
527 active_end.index = value.len();
528
529 self.change_selection(active_end);
530 }
531 }
532
533 pub fn select_beginning(&mut self) {
537 self.change_selection(SelectionEnd::new(0, 0));
538 }
539
540 pub fn select_end(&mut self, paragraph: &Paragraph) {
544 let lines = ¶graph.buffer().lines;
545 let value = Value::new(lines[lines.len() - 1].text());
546
547 let new_end = SelectionEnd::new(lines.len() - 1, value.len());
548
549 self.change_selection(new_end);
550 }
551
552 pub fn select_line(&mut self, line: usize, paragraph: &Paragraph) {
554 let value = Value::new(paragraph.buffer().lines[line].text());
555
556 let start = SelectionEnd::new(line, 0);
557 let end = SelectionEnd::new(line, value.len());
558
559 self.select_range(start, end);
560 }
561
562 pub fn select_all(&mut self, paragraph: &Paragraph) {
566 let line = paragraph.buffer().lines.len() - 1;
567 let index = Value::new(paragraph.buffer().lines[line].text()).len();
568
569 let end = SelectionEnd::new(line, index);
570
571 self.select_range(SelectionEnd::new(0, 0), end);
572 }
573}