1use crate::clipboard;
3use crate::input_method;
4use crate::keyboard;
5use crate::keyboard::key;
6use crate::mouse;
7use crate::renderer;
8use crate::text::highlighter;
9use crate::text::{self, Alignment, LineHeight, Position, Wrapping};
10use crate::time::{Duration, Instant};
11use crate::touch;
12use crate::widget::operation::{Focusable, TextInput};
13use crate::window;
14use crate::{
15 Color, Event, Font, InputMethod, Padding, Pixels, Point, Rectangle, Size, SmolStr, Vector,
16};
17
18use std::borrow::Cow;
19use std::sync::Arc;
20
21pub trait Editor: Sized + Default {
23 fn with_text(text: &str) -> Self;
25
26 fn is_empty(&self) -> bool;
28
29 fn cursor(&self) -> Cursor;
31
32 fn selection(&self) -> Option<Selection>;
34
35 fn copy(&self) -> Option<String>;
37
38 fn line(&self, index: usize) -> Option<Line<'_>>;
40
41 fn line_count(&self) -> usize;
43
44 fn perform(&mut self, action: Action);
46
47 fn move_to(&mut self, cursor: Cursor);
49
50 fn bounds(&self) -> Size;
52
53 fn min_bounds(&self) -> Size;
56
57 fn hint_factor(&self) -> Option<f32>;
59
60 fn update(
62 &mut self,
63 new_bounds: Size,
64 new_font: Font,
65 new_size: Pixels,
66 new_line_height: LineHeight,
67 new_wrapping: Wrapping,
68 new_alignment: Alignment,
69 new_hint_factor: Option<f32>,
70 new_parser: &mut impl text::Parser,
71 );
72
73 fn overwrite(&mut self, new_text: &str);
75
76 fn highlight<P: text::Parser>(
78 &mut self,
79 font: Font,
80 parser: &mut P,
81 highlight: impl Fn(P::Output) -> highlighter::Style,
82 );
83
84 fn lines(&self) -> impl Iterator<Item = Line<'_>> {
86 (0..)
87 .map(|i| self.line(i))
88 .take_while(Option::is_some)
89 .flatten()
90 }
91
92 fn text(&self) -> String {
94 let mut contents = String::new();
95 let mut lines = self.lines().peekable();
96
97 while let Some(line) = lines.next() {
98 contents.push_str(&line.text);
99
100 if lines.peek().is_some() {
101 contents.push_str(if line.ending == LineEnding::None {
102 LineEnding::default().as_str()
103 } else {
104 line.ending.as_str()
105 });
106 }
107 }
108
109 contents
110 }
111
112 fn font(&self) -> Font;
114
115 fn text_size(&self) -> Pixels;
117
118 fn line_height(&self) -> LineHeight;
120}
121
122#[derive(Debug, Clone, PartialEq)]
124pub enum Action {
125 Move(Motion),
127 Select(Motion),
129 SelectWord,
131 SelectLine,
133 SelectAll,
135 Edit(Edit),
137 Click(Point, mouse::click::Kind),
139 Drag(Point),
141 Scroll {
143 lines: i32,
145 },
146}
147
148impl Action {
149 pub fn is_edit(&self) -> bool {
151 matches!(self, Self::Edit(_))
152 }
153}
154
155#[derive(Debug, Clone, PartialEq)]
157pub enum Edit {
158 Insert(char),
160 Paste(Arc<String>),
162 Enter,
164 Indent,
166 Unindent,
168 Backspace,
170 BackspaceWord,
172 BackspaceLine,
174 Delete,
176 DeleteWord,
178 DeleteLine,
180 Undo,
182 Redo,
184}
185
186#[derive(Debug, Clone, Copy, PartialEq)]
188pub enum Motion {
189 Left,
191 Right,
193 Up,
195 Down,
197 WordLeft,
199 WordRight,
201 Home,
203 End,
205 PageUp,
207 PageDown,
209 DocumentStart,
211 DocumentEnd,
213}
214
215impl Motion {
216 pub fn widen(self) -> Self {
218 match self {
219 Self::Left => Self::WordLeft,
220 Self::Right => Self::WordRight,
221 Self::Home => Self::DocumentStart,
222 Self::End => Self::DocumentEnd,
223 _ => self,
224 }
225 }
226
227 pub fn direction(&self) -> Direction {
229 match self {
230 Self::Left
231 | Self::Up
232 | Self::WordLeft
233 | Self::Home
234 | Self::PageUp
235 | Self::DocumentStart => Direction::Left,
236 Self::Right
237 | Self::Down
238 | Self::WordRight
239 | Self::End
240 | Self::PageDown
241 | Self::DocumentEnd => Direction::Right,
242 }
243 }
244}
245
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub enum Direction {
249 Left,
251 Right,
253}
254
255#[derive(Debug, Clone)]
257pub enum Selection {
258 Caret(Point),
260
261 Range(Arc<[Rectangle]>),
263}
264
265#[derive(Debug, Clone, Copy, PartialEq)]
267pub struct Cursor {
268 pub position: Position,
270
271 pub selection: Option<Position>,
273}
274
275#[derive(Clone, Debug, Default, Eq, PartialEq)]
277pub struct Line<'a> {
278 pub text: Cow<'a, str>,
280 pub ending: LineEnding,
282}
283
284#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
286pub enum LineEnding {
287 #[default]
289 Lf,
290 CrLf,
292 Cr,
294 LfCr,
296 None,
298}
299
300impl LineEnding {
301 pub fn as_str(self) -> &'static str {
303 match self {
304 Self::Lf => "\n",
305 Self::CrLf => "\r\n",
306 Self::Cr => "\r",
307 Self::LfCr => "\n\r",
308 Self::None => "",
309 }
310 }
311}
312
313#[derive(Debug, Clone, Default)]
315pub struct State {
316 focus: Option<Focus>,
317 preedit: Option<input_method::Preedit>,
318 last_click: Option<mouse::Click>,
319 is_dragging: bool,
320 partial_scroll: f32,
321}
322
323impl State {
324 pub fn new() -> Self {
326 Self::default()
327 }
328
329 pub fn update<Message>(
331 &mut self,
332 editor: &impl Editor,
333 event: &Event,
334 bounds: Rectangle,
335 padding: Padding,
336 cursor: mouse::Cursor,
337 key_binding: impl Fn(KeyPress) -> Option<Binding<Message>>,
338 ) -> Option<Update<Message>> {
339 match event {
340 Event::Window(window::Event::Unfocused) => {
341 if let Some(focus) = &mut self.focus {
342 focus.is_window_focused = false;
343 }
344
345 None
346 }
347 Event::Window(window::Event::Focused) => {
348 if let Some(focus) = &mut self.focus {
349 focus.is_window_focused = true;
350 focus.updated_at = Instant::now();
351 }
352
353 Some(Update::Focus)
354 }
355 Event::Window(window::Event::RedrawRequested(now)) => {
356 let focus = self.focus.as_mut()?;
357
358 if !focus.is_window_focused {
359 return None;
360 }
361
362 focus.now = *now;
363
364 let millis_until_redraw = Focus::CURSOR_BLINK_INTERVAL_MILLIS
365 - (focus.now - focus.updated_at).as_millis()
366 % Focus::CURSOR_BLINK_INTERVAL_MILLIS;
367
368 Some(Update::RedrawAt(
369 focus.now + Duration::from_millis(millis_until_redraw as u64),
370 ))
371 }
372 Event::Clipboard(clipboard::Event::Read(Ok(content))) => {
373 let focus = self.focus.as_ref()?;
374
375 if !focus.is_window_focused {
376 return None;
377 }
378
379 let clipboard::Content::Text(text) = content.as_ref() else {
380 return None;
381 };
382
383 Some(Update::Action(Action::Edit(Edit::Paste(Arc::new(
384 text.clone(),
385 )))))
386 }
387 Event::Mouse(event) => match event {
388 mouse::Event::ButtonPressed(mouse::Button::Left) => {
389 if let Some(cursor_position) = cursor.position_in(bounds) {
390 let cursor_position =
391 cursor_position - Vector::new(padding.left, padding.top);
392
393 let click = mouse::Click::new(
394 cursor_position,
395 mouse::Button::Left,
396 self.last_click,
397 );
398
399 self.focus = Some(Focus::now());
400 self.last_click = Some(click);
401 self.is_dragging = true;
402
403 Some(Update::Action(Action::Click(
404 click.position(),
405 click.kind(),
406 )))
407 } else if self.focus.is_some() {
408 self.focus = None;
409
410 Some(Update::Unfocus)
411 } else {
412 None
413 }
414 }
415 mouse::Event::ButtonReleased(mouse::Button::Left) => {
416 self.is_dragging = false;
417
418 Some(Update::Release)
419 }
420 mouse::Event::CursorMoved { .. } if self.is_dragging => {
421 let position = cursor.position_from(bounds.position())?
422 - Vector::new(padding.left, padding.top);
423
424 Some(Update::Action(Action::Drag(position)))
425 }
426 mouse::Event::WheelScrolled { delta } if cursor.is_over(bounds) => {
427 let bounds = editor.bounds();
428
429 if bounds.height >= i32::MAX as f32 {
430 return None;
431 }
432
433 let lines = match delta {
434 mouse::ScrollDelta::Lines { y, .. } => {
435 if y.abs() > 0.0 {
436 y.signum() * -(y.abs() * 4.0).max(1.0)
437 } else {
438 0.0
439 }
440 }
441 mouse::ScrollDelta::Pixels { y, .. } => -y / 4.0,
442 };
443
444 let lines = lines + self.partial_scroll;
445 self.partial_scroll = lines.fract();
446
447 Some(Update::Action(Action::Scroll {
448 lines: lines as i32,
449 }))
450 }
451 _ => None,
452 },
453 Event::Touch(event) => match event {
454 touch::Event::FingerPressed { .. } => {
455 if let Some(cursor_position) = cursor.position_in(bounds) {
456 let cursor_position =
457 cursor_position - Vector::new(padding.left, padding.top);
458
459 let click = mouse::Click::new(
460 cursor_position,
461 mouse::Button::Left,
462 self.last_click,
463 );
464
465 self.focus = Some(Focus::now());
466 self.last_click = Some(click);
467 self.is_dragging = true;
468
469 Some(Update::Action(Action::Click(
470 click.position(),
471 click.kind(),
472 )))
473 } else if self.focus.is_some() {
474 self.focus = None;
475
476 Some(Update::Unfocus)
477 } else {
478 None
479 }
480 }
481 touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. } => {
482 self.is_dragging = false;
483
484 Some(Update::Release)
485 }
486 touch::Event::FingerMoved { .. } if self.is_dragging => {
487 let position =
488 cursor.position_in(bounds)? - Vector::new(padding.left, padding.top);
489
490 Some(Update::Action(Action::Drag(position)))
491 }
492 touch::Event::FingerMoved { .. } => None,
493 },
494 Event::InputMethod(event) => match event {
495 input_method::Event::Opened | input_method::Event::Closed => {
496 let is_open = matches!(event, input_method::Event::Opened);
497 self.preedit = is_open.then(input_method::Preedit::new);
498
499 Some(Update::InputMethod)
500 }
501 input_method::Event::Preedit(content, selection) if self.focus.is_some() => {
502 self.preedit = Some(input_method::Preedit {
503 content: content.clone(),
504 selection: selection.clone(),
505 text_size: Some(editor.text_size()),
506 });
507
508 Some(Update::InputMethod)
509 }
510 input_method::Event::Commit(content) if self.focus.is_some() => Some(
511 Update::Action(Action::Edit(Edit::Paste(Arc::new(content.clone())))),
512 ),
513 _ => None,
514 },
515 Event::Keyboard(keyboard::Event::KeyPressed {
516 key,
517 modified_key,
518 physical_key,
519 modifiers,
520 text,
521 ..
522 }) => {
523 let key_press = KeyPress {
524 key: key.clone(),
525 modified_key: modified_key.clone(),
526 physical_key: *physical_key,
527 modifiers: *modifiers,
528 text: text.clone(),
529 is_focused: self.is_focused(),
530 };
531
532 fn apply_binding<Message>(
533 binding: Binding<Message>,
534 editor: &impl Editor,
535 state: &mut State,
536 ) -> Option<Update<Message>> {
537 let action = |action| Update::Action(action);
538 let edit = |edit| action(Action::Edit(edit));
539
540 match binding {
541 Binding::Unfocus => {
542 state.focus = None;
543 state.is_dragging = false;
544
545 None
546 }
547 Binding::Copy => {
548 let selection = editor.copy()?;
549
550 Some(Update::Copy(selection))
551 }
552 Binding::Cut => {
553 let selection = editor.copy()?;
554
555 Some(Update::Sequence(vec![
556 Update::Copy(selection),
557 edit(Edit::Backspace),
558 ]))
559 }
560 Binding::Paste => Some(Update::Paste),
561 Binding::Undo => Some(edit(Edit::Undo)),
562 Binding::Redo => Some(edit(Edit::Redo)),
563 Binding::Move(motion) => Some(action(Action::Move(motion))),
564 Binding::Select(motion) => Some(action(Action::Select(motion))),
565 Binding::SelectWord => Some(action(Action::SelectWord)),
566 Binding::SelectLine => Some(action(Action::SelectLine)),
567 Binding::SelectAll => Some(action(Action::SelectAll)),
568 Binding::Insert(c) => Some(edit(Edit::Insert(c))),
569 Binding::Enter => Some(edit(Edit::Enter)),
570 Binding::Backspace => Some(edit(Edit::Backspace)),
571 Binding::BackspaceWord => Some(edit(Edit::BackspaceWord)),
572 Binding::BackspaceLine => Some(edit(Edit::BackspaceLine)),
573 Binding::Delete => Some(action(Action::Edit(Edit::Delete))),
574 Binding::DeleteWord => Some(edit(Edit::DeleteWord)),
575 Binding::DeleteLine => Some(edit(Edit::DeleteLine)),
576 Binding::Sequence(sequence) => {
577 let updates: Vec<_> = sequence
578 .into_iter()
579 .flat_map(|binding| apply_binding(binding, editor, state))
580 .collect();
581
582 if updates.is_empty() {
583 return None;
584 }
585
586 Some(Update::Sequence(updates))
587 }
588 Binding::Custom(message) => Some(Update::Custom(message)),
589 }
590 }
591
592 let update = apply_binding(key_binding(key_press)?, editor, self);
593
594 if let Some(focus) = &mut self.focus {
595 focus.updated_at = Instant::now();
596 }
597
598 update
599 }
600 _ => None,
601 }
602 }
603
604 pub fn input_method<'a>(
606 &'a self,
607 editor: &impl Editor,
608 position: Point,
609 ) -> InputMethod<&'a str> {
610 let Some(Focus {
611 is_window_focused: true,
612 ..
613 }) = &self.focus
614 else {
615 return InputMethod::Disabled;
616 };
617
618 let translation = position - Point::ORIGIN;
619
620 let Some(selection) = editor.selection() else {
621 return InputMethod::Disabled;
622 };
623
624 let cursor = match selection {
625 Selection::Caret(position) => position,
626 Selection::Range(ranges) => ranges.first().cloned().unwrap_or_default().position(),
627 };
628
629 let line_height = editor.line_height().to_absolute(editor.text_size());
630
631 let position = cursor + translation;
632
633 InputMethod::Enabled {
634 cursor: Rectangle::new(position, Size::new(1.0, f32::from(line_height))),
635 purpose: input_method::Purpose::Normal,
636 preedit: self.preedit.as_ref().map(input_method::Preedit::as_ref),
637 }
638 }
639
640 pub fn draw<Renderer: text::Renderer>(
642 &self,
643 editor: &Renderer::Editor,
644 renderer: &mut Renderer,
645 position: Point,
646 clip_bounds: Rectangle,
647 style: Style,
648 ) {
649 let bounds = Rectangle::new(position, editor.bounds());
650
651 let Some(clip_bounds) = clip_bounds.intersection(&bounds) else {
652 return;
653 };
654
655 if !editor.is_empty() {
656 renderer.fill_editor(editor, position, style.value, clip_bounds);
657 }
658
659 if !self.is_focused() {
660 return;
661 }
662
663 let translation = position - Point::ORIGIN;
664 let text_size = editor.text_size();
665 let line_height = editor.line_height();
666
667 let Some(selection) = editor.selection() else {
668 return;
669 };
670
671 match selection {
672 Selection::Caret(position) if self.is_cursor_visible() => {
673 let cursor = Rectangle::new(
674 position + translation,
675 Size::new(
676 if renderer::CRISP {
677 (1.0 / renderer.hint_factor().unwrap_or(1.0)).max(1.0)
678 } else {
679 1.0
680 },
681 line_height.to_absolute(text_size).into(),
682 ),
683 );
684
685 if let Some(clipped_cursor) = clip_bounds.intersection(&cursor) {
686 renderer.fill_quad(
687 renderer::Quad {
688 bounds: clipped_cursor,
689 ..renderer::Quad::default()
690 },
691 style.value,
692 );
693 }
694 }
695 Selection::Range(ranges) => {
696 for range in ranges
697 .iter()
698 .filter_map(|range| clip_bounds.intersection(&(*range + translation)))
699 {
700 renderer.fill_quad(
701 renderer::Quad {
702 bounds: range.round(),
703 ..renderer::Quad::default()
704 },
705 style.selection,
706 );
707 }
708 }
709 Selection::Caret(_) => {
710 renderer.fill_quad(renderer::Quad::default(), Color::TRANSPARENT);
712 }
713 }
714 }
715
716 pub fn is_cursor_visible(&self) -> bool {
718 self.focus.as_ref().is_some_and(Focus::is_cursor_visible)
719 }
720}
721
722pub struct Style {
724 pub value: Color,
726
727 pub selection: Color,
729}
730
731#[derive(Debug, Clone)]
732struct Focus {
733 updated_at: Instant,
734 now: Instant,
735 is_window_focused: bool,
736}
737
738impl Focus {
739 const CURSOR_BLINK_INTERVAL_MILLIS: u128 = 500;
740
741 fn now() -> Self {
742 let now = Instant::now();
743
744 Self {
745 updated_at: now,
746 now,
747 is_window_focused: true,
748 }
749 }
750
751 fn is_cursor_visible(&self) -> bool {
752 self.is_window_focused
753 && ((self.now - self.updated_at).as_millis() / Self::CURSOR_BLINK_INTERVAL_MILLIS)
754 .is_multiple_of(2)
755 }
756}
757
758impl State {
759 pub fn is_focused(&self) -> bool {
761 self.focus.is_some()
762 }
763}
764
765impl Focusable for State {
766 fn is_focused(&self) -> bool {
767 self.focus.is_some()
768 }
769
770 fn focus(&mut self) {
771 self.focus = Some(Focus::now());
772 }
773
774 fn unfocus(&mut self) {
775 self.focus = None;
776 }
777}
778
779#[derive(Debug, Clone, PartialEq)]
781pub enum Binding<Message> {
782 Unfocus,
784 Copy,
786 Cut,
788 Paste,
790 Undo,
792 Redo,
794 Move(Motion),
796 Select(Motion),
798 SelectWord,
800 SelectLine,
802 SelectAll,
804 Insert(char),
806 Enter,
808 Backspace,
810 BackspaceWord,
812 BackspaceLine,
814 Delete,
816 DeleteWord,
818 DeleteLine,
820 Sequence(Vec<Self>),
822 Custom(Message),
824}
825
826#[derive(Debug, Clone, PartialEq, Eq)]
828pub struct KeyPress {
829 pub key: keyboard::Key,
833 pub modified_key: keyboard::Key,
837 pub physical_key: keyboard::key::Physical,
841 pub modifiers: keyboard::Modifiers,
843 pub text: Option<SmolStr>,
845 pub is_focused: bool,
847}
848
849impl<Message> Binding<Message> {
850 pub fn from_key_press(event: KeyPress) -> Option<Self> {
852 let KeyPress {
853 key,
854 modified_key,
855 physical_key,
856 modifiers,
857 text,
858 is_focused,
859 } = event;
860
861 if !is_focused {
862 return None;
863 }
864
865 let combination = match key.to_latin(physical_key) {
866 Some('c') if modifiers.command() => Some(Self::Copy),
867 Some('x') if modifiers.command() => Some(Self::Cut),
868 Some('v') if modifiers.command() && !modifiers.alt() => Some(Self::Paste),
869 Some('a') if modifiers.command() => Some(Self::SelectAll),
870 Some('z') if modifiers.command() => Some(Self::Undo),
871 Some('y') if modifiers.command() => Some(Self::Redo),
872 _ => None,
873 };
874
875 if let Some(binding) = combination {
876 return Some(binding);
877 }
878
879 #[cfg(target_os = "macos")]
880 let modified_key = convert_macos_shortcut(&key, modifiers).unwrap_or(modified_key);
881
882 match modified_key.as_ref() {
883 keyboard::Key::Named(key::Named::Enter) => Some(Self::Enter),
884 keyboard::Key::Named(key::Named::Backspace) => Some(
885 if modifiers.macos_command() || (modifiers.command() && modifiers.shift()) {
886 Self::BackspaceLine
887 } else if modifiers.jump() {
888 Self::BackspaceWord
889 } else {
890 Self::Backspace
891 },
892 ),
893 keyboard::Key::Named(key::Named::Delete)
894 if text.is_none() || text.as_deref() == Some("\u{7f}") =>
895 {
896 Some(
897 if modifiers.macos_command() || (modifiers.command() && modifiers.shift()) {
898 Self::DeleteLine
899 } else if modifiers.jump() {
900 Self::DeleteWord
901 } else {
902 Self::Delete
903 },
904 )
905 }
906 keyboard::Key::Named(key::Named::Escape) => Some(Self::Unfocus),
907 _ => {
908 if let Some(text) = text {
909 let c = text.chars().find(|c| !c.is_control())?;
910
911 Some(Self::Insert(c))
912 } else if let keyboard::Key::Named(named_key) = key.as_ref() {
913 let motion = motion(named_key)?;
914
915 let motion = if modifiers.macos_command() {
916 match motion {
917 Motion::Left => Motion::Home,
918 Motion::Right => Motion::End,
919 _ => motion,
920 }
921 } else {
922 motion
923 };
924
925 let motion = if modifiers.jump() {
926 motion.widen()
927 } else {
928 motion
929 };
930
931 Some(if modifiers.shift() {
932 Self::Select(motion)
933 } else {
934 Self::Move(motion)
935 })
936 } else {
937 None
938 }
939 }
940 }
941 }
942}
943
944pub enum Update<Message> {
946 Action(Action),
948 Focus,
950 Unfocus,
952 InputMethod,
954 Release,
956 Copy(String),
958 Paste,
960 RedrawAt(Instant),
962 Custom(Message),
964 Sequence(Vec<Update<Message>>),
966}
967
968fn motion(key: key::Named) -> Option<Motion> {
969 match key {
970 key::Named::ArrowLeft => Some(Motion::Left),
971 key::Named::ArrowRight => Some(Motion::Right),
972 key::Named::ArrowUp => Some(Motion::Up),
973 key::Named::ArrowDown => Some(Motion::Down),
974 key::Named::Home => Some(Motion::Home),
975 key::Named::End => Some(Motion::End),
976 key::Named::PageUp => Some(Motion::PageUp),
977 key::Named::PageDown => Some(Motion::PageDown),
978 _ => None,
979 }
980}
981
982#[cfg(target_os = "macos")]
983fn convert_macos_shortcut(
984 key: &keyboard::Key,
985 modifiers: keyboard::Modifiers,
986) -> Option<keyboard::Key> {
987 if modifiers != keyboard::Modifiers::CTRL {
988 return None;
989 }
990
991 let key = match key.as_ref() {
992 keyboard::Key::Character("b") => key::Named::ArrowLeft,
993 keyboard::Key::Character("f") => key::Named::ArrowRight,
994 keyboard::Key::Character("a") => key::Named::Home,
995 keyboard::Key::Character("e") => key::Named::End,
996 keyboard::Key::Character("h") => key::Named::Backspace,
997 keyboard::Key::Character("d") => key::Named::Delete,
998 _ => return None,
999 };
1000
1001 Some(keyboard::Key::Named(key))
1002}
1003
1004impl<T: Editor> TextInput for T {
1005 fn text(&self) -> text::Fragment<'_> {
1006 text::Fragment::Owned(Editor::text(self))
1007 }
1008
1009 fn move_cursor_to_front(&mut self) {
1010 self.perform(Action::Move(Motion::DocumentStart));
1011 }
1012
1013 fn move_cursor_to_end(&mut self) {
1014 self.perform(Action::Move(Motion::DocumentEnd));
1015 }
1016
1017 fn move_cursor_to(&mut self, position: text::Position) {
1018 self.move_to(Cursor {
1019 position,
1020 selection: None,
1021 });
1022 }
1023
1024 fn select_all(&mut self) {
1025 self.perform(Action::SelectAll);
1026 }
1027
1028 fn select_range(&mut self, start: text::Position, end: text::Position) {
1029 self.move_to(Cursor {
1030 position: start,
1031 selection: Some(end),
1032 });
1033 }
1034}