1use crate::core::text::editor::{
3 self, Action, Cursor, Direction, Edit, Motion, Position, Selection,
4};
5use crate::core::text::highlighter::{self, Highlighter};
6use crate::core::text::{LineHeight, Wrapping};
7use crate::core::{Font, Pixels, Point, Rectangle, Size};
8use crate::text;
9
10use cosmic_text::Edit as _;
11
12use std::borrow::Cow;
13use std::fmt;
14use std::sync::{self, Arc, RwLock};
15
16const UNDO_LIMIT: usize = 200;
18
19#[derive(Clone)]
21struct Snapshot {
22 text: String,
24 cursor: cosmic_text::Cursor,
26 selection: cosmic_text::Selection,
28}
29
30#[derive(Debug, PartialEq)]
32pub struct Editor(Option<Arc<Internal>>);
33
34struct Internal {
35 editor: cosmic_text::Editor<'static>,
36 selection: RwLock<Option<Selection>>,
37 font: Font,
38 bounds: Size,
39 topmost_line_changed: Option<usize>,
40 hint: bool,
41 hint_factor: f32,
42 version: text::Version,
43 undo_stack: Vec<Snapshot>,
44 redo_stack: Vec<Snapshot>,
45}
46
47impl Editor {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn buffer(&self) -> &cosmic_text::Buffer {
55 buffer_from_editor(&self.internal().editor)
56 }
57
58 pub fn downgrade(&self) -> Weak {
64 let editor = self.internal();
65
66 Weak {
67 raw: Arc::downgrade(editor),
68 bounds: editor.bounds,
69 }
70 }
71
72 fn internal(&self) -> &Arc<Internal> {
73 self.0
74 .as_ref()
75 .expect("Editor should always be initialized")
76 }
77
78 fn with_internal_mut<T>(&mut self, f: impl FnOnce(&mut Internal) -> T) -> T {
79 let editor = self.0.take().expect("Editor should always be initialized");
80
81 let mut internal =
83 Arc::try_unwrap(editor).expect("Editor cannot have multiple strong references");
84
85 let _ = internal
87 .selection
88 .write()
89 .expect("Write to cursor cache")
90 .take();
91
92 let result = f(&mut internal);
93
94 self.0 = Some(Arc::new(internal));
95
96 result
97 }
98}
99
100impl editor::Editor for Editor {
101 type Font = Font;
102
103 fn with_text(text: &str) -> Self {
104 let mut buffer = cosmic_text::Buffer::new_empty(cosmic_text::Metrics {
105 font_size: 1.0,
106 line_height: 1.0,
107 });
108
109 let mut font_system = text::font_system().write().expect("Write font system");
110
111 buffer.set_text(
112 font_system.raw(),
113 text,
114 &cosmic_text::Attrs::new(),
115 cosmic_text::Shaping::Advanced,
116 None,
117 );
118
119 Editor(Some(Arc::new(Internal {
120 editor: cosmic_text::Editor::new(buffer),
121 version: font_system.version(),
122 ..Default::default()
123 })))
124 }
125
126 fn is_empty(&self) -> bool {
127 let buffer = self.buffer();
128
129 buffer.lines.is_empty() || (buffer.lines.len() == 1 && buffer.lines[0].text().is_empty())
130 }
131
132 fn line(&self, index: usize) -> Option<editor::Line<'_>> {
133 self.buffer().lines.get(index).map(|line| editor::Line {
134 text: Cow::Borrowed(line.text()),
135 ending: match line.ending() {
136 cosmic_text::LineEnding::Lf => editor::LineEnding::Lf,
137 cosmic_text::LineEnding::CrLf => editor::LineEnding::CrLf,
138 cosmic_text::LineEnding::Cr => editor::LineEnding::Cr,
139 cosmic_text::LineEnding::LfCr => editor::LineEnding::LfCr,
140 cosmic_text::LineEnding::None => editor::LineEnding::None,
141 },
142 })
143 }
144
145 fn line_count(&self) -> usize {
146 self.buffer().lines.len()
147 }
148
149 fn copy(&self) -> Option<String> {
150 self.internal().editor.copy_selection()
151 }
152
153 fn selection(&self) -> editor::Selection {
154 let internal = self.internal();
155
156 if let Ok(Some(cursor)) = internal.selection.read().as_deref() {
157 return cursor.clone();
158 }
159
160 let cursor = internal.editor.cursor();
161 let buffer = buffer_from_editor(&internal.editor);
162
163 let cursor = match internal.editor.selection_bounds() {
164 Some((start, end)) => {
165 let line_height = buffer.metrics().line_height;
166 let selected_lines = end.line - start.line + 1;
167
168 let visual_lines_offset = visual_lines_offset(start.line, buffer);
169
170 let regions = buffer
171 .lines
172 .iter()
173 .skip(start.line)
174 .take(selected_lines)
175 .enumerate()
176 .flat_map(|(i, line)| {
177 highlight_line(
178 line,
179 if i == 0 { start.index } else { 0 },
180 if i == selected_lines - 1 {
181 end.index
182 } else {
183 line.text().len()
184 },
185 )
186 })
187 .enumerate()
188 .filter_map(|(visual_line, (x, width))| {
189 if width > 0.0 {
190 Some(
191 Rectangle {
192 x,
193 width,
194 y: (visual_line as i32 + visual_lines_offset) as f32
195 * line_height
196 - buffer.scroll().vertical,
197 height: line_height,
198 } * (1.0 / internal.hint_factor),
199 )
200 } else {
201 None
202 }
203 })
204 .collect();
205
206 Selection::Range(regions)
207 }
208 _ => {
209 let line_height = buffer.metrics().line_height;
210
211 let visual_lines_offset = visual_lines_offset(cursor.line, buffer);
212
213 let line = buffer
214 .lines
215 .get(cursor.line)
216 .expect("Cursor line should be present");
217
218 let layout = line.layout_opt().expect("Line layout should be cached");
219
220 let mut lines = layout.iter().enumerate();
221
222 let (visual_line, offset) = lines
223 .find_map(|(i, line)| {
224 let start = line.glyphs.first().map(|glyph| glyph.start).unwrap_or(0);
225 let end = line.glyphs.last().map(|glyph| glyph.end).unwrap_or(0);
226
227 let is_cursor_before_start = start > cursor.index;
228
229 let is_cursor_before_end = match cursor.affinity {
230 cosmic_text::Affinity::Before => cursor.index <= end,
231 cosmic_text::Affinity::After => cursor.index < end,
232 };
233
234 if is_cursor_before_start {
235 Some((i - 1, layout[i - 1].w))
244 } else if is_cursor_before_end {
245 let offset = line
246 .glyphs
247 .iter()
248 .take_while(|glyph| cursor.index > glyph.start)
249 .map(|glyph| glyph.w)
250 .sum();
251
252 Some((i, offset))
253 } else {
254 None
255 }
256 })
257 .unwrap_or((
258 layout.len().saturating_sub(1),
259 layout.last().map(|line| line.w).unwrap_or(0.0),
260 ));
261
262 Selection::Caret(Point::new(
263 offset / internal.hint_factor,
264 ((visual_lines_offset + visual_line as i32) as f32 * line_height
265 - buffer.scroll().vertical)
266 / internal.hint_factor,
267 ))
268 }
269 };
270
271 *internal.selection.write().expect("Write to cursor cache") = Some(cursor.clone());
272
273 cursor
274 }
275
276 fn cursor(&self) -> Cursor {
277 let editor = &self.internal().editor;
278
279 let position = {
280 let cursor = editor.cursor();
281
282 Position {
283 line: cursor.line,
284 column: cursor.index,
285 }
286 };
287
288 let selection = match editor.selection() {
289 cosmic_text::Selection::None => None,
290 cosmic_text::Selection::Normal(cursor)
291 | cosmic_text::Selection::Line(cursor)
292 | cosmic_text::Selection::Word(cursor) => Some(Position {
293 line: cursor.line,
294 column: cursor.index,
295 }),
296 };
297
298 Cursor {
299 position,
300 selection,
301 }
302 }
303
304 fn perform(&mut self, action: Action) {
305 let mut font_system = text::font_system().write().expect("Write font system");
306
307 self.with_internal_mut(|internal| {
308 match action {
309 Action::Move(motion) => {
311 let editor = &mut internal.editor;
312
313 if let Some((start, end)) = editor.selection_bounds() {
314 editor.set_selection(cosmic_text::Selection::None);
315
316 match motion {
317 Motion::Home
320 | Motion::End
321 | Motion::DocumentStart
322 | Motion::DocumentEnd => {
323 editor.action(
324 font_system.raw(),
325 cosmic_text::Action::Motion(to_motion(motion)),
326 );
327 }
328 _ => editor.set_cursor(match motion.direction() {
330 Direction::Left => start,
331 Direction::Right => end,
332 }),
333 }
334 } else {
335 editor.action(
336 font_system.raw(),
337 cosmic_text::Action::Motion(to_motion(motion)),
338 );
339 }
340 }
341
342 Action::Select(motion) => {
344 let editor = &mut internal.editor;
345 let cursor = editor.cursor();
346
347 if editor.selection_bounds().is_none() {
348 editor.set_selection(cosmic_text::Selection::Normal(cursor));
349 }
350
351 editor.action(
352 font_system.raw(),
353 cosmic_text::Action::Motion(to_motion(motion)),
354 );
355
356 if let Some((start, end)) = editor.selection_bounds()
358 && start.line == end.line
359 && start.index == end.index
360 {
361 editor.set_selection(cosmic_text::Selection::None);
362 }
363 }
364 Action::SelectWord => {
365 let cursor = internal.editor.cursor();
366
367 internal
368 .editor
369 .set_selection(cosmic_text::Selection::Word(cursor));
370 }
371 Action::SelectLine => {
372 let cursor = internal.editor.cursor();
373
374 internal
375 .editor
376 .set_selection(cosmic_text::Selection::Line(cursor));
377 }
378 Action::SelectAll => {
379 let editor = &mut internal.editor;
380 let buffer = buffer_from_editor(editor);
381
382 if buffer.lines.len() > 1
383 || buffer
384 .lines
385 .first()
386 .is_some_and(|line| !line.text().is_empty())
387 {
388 let cursor = editor.cursor();
389
390 editor.set_selection(cosmic_text::Selection::Normal(cosmic_text::Cursor {
391 line: 0,
392 index: 0,
393 ..cursor
394 }));
395
396 editor.action(
397 font_system.raw(),
398 cosmic_text::Action::Motion(cosmic_text::Motion::BufferEnd),
399 );
400 }
401 }
402
403 Action::Edit(edit) => {
405 internal.push_undo();
407
408 let editor = &mut internal.editor;
409
410 let topmost_line_before_edit = editor
411 .selection_bounds()
412 .map(|(start, _)| start)
413 .unwrap_or_else(|| editor.cursor())
414 .line;
415
416 match edit {
417 Edit::Insert(c) => {
418 editor.action(font_system.raw(), cosmic_text::Action::Insert(c));
419 }
420 Edit::Paste(text) => {
421 editor.insert_string(&text, None);
422 }
423 Edit::Indent => {
424 editor.action(font_system.raw(), cosmic_text::Action::Indent);
425 }
426 Edit::Unindent => {
427 editor.action(font_system.raw(), cosmic_text::Action::Unindent);
428 }
429 Edit::Enter => {
430 editor.action(font_system.raw(), cosmic_text::Action::Enter);
431 }
432 Edit::Backspace => {
433 editor.action(font_system.raw(), cosmic_text::Action::Backspace);
434 }
435 Edit::Delete => {
436 editor.action(font_system.raw(), cosmic_text::Action::Delete);
437 }
438 }
439
440 let cursor = editor.cursor();
441 let selection_start = editor
442 .selection_bounds()
443 .map(|(start, _)| start)
444 .unwrap_or(cursor);
445
446 internal.topmost_line_changed =
447 Some(selection_start.line.min(topmost_line_before_edit));
448 }
449
450 Action::Undo => {
452 if let Some(snapshot) = internal.undo_stack.pop() {
453 let current = internal.snapshot();
455 internal.redo_stack.push(current);
456
457 internal.restore_snapshot(&snapshot, font_system.raw());
458 }
459 }
460 Action::Redo => {
461 if let Some(snapshot) = internal.redo_stack.pop() {
462 let current = internal.snapshot();
464 internal.undo_stack.push(current);
465
466 internal.restore_snapshot(&snapshot, font_system.raw());
467 }
468 }
469
470 Action::Click(position) => {
472 internal.editor.action(
473 font_system.raw(),
474 cosmic_text::Action::Click {
475 x: (position.x * internal.hint_factor) as i32,
476 y: (position.y * internal.hint_factor) as i32,
477 },
478 );
479 }
480 Action::Drag(position) => {
481 internal.editor.action(
482 font_system.raw(),
483 cosmic_text::Action::Drag {
484 x: (position.x * internal.hint_factor) as i32,
485 y: (position.y * internal.hint_factor) as i32,
486 },
487 );
488
489 if let Some((start, end)) = internal.editor.selection_bounds()
491 && start.line == end.line
492 && start.index == end.index
493 {
494 internal.editor.set_selection(cosmic_text::Selection::None);
495 }
496 }
497 Action::Scroll { lines } => {
498 let editor = &mut internal.editor;
499
500 editor.action(
501 font_system.raw(),
502 cosmic_text::Action::Scroll {
503 pixels: lines as f32 * buffer_from_editor(editor).metrics().line_height,
504 },
505 );
506 }
507 }
508 });
509 }
510
511 fn move_to(&mut self, cursor: Cursor) {
512 self.with_internal_mut(|internal| {
513 internal.editor.set_cursor(cosmic_text::Cursor {
515 line: cursor.position.line,
516 index: cursor.position.column,
517 affinity: cosmic_text::Affinity::Before,
518 });
519
520 if let Some(selection) = cursor.selection {
521 internal
522 .editor
523 .set_selection(cosmic_text::Selection::Normal(cosmic_text::Cursor {
524 line: selection.line,
525 index: selection.column,
526 affinity: cosmic_text::Affinity::Before,
527 }));
528 }
529 });
530 }
531
532 fn bounds(&self) -> Size {
533 self.internal().bounds
534 }
535
536 fn min_bounds(&self) -> Size {
537 let internal = self.internal();
538
539 let (bounds, _has_rtl) = text::measure(buffer_from_editor(&internal.editor));
540
541 bounds * (1.0 / internal.hint_factor)
542 }
543
544 fn hint_factor(&self) -> Option<f32> {
545 let internal = self.internal();
546
547 internal.hint.then_some(internal.hint_factor)
548 }
549
550 fn update(
551 &mut self,
552 new_bounds: Size,
553 new_font: Font,
554 new_size: Pixels,
555 new_line_height: LineHeight,
556 new_wrapping: Wrapping,
557 new_hint_factor: Option<f32>,
558 new_highlighter: &mut impl Highlighter,
559 ) {
560 self.with_internal_mut(|internal| {
561 let mut font_system = text::font_system().write().expect("Write font system");
562
563 let buffer = buffer_mut_from_editor(&mut internal.editor);
564
565 if font_system.version() != internal.version {
566 log::trace!("Updating `FontSystem` of `Editor`...");
567
568 for line in buffer.lines.iter_mut() {
569 line.reset();
570 }
571
572 internal.version = font_system.version();
573 internal.topmost_line_changed = Some(0);
574 }
575
576 if new_font != internal.font {
577 log::trace!("Updating font of `Editor`...");
578
579 for line in buffer.lines.iter_mut() {
580 let _ = line.set_attrs_list(cosmic_text::AttrsList::new(&text::to_attributes(
581 new_font,
582 )));
583 }
584
585 internal.font = new_font;
586 internal.topmost_line_changed = Some(0);
587 }
588
589 let metrics = buffer.metrics();
590 let new_line_height = new_line_height.to_absolute(new_size);
591 let mut hinting_changed = false;
592
593 let new_hint_factor = text::hint_factor(new_size, new_hint_factor);
594
595 if new_hint_factor != internal.hint.then_some(internal.hint_factor) {
596 internal.hint = new_hint_factor.is_some();
597 internal.hint_factor = new_hint_factor.unwrap_or(1.0);
598
599 buffer.set_hinting(
600 font_system.raw(),
601 if internal.hint {
602 cosmic_text::Hinting::Enabled
603 } else {
604 cosmic_text::Hinting::Disabled
605 },
606 );
607
608 hinting_changed = true;
609 }
610
611 if new_size.0 != metrics.font_size
612 || new_line_height.0 != metrics.line_height
613 || hinting_changed
614 {
615 log::trace!("Updating `Metrics` of `Editor`...");
616
617 buffer.set_metrics(
618 font_system.raw(),
619 cosmic_text::Metrics::new(
620 new_size.0 * internal.hint_factor,
621 new_line_height.0 * internal.hint_factor,
622 ),
623 );
624 }
625
626 let new_wrap = text::to_wrap(new_wrapping);
627
628 if new_wrap != buffer.wrap() {
629 log::trace!("Updating `Wrap` strategy of `Editor`...");
630
631 buffer.set_wrap(font_system.raw(), new_wrap);
632 }
633
634 if new_bounds != internal.bounds || hinting_changed {
635 log::trace!("Updating size of `Editor`...");
636
637 buffer.set_size(
638 font_system.raw(),
639 Some(new_bounds.width * internal.hint_factor),
640 Some(new_bounds.height * internal.hint_factor),
641 );
642
643 internal.bounds = new_bounds;
644 }
645
646 if let Some(topmost_line_changed) = internal.topmost_line_changed.take() {
647 log::trace!(
648 "Notifying highlighter of line \
649 change: {topmost_line_changed}"
650 );
651
652 new_highlighter.change_line(topmost_line_changed);
653 }
654
655 internal.editor.shape_as_needed(font_system.raw(), false);
656 });
657 }
658
659 fn highlight<H: Highlighter>(
660 &mut self,
661 font: Self::Font,
662 highlighter: &mut H,
663 format_highlight: impl Fn(&H::Highlight) -> highlighter::Format<Self::Font>,
664 ) {
665 let internal = self.internal();
666 let buffer = buffer_from_editor(&internal.editor);
667
668 let scroll = buffer.scroll();
669 let mut window = (internal.bounds.height * internal.hint_factor
670 / buffer.metrics().line_height)
671 .ceil() as i32;
672
673 let last_visible_line = buffer.lines[scroll.line..]
674 .iter()
675 .enumerate()
676 .find_map(|(i, line)| {
677 let visible_lines = line
678 .layout_opt()
679 .as_ref()
680 .expect("Line layout should be cached")
681 .len() as i32;
682
683 if window > visible_lines {
684 window -= visible_lines;
685 None
686 } else {
687 Some(scroll.line + i)
688 }
689 })
690 .unwrap_or(buffer.lines.len().saturating_sub(1));
691
692 let current_line = highlighter.current_line();
693
694 if current_line > last_visible_line {
695 return;
696 }
697
698 let editor = self.0.take().expect("Editor should always be initialized");
699
700 let mut internal =
701 Arc::try_unwrap(editor).expect("Editor cannot have multiple strong references");
702
703 let mut font_system = text::font_system().write().expect("Write font system");
704
705 let attributes = text::to_attributes(font);
706
707 for line in &mut buffer_mut_from_editor(&mut internal.editor).lines
708 [current_line..=last_visible_line]
709 {
710 let mut list = cosmic_text::AttrsList::new(&attributes);
711
712 for (range, highlight) in highlighter.highlight_line(line.text()) {
713 let format = format_highlight(&highlight);
714
715 if format.color.is_some() || format.font.is_some() {
716 list.add_span(
717 range,
718 &cosmic_text::Attrs {
719 color_opt: format.color.map(text::to_color),
720 ..if let Some(font) = format.font {
721 text::to_attributes(font)
722 } else {
723 attributes.clone()
724 }
725 },
726 );
727 }
728 }
729
730 let _ = line.set_attrs_list(list);
731 }
732
733 internal.editor.shape_as_needed(font_system.raw(), false);
734
735 self.0 = Some(Arc::new(internal));
736 }
737}
738
739impl Default for Editor {
740 fn default() -> Self {
741 Self(Some(Arc::new(Internal::default())))
742 }
743}
744
745impl PartialEq for Internal {
746 fn eq(&self, other: &Self) -> bool {
747 self.font == other.font
748 && self.bounds == other.bounds
749 && buffer_from_editor(&self.editor).metrics()
750 == buffer_from_editor(&other.editor).metrics()
751 }
752}
753
754impl Default for Internal {
755 fn default() -> Self {
756 Self {
757 editor: cosmic_text::Editor::new(cosmic_text::Buffer::new_empty(
758 cosmic_text::Metrics {
759 font_size: 1.0,
760 line_height: 1.0,
761 },
762 )),
763 selection: RwLock::new(None),
764 font: Font::default(),
765 bounds: Size::ZERO,
766 topmost_line_changed: None,
767 hint: false,
768 hint_factor: 1.0,
769 version: text::Version::default(),
770 undo_stack: Vec::new(),
771 redo_stack: Vec::new(),
772 }
773 }
774}
775
776impl fmt::Debug for Internal {
777 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
778 f.debug_struct("Internal")
779 .field("font", &self.font)
780 .field("bounds", &self.bounds)
781 .finish()
782 }
783}
784
785#[derive(Debug, Clone)]
787pub struct Weak {
788 raw: sync::Weak<Internal>,
789 pub bounds: Size,
791}
792
793impl Weak {
794 pub fn upgrade(&self) -> Option<Editor> {
796 self.raw.upgrade().map(Some).map(Editor)
797 }
798}
799
800impl PartialEq for Weak {
801 fn eq(&self, other: &Self) -> bool {
802 match (self.raw.upgrade(), other.raw.upgrade()) {
803 (Some(p1), Some(p2)) => p1 == p2,
804 _ => false,
805 }
806 }
807}
808
809fn highlight_line(
810 line: &cosmic_text::BufferLine,
811 from: usize,
812 to: usize,
813) -> impl Iterator<Item = (f32, f32)> + '_ {
814 let layout = line.layout_opt().map(Vec::as_slice).unwrap_or_default();
815
816 layout.iter().map(move |visual_line| {
817 let start = visual_line
818 .glyphs
819 .first()
820 .map(|glyph| glyph.start)
821 .unwrap_or(0);
822 let end = visual_line
823 .glyphs
824 .last()
825 .map(|glyph| glyph.end)
826 .unwrap_or(0);
827
828 let range = start.max(from)..end.min(to);
829
830 if range.is_empty() {
831 (0.0, 0.0)
832 } else if range.start == start && range.end == end {
833 (0.0, visual_line.w)
834 } else {
835 let first_glyph = visual_line
836 .glyphs
837 .iter()
838 .position(|glyph| range.start <= glyph.start)
839 .unwrap_or(0);
840
841 let mut glyphs = visual_line.glyphs.iter();
842
843 let x = glyphs.by_ref().take(first_glyph).map(|glyph| glyph.w).sum();
844
845 let width: f32 = glyphs
846 .take_while(|glyph| range.end > glyph.start)
847 .map(|glyph| glyph.w)
848 .sum();
849
850 (x, width)
851 }
852 })
853}
854
855fn visual_lines_offset(line: usize, buffer: &cosmic_text::Buffer) -> i32 {
856 let scroll = buffer.scroll();
857
858 let start = scroll.line.min(line);
859 let end = scroll.line.max(line);
860
861 let visual_lines_offset: usize = buffer.lines[start..]
862 .iter()
863 .take(end - start)
864 .map(|line| line.layout_opt().map(Vec::len).unwrap_or_default())
865 .sum();
866
867 visual_lines_offset as i32 * if scroll.line < line { 1 } else { -1 }
868}
869
870fn text_from_buffer(buffer: &cosmic_text::Buffer) -> String {
872 let mut text = String::new();
873 let line_count = buffer.lines.len();
874
875 for (i, line) in buffer.lines.iter().enumerate() {
876 text.push_str(line.text());
877 if i + 1 < line_count {
878 let ending = match line.ending() {
879 cosmic_text::LineEnding::CrLf => "\r\n",
880 cosmic_text::LineEnding::Cr => "\r",
881 cosmic_text::LineEnding::LfCr => "\n\r",
882 cosmic_text::LineEnding::None | cosmic_text::LineEnding::Lf => "\n",
883 };
884 text.push_str(ending);
885 }
886 }
887
888 text
889}
890
891impl Internal {
892 fn snapshot(&self) -> Snapshot {
894 let buffer = buffer_from_editor(&self.editor);
895 Snapshot {
896 text: text_from_buffer(buffer),
897 cursor: self.editor.cursor(),
898 selection: self.editor.selection(),
899 }
900 }
901
902 fn push_undo(&mut self) {
904 let snap = self.snapshot();
905
906 if self.undo_stack.len() >= UNDO_LIMIT {
907 let _ = self.undo_stack.remove(0);
908 }
909
910 self.undo_stack.push(snap);
911 self.redo_stack.clear();
912 }
913
914 fn restore_snapshot(&mut self, snapshot: &Snapshot, font_system: &mut cosmic_text::FontSystem) {
916 let buffer = buffer_mut_from_editor(&mut self.editor);
917
918 buffer.set_text(
919 font_system,
920 &snapshot.text,
921 &cosmic_text::Attrs::new(),
922 cosmic_text::Shaping::Advanced,
923 None,
924 );
925
926 let font_attrs = crate::text::to_attributes(self.font);
928 for line in buffer.lines.iter_mut() {
929 let _ = line.set_attrs_list(cosmic_text::AttrsList::new(&font_attrs));
930 }
931
932 self.editor.set_cursor(snapshot.cursor);
933 self.editor.set_selection(snapshot.selection);
934 self.topmost_line_changed = Some(0);
935 }
936}
937
938fn to_motion(motion: Motion) -> cosmic_text::Motion {
939 match motion {
940 Motion::Left => cosmic_text::Motion::Left,
941 Motion::Right => cosmic_text::Motion::Right,
942 Motion::Up => cosmic_text::Motion::Up,
943 Motion::Down => cosmic_text::Motion::Down,
944 Motion::WordLeft => cosmic_text::Motion::LeftWord,
945 Motion::WordRight => cosmic_text::Motion::RightWord,
946 Motion::Home => cosmic_text::Motion::Home,
947 Motion::End => cosmic_text::Motion::End,
948 Motion::PageUp => cosmic_text::Motion::PageUp,
949 Motion::PageDown => cosmic_text::Motion::PageDown,
950 Motion::DocumentStart => cosmic_text::Motion::BufferStart,
951 Motion::DocumentEnd => cosmic_text::Motion::BufferEnd,
952 }
953}
954
955fn buffer_from_editor<'a, 'b>(editor: &'a impl cosmic_text::Edit<'b>) -> &'a cosmic_text::Buffer
956where
957 'b: 'a,
958{
959 match editor.buffer_ref() {
960 cosmic_text::BufferRef::Owned(buffer) => buffer,
961 cosmic_text::BufferRef::Borrowed(buffer) => buffer,
962 cosmic_text::BufferRef::Arc(buffer) => buffer,
963 }
964}
965
966fn buffer_mut_from_editor<'a, 'b>(
967 editor: &'a mut impl cosmic_text::Edit<'b>,
968) -> &'a mut cosmic_text::Buffer
969where
970 'b: 'a,
971{
972 match editor.buffer_ref_mut() {
973 cosmic_text::BufferRef::Owned(buffer) => buffer,
974 cosmic_text::BufferRef::Borrowed(buffer) => buffer,
975 cosmic_text::BufferRef::Arc(_buffer) => unreachable!(),
976 }
977}