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