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