1#[cfg(not(feature = "std"))]
4use alloc::{
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::cmp;
9
10use unicode_segmentation::UnicodeSegmentation;
11
12use crate::{
13 render_decoration, Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef,
14 Change, ChangeItem, Color, Cursor, Edit, FontSystem, LayoutRun, LineEnding, LineIter, Renderer,
15 Selection, Shaping,
16};
17
18#[derive(Debug, Clone)]
20pub struct Editor<'buffer> {
21 buffer_ref: BufferRef<'buffer>,
22 cursor: Cursor,
23 cursor_x_opt: Option<i32>,
24 selection: Selection,
25 cursor_moved: bool,
26 auto_indent: bool,
27 change: Option<Change>,
28}
29
30fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> {
31 let x = run.cursor_position(cursor)?;
32 Some((x as i32, run.line_top as i32))
33}
34
35impl<'buffer> Editor<'buffer> {
36 pub fn new(buffer: impl Into<BufferRef<'buffer>>) -> Self {
38 Self {
39 buffer_ref: buffer.into(),
40 cursor: Cursor::default(),
41 cursor_x_opt: None,
42 selection: Selection::None,
43 cursor_moved: false,
44 auto_indent: false,
45 change: None,
46 }
47 }
48
49 #[cfg(feature = "swash")]
53 #[allow(clippy::too_many_arguments)]
54 pub fn draw<F>(
55 &mut self,
56 font_system: &mut FontSystem,
57 cache: &mut crate::SwashCache,
58 text_color: Color,
59 cursor_color: Color,
60 selection_color: Color,
61 selected_text_color: Color,
62 callback: F,
63 ) where
64 F: FnMut(i32, i32, u32, u32, Color),
65 {
66 self.with_buffer_mut(|buffer| buffer.shape_until_scroll(font_system, false));
67 let mut renderer = crate::LegacyRenderer {
68 font_system,
69 cache,
70 callback,
71 };
72 self.render(
73 &mut renderer,
74 text_color,
75 cursor_color,
76 selection_color,
77 selected_text_color,
78 );
79 }
80
81 pub fn render<R: Renderer>(
86 &self,
87 renderer: &mut R,
88 text_color: Color,
89 cursor_color: Color,
90 selection_color: Color,
91 selected_text_color: Color,
92 ) {
93 let selection_bounds = self.selection_bounds();
94 self.with_buffer(|buffer| {
95 for run in buffer.layout_runs() {
96 let line_i = run.line_i;
97 let line_y = run.line_y;
98 let line_top = run.line_top;
99 let line_height = run.line_height;
100
101 if let Some((start, end)) = selection_bounds {
103 if line_i >= start.line && line_i <= end.line {
104 let highlights: Vec<(f32, f32)> = run.highlight(start, end).collect();
105
106 if highlights.is_empty() && run.glyphs.is_empty() && end.line > line_i {
107 let max = buffer.size().0.unwrap_or(0.0) as i32;
109 renderer.rectangle(
110 0,
111 line_top as i32,
112 max as u32,
113 line_height as u32,
114 selection_color,
115 );
116 } else {
117 let len = highlights.len();
118 for (idx, (x, width)) in highlights.into_iter().enumerate() {
119 let mut min = x as i32;
120 let mut max = (x + width) as i32;
121
122 if idx == len - 1 && end.line > line_i {
125 if run.rtl {
126 min = 0;
127 } else {
128 max = buffer.size().0.unwrap_or(0.0) as i32;
129 }
130 }
131
132 renderer.rectangle(
133 min,
134 line_top as i32,
135 cmp::max(0, max - min) as u32,
136 line_height as u32,
137 selection_color,
138 );
139 }
140 }
141 }
142 }
143
144 if let Some((x, y)) = cursor_position(&self.cursor, &run) {
146 renderer.rectangle(x, y, 1, line_height as u32, cursor_color);
147 }
148
149 render_decoration(renderer, &run, text_color);
150 for glyph in run.glyphs {
151 let physical_glyph = glyph.physical((0., line_y), 1.0);
152
153 let mut glyph_color = glyph.color_opt.map_or(text_color, |some| some);
154 if text_color != selected_text_color {
155 if let Some((start, end)) = selection_bounds {
156 if line_i >= start.line
157 && line_i <= end.line
158 && (start.line != line_i || glyph.end > start.index)
159 && (end.line != line_i || glyph.start < end.index)
160 {
161 glyph_color = selected_text_color;
162 }
163 }
164 }
165
166 renderer.glyph(physical_glyph, glyph_color);
167 }
168 }
169 });
170 }
171}
172
173impl<'buffer> Edit<'buffer> for Editor<'buffer> {
174 fn buffer_ref(&self) -> &BufferRef<'buffer> {
175 &self.buffer_ref
176 }
177
178 fn buffer_ref_mut(&mut self) -> &mut BufferRef<'buffer> {
179 &mut self.buffer_ref
180 }
181
182 fn cursor(&self) -> Cursor {
183 self.cursor
184 }
185
186 fn set_cursor(&mut self, cursor: Cursor) {
187 if self.cursor != cursor {
188 self.cursor = cursor;
189 self.cursor_moved = true;
190 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
191 }
192 }
193
194 fn selection(&self) -> Selection {
195 self.selection
196 }
197
198 fn set_selection(&mut self, selection: Selection) {
199 if self.selection != selection {
200 self.selection = selection;
201 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
202 }
203 }
204
205 fn auto_indent(&self) -> bool {
206 self.auto_indent
207 }
208
209 fn set_auto_indent(&mut self, auto_indent: bool) {
210 self.auto_indent = auto_indent;
211 }
212
213 fn tab_width(&self) -> u16 {
214 self.with_buffer(super::super::buffer::Buffer::tab_width)
215 }
216
217 fn set_tab_width(&mut self, tab_width: u16) {
218 self.with_buffer_mut(|buffer| buffer.set_tab_width(tab_width));
219 }
220
221 fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
222 if self.cursor_moved {
223 let cursor = self.cursor;
224 self.with_buffer_mut(|buffer| buffer.shape_until_cursor(font_system, cursor, prune));
225 self.cursor_moved = false;
226 } else {
227 self.with_buffer_mut(|buffer| buffer.shape_until_scroll(font_system, prune));
228 }
229 }
230
231 fn delete_range(&mut self, start: Cursor, end: Cursor) {
232 let change_item = self.with_buffer_mut(|buffer| {
233 let mut change_lines = Vec::new();
235
236 let end_line_opt = if end.line > start.line {
238 let after = buffer.lines[end.line].split_off(end.index);
240
241 let removed = buffer.lines.remove(end.line);
243 change_lines.insert(0, removed);
244
245 Some(after)
246 } else {
247 None
248 };
249
250 for line_i in (start.line + 1..end.line).rev() {
252 let removed = buffer.lines.remove(line_i);
253 change_lines.insert(0, removed);
254 }
255
256 {
258 let line = &mut buffer.lines[start.line];
259
260 let after_opt = if start.line == end.line {
262 Some(line.split_off(end.index))
263 } else {
264 None
265 };
266
267 let removed = line.split_off(start.index);
269 change_lines.insert(0, removed);
270
271 if let Some(after) = after_opt {
273 line.append(&after);
274 }
275
276 if let Some(mut end_line) = end_line_opt {
278 if end_line.ending() == LineEnding::None {
280 end_line.set_ending(line.ending());
281 }
282 line.append(&end_line);
283 }
284 }
285
286 let mut text = String::new();
287 let mut last_ending: Option<LineEnding> = None;
288 for line in change_lines {
289 if let Some(ending) = last_ending {
290 text.push_str(ending.as_str());
291 }
292 text.push_str(line.text());
293 last_ending = Some(line.ending());
294 }
295
296 ChangeItem {
297 start,
298 end,
299 text,
300 insert: false,
301 }
302 });
303
304 if let Some(ref mut change) = self.change {
305 change.items.push(change_item);
306 }
307 }
308
309 fn insert_at(
310 &mut self,
311 mut cursor: Cursor,
312 data: &str,
313 attrs_list: Option<AttrsList>,
314 ) -> Cursor {
315 let mut remaining_split_len = data.len();
316 if remaining_split_len == 0 {
317 return cursor;
318 }
319
320 let change_item = self.with_buffer_mut(|buffer| {
321 let start = cursor;
323
324 while cursor.line >= buffer.lines.len() {
326 let mut last_ending = LineEnding::None;
328 if let Some(last_line) = buffer.lines.last_mut() {
329 last_ending = last_line.ending();
330 if last_ending == LineEnding::None {
332 last_line.set_ending(LineEnding::default());
333 }
334 }
335 let line = BufferLine::new(
336 String::new(),
337 last_ending,
338 AttrsList::new(&attrs_list.as_ref().map_or_else(
339 || {
340 buffer
341 .lines
342 .last()
343 .map_or_else(Attrs::new, |line| line.attrs_list().defaults())
344 },
345 |x| x.defaults(),
346 )),
347 Shaping::Advanced,
348 );
349 buffer.lines.push(line);
350 }
351
352 let line: &mut BufferLine = &mut buffer.lines[cursor.line];
353 let insert_line = cursor.line + 1;
354
355 let after: BufferLine = line.split_off(cursor.index);
357 let after_len = after.text().len();
358
359 let mut final_attrs = attrs_list.unwrap_or_else(|| {
361 AttrsList::new(&line.attrs_list().get_span(cursor.index.saturating_sub(1)))
362 });
363
364 let mut lines: Vec<_> = LineIter::new(data).collect();
366 if lines.last().map(|line| line.1).unwrap_or(LineEnding::None) != LineEnding::None {
368 lines.push((Default::default(), LineEnding::None));
369 }
370 let mut lines_iter = lines.into_iter();
371
372 if let Some((range, ending)) = lines_iter.next() {
374 let data_line = &data[range];
375 let mut these_attrs = final_attrs.split_off(data_line.len());
376 remaining_split_len -= data_line.len() + ending.as_str().len();
377 core::mem::swap(&mut these_attrs, &mut final_attrs);
378 line.append(&BufferLine::new(
379 data_line,
380 ending,
381 these_attrs,
382 Shaping::Advanced,
383 ));
384 }
385 if let Some((range, ending)) = lines_iter.next_back() {
387 let data_line = &data[range];
388 remaining_split_len -= data_line.len() + ending.as_str().len();
389 let mut tmp = BufferLine::new(
390 data_line,
391 ending,
392 final_attrs.split_off(remaining_split_len),
393 Shaping::Advanced,
394 );
395 tmp.append(&after);
396 buffer.lines.insert(insert_line, tmp);
397 cursor.line += 1;
398 } else {
399 line.append(&after);
400 }
401 for (range, ending) in lines_iter.rev() {
403 let data_line = &data[range];
404 remaining_split_len -= data_line.len() + ending.as_str().len();
405 let tmp = BufferLine::new(
406 data_line,
407 ending,
408 final_attrs.split_off(remaining_split_len),
409 Shaping::Advanced,
410 );
411 buffer.lines.insert(insert_line, tmp);
412 cursor.line += 1;
413 }
414
415 assert_eq!(remaining_split_len, 0);
416
417 cursor.index = buffer.lines[cursor.line].text().len() - after_len;
419
420 ChangeItem {
421 start,
422 end: cursor,
423 text: data.to_string(),
424 insert: true,
425 }
426 });
427
428 if let Some(ref mut change) = self.change {
429 change.items.push(change_item);
430 }
431
432 cursor
433 }
434
435 fn copy_selection(&self) -> Option<String> {
436 let (start, end) = self.selection_bounds()?;
437 self.with_buffer(|buffer| {
438 let mut selection = String::new();
439 {
441 if start.line == end.line {
443 selection.push_str(&buffer.lines[start.line].text()[start.index..end.index]);
444 } else {
445 selection.push_str(&buffer.lines[start.line].text()[start.index..]);
446 selection.push('\n');
447 }
448 }
449
450 for line_i in start.line + 1..end.line {
452 selection.push_str(buffer.lines[line_i].text());
453 selection.push('\n');
454 }
455
456 if end.line > start.line {
458 selection.push_str(&buffer.lines[end.line].text()[..end.index]);
460 }
461
462 Some(selection)
463 })
464 }
465
466 fn delete_selection(&mut self) -> bool {
467 let Some((start, end)) = self.selection_bounds() else {
468 return false;
469 };
470
471 self.cursor = start;
473
474 self.selection = Selection::None;
476
477 self.delete_range(start, end);
479
480 true
481 }
482
483 fn apply_change(&mut self, change: &Change) -> bool {
484 if let Some(pending) = self.change.take() {
486 if !pending.items.is_empty() {
487 log::warn!("pending change caused apply_change to be ignored!");
489 self.change = Some(pending);
490 return false;
491 }
492 }
493
494 for item in &change.items {
495 if item.insert {
497 self.cursor = self.insert_at(item.start, &item.text, None);
498 } else {
499 self.cursor = item.start;
500 self.delete_range(item.start, item.end);
501 }
502 }
503 true
504 }
505
506 fn start_change(&mut self) {
507 if self.change.is_none() {
508 self.change = Some(Change::default());
509 }
510 }
511
512 fn finish_change(&mut self) -> Option<Change> {
513 self.change.take()
514 }
515
516 fn action(&mut self, font_system: &mut FontSystem, action: Action) {
517 let old_cursor = self.cursor;
518
519 match action {
520 Action::Motion(motion) => {
521 let cursor = self.cursor;
522 let cursor_x_opt = self.cursor_x_opt;
523 if let Some((new_cursor, new_cursor_x_opt)) = self.with_buffer_mut(|buffer| {
524 buffer.cursor_motion(font_system, cursor, cursor_x_opt, motion)
525 }) {
526 self.cursor = new_cursor;
527 self.cursor_x_opt = new_cursor_x_opt;
528 }
529 }
530 Action::Escape => {
531 match self.selection {
532 Selection::None => {}
533 _ => self.with_buffer_mut(|buffer| buffer.set_redraw(true)),
534 }
535 self.selection = Selection::None;
536 }
537 Action::Insert(character) => {
538 if character.is_control() && !['\t', '\n', '\u{92}'].contains(&character) {
539 log::debug!("Refusing to insert control character {character:?}");
541 } else if character == '\n' {
542 self.action(font_system, Action::Enter);
543 } else {
544 let mut str_buf = [0u8; 8];
545 let str_ref = character.encode_utf8(&mut str_buf);
546 self.insert_string(str_ref, None);
547 }
548 }
549 Action::Enter => {
550 if self.auto_indent {
552 let mut string = String::from("\n");
553 self.with_buffer(|buffer| {
554 let line = &buffer.lines[self.cursor.line];
555 let text = line.text();
556 for c in text.chars() {
557 if c.is_whitespace() {
558 string.push(c);
559 } else {
560 break;
561 }
562 }
563 });
564 self.insert_string(&string, None);
565 } else {
566 self.insert_string("\n", None);
567 }
568
569 let line_i = self.cursor.line;
571 self.with_buffer_mut(|buffer| {
572 buffer.line_layout(font_system, line_i);
573 });
574 }
575 Action::Backspace => {
576 if self.delete_selection() {
577 } else {
579 let end = self.cursor;
581
582 if self.cursor.index > 0 {
583 self.cursor.index = self.with_buffer(|buffer| {
585 buffer.lines[self.cursor.line].text()[..self.cursor.index]
586 .char_indices()
587 .next_back()
588 .map_or(0, |(i, _)| i)
589 });
590 } else if self.cursor.line > 0 {
591 self.cursor.line -= 1;
593 self.cursor.index =
594 self.with_buffer(|buffer| buffer.lines[self.cursor.line].text().len());
595 }
596
597 if self.cursor != end {
598 self.delete_range(self.cursor, end);
600 }
601 }
602 }
603 Action::Delete => {
604 if self.delete_selection() {
605 } else {
607 let mut start = self.cursor;
609 let mut end = self.cursor;
610
611 self.with_buffer(|buffer| {
612 if start.index < buffer.lines[start.line].text().len() {
613 let line = &buffer.lines[start.line];
614
615 let range_opt = line
616 .text()
617 .grapheme_indices(true)
618 .take_while(|(i, _)| *i <= start.index)
619 .last()
620 .map(|(i, c)| i..(i + c.len()));
621
622 if let Some(range) = range_opt {
623 start.index = range.start;
624 end.index = range.end;
625 }
626 } else if start.line + 1 < buffer.lines.len() {
627 end.line += 1;
628 end.index = 0;
629 }
630 });
631
632 if start != end {
633 self.cursor = start;
634 self.delete_range(start, end);
635 }
636 }
637 }
638 Action::Indent => {
639 let (start, end) = match self.selection_bounds() {
641 Some(some) => some,
642 None => (self.cursor, self.cursor),
643 };
644
645 let tab_width: usize = self.tab_width().into();
647 for line_i in start.line..=end.line {
648 let mut after_whitespace = 0;
650 let mut required_indent = 0;
651 self.with_buffer(|buffer| {
652 let line = &buffer.lines[line_i];
653 let text = line.text();
654
655 if self.selection == Selection::None {
656 let whitespace_length = match line.text()[0..self.cursor.index]
658 .chars()
659 .rev()
660 .position(|c| !c.is_whitespace())
661 {
662 Some(length) => length,
663 None => self.cursor.index,
665 };
666 required_indent = tab_width - (whitespace_length % tab_width);
667 after_whitespace = self.cursor.index;
668 } else {
669 for (count, (index, c)) in text.char_indices().enumerate() {
671 if !c.is_whitespace() {
672 after_whitespace = index;
673 required_indent = tab_width - (count % tab_width);
674 break;
675 }
676 }
677 }
678 });
679
680 self.insert_at(
681 Cursor::new(line_i, after_whitespace),
682 &" ".repeat(required_indent),
683 None,
684 );
685
686 if self.cursor.line == line_i {
688 if self.cursor.index < after_whitespace {
690 self.cursor.index = after_whitespace;
691 }
692 self.cursor.index += required_indent;
693 }
694
695 match self.selection {
697 Selection::None => {}
698 Selection::Normal(ref mut select)
699 | Selection::Line(ref mut select)
700 | Selection::Word(ref mut select) => {
701 if select.line == line_i && select.index >= after_whitespace {
702 select.index += required_indent;
703 }
704 }
705 }
706 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
708 }
709 }
710 Action::Unindent => {
711 let (start, end) = match self.selection_bounds() {
713 Some(some) => some,
714 None => (self.cursor, self.cursor),
715 };
716
717 let tab_width: usize = self.tab_width().into();
719 for line_i in start.line..=end.line {
720 let mut last_indent = 0;
722 let mut after_whitespace = 0;
723 self.with_buffer(|buffer| {
724 let line = &buffer.lines[line_i];
725 let text = line.text();
726 after_whitespace = text.len();
728 for (count, (index, c)) in text.char_indices().enumerate() {
729 if !c.is_whitespace() {
730 after_whitespace = index;
731 break;
732 }
733 if count % tab_width == 0 {
734 last_indent = index;
735 }
736 }
737 });
738
739 if last_indent == after_whitespace {
741 continue;
742 }
743
744 self.delete_range(
746 Cursor::new(line_i, last_indent),
747 Cursor::new(line_i, after_whitespace),
748 );
749
750 if self.cursor.line == line_i && self.cursor.index > last_indent {
752 self.cursor.index -= after_whitespace - last_indent;
753 }
754
755 match self.selection {
757 Selection::None => {}
758 Selection::Normal(ref mut select)
759 | Selection::Line(ref mut select)
760 | Selection::Word(ref mut select) => {
761 if select.line == line_i && select.index > last_indent {
762 select.index -= after_whitespace - last_indent;
763 }
764 }
765 }
766
767 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
769 }
770 }
771 Action::Click { x, y } => {
772 self.set_selection(Selection::None);
773
774 if let Some(new_cursor) = self.with_buffer_mut(|buffer| {
775 buffer.shape_until_scroll(font_system, false);
776 buffer.hit(x as f32, y as f32)
777 }) {
778 if new_cursor != self.cursor {
779 self.cursor = new_cursor;
780 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
781 }
782 }
783 }
784 Action::DoubleClick { x, y } => {
785 self.set_selection(Selection::None);
786
787 if let Some(new_cursor) = self.with_buffer_mut(|buffer| {
788 buffer.shape_until_scroll(font_system, false);
789 buffer.hit(x as f32, y as f32)
790 }) {
791 if new_cursor != self.cursor {
792 self.cursor = new_cursor;
793 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
794 }
795 self.selection = Selection::Word(self.cursor);
796 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
797 }
798 }
799 Action::TripleClick { x, y } => {
800 self.set_selection(Selection::None);
801
802 if let Some(new_cursor) = self.with_buffer_mut(|buffer| {
803 buffer.shape_until_scroll(font_system, false);
804 buffer.hit(x as f32, y as f32)
805 }) {
806 if new_cursor != self.cursor {
807 self.cursor = new_cursor;
808 }
809 self.selection = Selection::Line(self.cursor);
810 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
811 }
812 }
813 Action::Drag { x, y } => {
814 if self.selection == Selection::None {
815 self.selection = Selection::Normal(self.cursor);
816 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
817 }
818
819 if let Some(new_cursor) = self.with_buffer_mut(|buffer| {
820 buffer.shape_until_scroll(font_system, false);
821 buffer.hit(x as f32, y as f32)
822 }) {
823 if new_cursor != self.cursor {
824 self.cursor = new_cursor;
825 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
826 }
827 }
828 }
829 Action::Scroll { pixels } => {
830 self.with_buffer_mut(|buffer| {
831 let mut scroll = buffer.scroll();
832 scroll.vertical += pixels;
834 buffer.set_scroll(scroll);
835 });
836 }
837 }
838
839 if old_cursor != self.cursor {
840 self.cursor_moved = true;
841 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
842
843 }
859 }
860
861 fn cursor_position(&self) -> Option<(i32, i32)> {
862 self.with_buffer(|buffer| {
863 buffer
864 .layout_runs()
865 .find_map(|run| cursor_position(&self.cursor, &run))
866 })
867 }
868}
869
870impl BorrowedWithFontSystem<'_, Editor<'_>> {
871 #[cfg(feature = "swash")]
872 pub fn draw<F>(
873 &mut self,
874 cache: &mut crate::SwashCache,
875 text_color: Color,
876 cursor_color: Color,
877 selection_color: Color,
878 selected_text_color: Color,
879 f: F,
880 ) where
881 F: FnMut(i32, i32, u32, u32, Color),
882 {
883 self.inner.draw(
884 self.font_system,
885 cache,
886 text_color,
887 cursor_color,
888 selection_color,
889 selected_text_color,
890 f,
891 );
892 }
893}