1use alloc::{collections::BTreeMap, string::String};
2use core::cmp;
3use modit::{Event, Key, Parser, TextObject, WordIter};
4use unicode_segmentation::UnicodeSegmentation;
5
6use crate::{
7 Action, AttrsList, BorrowedWithFontSystem, BufferRef, Change, Color, Cursor, Edit, FontSystem,
8 Motion, Selection, SyntaxEditor, SyntaxTheme,
9};
10
11pub use modit::{ViMode, ViParser};
12
13fn undo_2_action<'buffer, E: Edit<'buffer>>(
14 editor: &mut E,
15 action: cosmic_undo_2::Action<&Change>,
16) {
17 match action {
18 cosmic_undo_2::Action::Do(change) => {
19 editor.apply_change(change);
20 }
21 cosmic_undo_2::Action::Undo(change) => {
22 let mut reversed = change.clone();
24 reversed.reverse();
25 editor.apply_change(&reversed);
26 }
27 }
28}
29
30fn finish_change<'buffer, E: Edit<'buffer>>(
31 editor: &mut E,
32 commands: &mut cosmic_undo_2::Commands<Change>,
33 changed: &mut bool,
34 pivot: Option<usize>,
35) -> Option<Change> {
36 match editor.finish_change() {
38 Some(change) => {
39 if !change.items.is_empty() {
40 commands.push(change.clone());
41 *changed = eval_changed(commands, pivot);
42 }
43 Some(change)
44 }
45 None => None,
46 }
47}
48
49fn eval_changed(commands: &cosmic_undo_2::Commands<Change>, pivot: Option<usize>) -> bool {
51 match (commands.current_command_index(), pivot) {
56 (Some(current), Some(pivot)) => current != pivot,
57 (None, None) => false,
61 _ => true,
64 }
65}
66
67fn search<'buffer, E: Edit<'buffer>>(editor: &mut E, value: &str, forwards: bool) -> bool {
68 let mut cursor = editor.cursor();
69 let start_line = cursor.line;
70 if forwards {
71 while cursor.line < editor.with_buffer(|buffer| buffer.lines.len()) {
72 if let Some(index) = editor.with_buffer(|buffer| {
73 buffer.lines[cursor.line]
74 .text()
75 .match_indices(value)
76 .filter_map(|(i, _)| {
77 if cursor.line != start_line || i > cursor.index {
78 Some(i)
79 } else {
80 None
81 }
82 })
83 .next()
84 }) {
85 cursor.index = index;
86 editor.set_cursor(cursor);
87 return true;
88 }
89
90 cursor.line += 1;
91 }
92 } else {
93 cursor.line += 1;
94 while cursor.line > 0 {
95 cursor.line -= 1;
96
97 if let Some(index) = editor.with_buffer(|buffer| {
98 buffer.lines[cursor.line]
99 .text()
100 .rmatch_indices(value)
101 .filter_map(|(i, _)| {
102 if cursor.line != start_line || i < cursor.index {
103 Some(i)
104 } else {
105 None
106 }
107 })
108 .next()
109 }) {
110 cursor.index = index;
111 editor.set_cursor(cursor);
112 return true;
113 }
114 }
115 }
116 false
117}
118
119fn select_in<'buffer, E: Edit<'buffer>>(editor: &mut E, start_c: char, end_c: char, include: bool) {
120 let cursor = editor.cursor();
122 let (start, end) = editor.with_buffer(|buffer| {
123 let mut end = cursor;
125 let mut starts = 0;
126 let mut ends = 0;
127 'find_end: loop {
128 let line = &buffer.lines[end.line];
129 let text = line.text();
130 for (i, c) in text[end.index..].char_indices() {
131 if c == end_c {
132 ends += 1;
133 } else if c == start_c {
134 starts += 1;
135 }
136 if ends > starts {
137 end.index += if include { i + c.len_utf8() } else { i };
138 break 'find_end;
139 }
140 }
141 if end.line + 1 < buffer.lines.len() {
142 end.line += 1;
143 end.index = 0;
144 } else {
145 break 'find_end;
146 }
147 }
148
149 let mut start = cursor;
151 'find_start: loop {
152 let line = &buffer.lines[start.line];
153 let text = line.text();
154 for (i, c) in text[..start.index].char_indices().rev() {
155 if c == start_c {
156 starts += 1;
157 } else if c == end_c {
158 ends += 1;
159 }
160 if starts >= ends {
161 start.index = if include { i } else { i + c.len_utf8() };
162 break 'find_start;
163 }
164 }
165 if start.line > 0 {
166 start.line -= 1;
167 start.index = buffer.lines[start.line].text().len();
168 } else {
169 break 'find_start;
170 }
171 }
172
173 (start, end)
174 });
175
176 editor.set_selection(Selection::Normal(start));
177 editor.set_cursor(end);
178}
179
180#[derive(Debug)]
181pub struct ViEditor<'syntax_system, 'buffer> {
182 editor: SyntaxEditor<'syntax_system, 'buffer>,
183 parser: ViParser,
184 passthrough: bool,
185 registers: BTreeMap<char, (Selection, String)>,
186 search_opt: Option<(String, bool)>,
187 commands: cosmic_undo_2::Commands<Change>,
188 changed: bool,
189 save_pivot: Option<usize>,
190}
191
192impl<'syntax_system, 'buffer> ViEditor<'syntax_system, 'buffer> {
193 pub fn new(editor: SyntaxEditor<'syntax_system, 'buffer>) -> Self {
194 Self {
195 editor,
196 parser: ViParser::new(),
197 passthrough: false,
198 registers: BTreeMap::new(),
199 search_opt: None,
200 commands: cosmic_undo_2::Commands::new(),
201 changed: false,
202 save_pivot: None,
203 }
204 }
205
206 pub fn update_theme(&mut self, theme_name: &str) -> bool {
208 self.editor.update_theme(theme_name)
209 }
210
211 #[cfg(feature = "std")]
213 pub fn load_text<P: AsRef<std::path::Path>>(
214 &mut self,
215 font_system: &mut FontSystem,
216 path: P,
217 attrs: crate::Attrs,
218 ) -> std::io::Result<()> {
219 self.editor.load_text(font_system, path, attrs)
220 }
221
222 pub fn background_color(&self) -> Color {
224 self.editor.background_color()
225 }
226
227 pub fn foreground_color(&self) -> Color {
229 self.editor.foreground_color()
230 }
231
232 pub fn cursor_color(&self) -> Color {
234 self.editor.cursor_color()
235 }
236
237 pub fn selection_color(&self) -> Color {
239 self.editor.selection_color()
240 }
241
242 pub fn theme(&self) -> &SyntaxTheme {
244 self.editor.theme()
245 }
246
247 pub fn changed(&self) -> bool {
249 self.changed
250 }
251
252 pub fn set_changed(&mut self, changed: bool) {
254 self.changed = changed;
255 }
256
257 pub fn save_point(&mut self) {
267 self.save_pivot = Some(self.commands.current_command_index().unwrap_or_default());
268 self.changed = false;
269 }
270
271 pub fn set_passthrough(&mut self, passthrough: bool) {
273 if passthrough != self.passthrough {
274 self.passthrough = passthrough;
275 self.with_buffer_mut(|buffer| buffer.set_redraw(true));
276 }
277 }
278
279 pub fn parser(&self) -> &ViParser {
281 &self.parser
282 }
283
284 pub fn redo(&mut self) {
286 log::debug!("Redo");
287 for action in self.commands.redo() {
288 undo_2_action(&mut self.editor, action);
289 }
290 self.changed = eval_changed(&self.commands, self.save_pivot);
291 }
292
293 pub fn undo(&mut self) {
295 log::debug!("Undo");
296 for action in self.commands.undo() {
297 undo_2_action(&mut self.editor, action);
298 }
299 self.changed = eval_changed(&self.commands, self.save_pivot);
300 }
301
302 #[cfg(feature = "swash")]
303 pub fn draw<F>(&self, font_system: &mut FontSystem, cache: &mut crate::SwashCache, mut f: F)
304 where
305 F: FnMut(i32, i32, u32, u32, Color),
306 {
307 let background_color = self.background_color();
308 let foreground_color = self.foreground_color();
309 let cursor_color = self.cursor_color();
310 let selection_color = self.selection_color();
311 self.with_buffer(|buffer| {
312 let size = buffer.size();
313 f(0, 0, size.0 as u32, size.1 as u32, background_color);
314 let font_size = buffer.metrics().font_size;
315 let line_height = buffer.metrics().line_height;
316 for run in buffer.layout_runs() {
317 let line_i = run.line_i;
318 let line_y = run.line_y;
319 let line_top = run.line_top;
320
321 let cursor_glyph_opt = |cursor: &Cursor| -> Option<(usize, f32, f32)> {
322 let default_width = font_size / 2.0;
324 if cursor.line == line_i {
325 for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
326 if cursor.index >= glyph.start && cursor.index < glyph.end {
327 let mut before = 0;
329 let mut total = 0;
330
331 let cluster = &run.text[glyph.start..glyph.end];
332 for (i, _) in cluster.grapheme_indices(true) {
333 if glyph.start + i < cursor.index {
334 before += 1;
335 }
336 total += 1;
337 }
338
339 let width = glyph.w / (total as f32);
340 let offset = (before as f32) * width;
341 return Some((glyph_i, offset, width));
342 }
343 }
344 match run.glyphs.last() {
345 Some(glyph) => {
346 if cursor.index == glyph.end {
347 return Some((run.glyphs.len(), 0.0, default_width));
348 }
349 }
350 None => {
351 return Some((0, 0.0, default_width));
352 }
353 }
354 }
355 None
356 };
357
358 if let Some((start, end)) = self.selection_bounds() {
360 if line_i >= start.line && line_i <= end.line {
361 let mut range_opt = None;
362 for glyph in run.glyphs.iter() {
363 let cluster = &run.text[glyph.start..glyph.end];
365 let total = cluster.grapheme_indices(true).count();
366 let mut c_x = glyph.x;
367 let c_w = glyph.w / total as f32;
368 for (i, c) in cluster.grapheme_indices(true) {
369 let c_start = glyph.start + i;
370 let c_end = glyph.start + i + c.len();
371 if (start.line != line_i || c_end > start.index)
372 && (end.line != line_i || c_start < end.index)
373 {
374 range_opt = match range_opt.take() {
375 Some((min, max)) => Some((
376 cmp::min(min, c_x as i32),
377 cmp::max(max, (c_x + c_w) as i32),
378 )),
379 None => Some((c_x as i32, (c_x + c_w) as i32)),
380 };
381 } else if let Some((min, max)) = range_opt.take() {
382 f(
383 min,
384 line_top as i32,
385 cmp::max(0, max - min) as u32,
386 line_height as u32,
387 selection_color,
388 );
389 }
390 c_x += c_w;
391 }
392 }
393
394 if run.glyphs.is_empty() && end.line > line_i {
395 range_opt = Some((0, buffer.size().0 as i32));
397 }
398
399 if let Some((mut min, mut max)) = range_opt.take() {
400 if end.line > line_i {
401 if run.rtl {
403 min = 0;
404 } else {
405 max = buffer.size().0 as i32;
406 }
407 }
408 f(
409 min,
410 line_top as i32,
411 cmp::max(0, max - min) as u32,
412 line_height as u32,
413 selection_color,
414 );
415 }
416 }
417 }
418
419 if let Some((cursor_glyph, cursor_glyph_offset, cursor_glyph_width)) =
421 cursor_glyph_opt(&self.cursor())
422 {
423 let block_cursor = if self.passthrough {
424 false
425 } else {
426 match self.parser.mode {
427 ViMode::Insert | ViMode::Replace => false,
428 _ => true, }
430 };
431
432 let (start_x, end_x) = match run.glyphs.get(cursor_glyph) {
433 Some(glyph) => {
434 if glyph.level.is_rtl() {
436 (
437 (glyph.x + glyph.w - cursor_glyph_offset) as i32,
438 (glyph.x + glyph.w - cursor_glyph_offset - cursor_glyph_width)
439 as i32,
440 )
441 } else {
442 (
443 (glyph.x + cursor_glyph_offset) as i32,
444 (glyph.x + cursor_glyph_offset + cursor_glyph_width) as i32,
445 )
446 }
447 }
448 None => match run.glyphs.last() {
449 Some(glyph) => {
450 if glyph.level.is_rtl() {
452 (glyph.x as i32, (glyph.x - cursor_glyph_width) as i32)
453 } else {
454 (
455 (glyph.x + glyph.w) as i32,
456 (glyph.x + glyph.w + cursor_glyph_width) as i32,
457 )
458 }
459 }
460 None => {
461 (0, cursor_glyph_width as i32)
463 }
464 },
465 };
466
467 if block_cursor {
468 let left_x = cmp::min(start_x, end_x);
469 let right_x = cmp::max(start_x, end_x);
470 f(
471 left_x,
472 line_top as i32,
473 (right_x - left_x) as u32,
474 line_height as u32,
475 selection_color,
476 );
477 } else {
478 f(
479 start_x,
480 line_top as i32,
481 1,
482 line_height as u32,
483 cursor_color,
484 );
485 }
486 }
487
488 for glyph in run.glyphs.iter() {
489 let physical_glyph = glyph.physical((0., 0.), 1.0);
490
491 let glyph_color = match glyph.color_opt {
492 Some(some) => some,
493 None => foreground_color,
494 };
495
496 cache.with_pixels(
497 font_system,
498 physical_glyph.cache_key,
499 glyph_color,
500 |x, y, color| {
501 f(
502 physical_glyph.x + x,
503 line_y as i32 + physical_glyph.y + y,
504 1,
505 1,
506 color,
507 );
508 },
509 );
510 }
511 }
512 });
513 }
514}
515
516impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer> {
517 fn buffer_ref(&self) -> &BufferRef<'buffer> {
518 self.editor.buffer_ref()
519 }
520
521 fn buffer_ref_mut(&mut self) -> &mut BufferRef<'buffer> {
522 self.editor.buffer_ref_mut()
523 }
524
525 fn cursor(&self) -> Cursor {
526 self.editor.cursor()
527 }
528
529 fn set_cursor(&mut self, cursor: Cursor) {
530 self.editor.set_cursor(cursor);
531 }
532
533 fn selection(&self) -> Selection {
534 self.editor.selection()
535 }
536
537 fn set_selection(&mut self, selection: Selection) {
538 self.editor.set_selection(selection);
539 }
540
541 fn auto_indent(&self) -> bool {
542 self.editor.auto_indent()
543 }
544
545 fn set_auto_indent(&mut self, auto_indent: bool) {
546 self.editor.set_auto_indent(auto_indent);
547 }
548
549 fn tab_width(&self) -> u16 {
550 self.editor.tab_width()
551 }
552
553 fn set_tab_width(&mut self, tab_width: u16) {
554 self.editor.set_tab_width(tab_width);
555 }
556
557 fn shape_as_needed(&mut self, font_system: &mut FontSystem, prune: bool) {
558 self.editor.shape_as_needed(font_system, prune);
559 }
560
561 fn delete_range(&mut self, start: Cursor, end: Cursor) {
562 self.editor.delete_range(start, end);
563 }
564
565 fn insert_at(&mut self, cursor: Cursor, data: &str, attrs_list: Option<AttrsList>) -> Cursor {
566 self.editor.insert_at(cursor, data, attrs_list)
567 }
568
569 fn copy_selection(&self) -> Option<String> {
570 self.editor.copy_selection()
571 }
572
573 fn delete_selection(&mut self) -> bool {
574 self.editor.delete_selection()
575 }
576
577 fn apply_change(&mut self, change: &Change) -> bool {
578 self.editor.apply_change(change)
579 }
580
581 fn start_change(&mut self) {
582 self.editor.start_change();
583 }
584
585 fn finish_change(&mut self) -> Option<Change> {
586 finish_change(
587 &mut self.editor,
588 &mut self.commands,
589 &mut self.changed,
590 self.save_pivot,
591 )
592 }
593
594 fn action(&mut self, font_system: &mut FontSystem, action: Action) {
595 log::debug!("Action {:?}", action);
596
597 let editor = &mut self.editor;
598
599 editor.start_change();
601
602 if self.passthrough {
603 editor.action(font_system, action);
604 finish_change(
606 editor,
607 &mut self.commands,
608 &mut self.changed,
609 self.save_pivot,
610 );
611 return;
612 }
613
614 let key = match action {
615 Action::Backspace => Key::Backspace,
617 Action::Delete => Key::Delete,
618 Action::Motion {
619 motion: Motion::Down,
620 ..
621 } => Key::Down,
622 Action::Motion {
623 motion: Motion::End,
624 ..
625 } => Key::End,
626 Action::Enter => Key::Enter,
627 Action::Escape => Key::Escape,
628 Action::Motion {
629 motion: Motion::Home,
630 ..
631 } => Key::Home,
632 Action::Indent => Key::Tab,
633 Action::Insert(c) => Key::Char(c),
634 Action::Motion {
635 motion: Motion::Left,
636 ..
637 } => Key::Left,
638 Action::Motion {
639 motion: Motion::PageDown,
640 ..
641 } => Key::PageDown,
642 Action::Motion {
643 motion: Motion::PageUp,
644 ..
645 } => Key::PageUp,
646 Action::Motion {
647 motion: Motion::Right,
648 ..
649 } => Key::Right,
650 Action::Unindent => Key::Backtab,
651 Action::Motion {
652 motion: Motion::Up, ..
653 } => Key::Up,
654 _ => {
655 log::debug!("Pass through action {:?}", action);
656 editor.action(font_system, action);
657 finish_change(
659 editor,
660 &mut self.commands,
661 &mut self.changed,
662 self.save_pivot,
663 );
664 return;
665 }
666 };
667
668 let has_selection = match editor.selection() {
669 Selection::None => false,
670 _ => true,
671 };
672
673 self.parser.parse(key, has_selection, |event| {
674 log::debug!(" Event {:?}", event);
675 let action = match event {
676 Event::AutoIndent => {
677 log::info!("TODO: AutoIndent");
678 return;
679 }
680 Event::Backspace => Action::Backspace,
681 Event::BackspaceInLine => {
682 let cursor = editor.cursor();
683 if cursor.index > 0 {
684 Action::Backspace
685 } else {
686 return;
687 }
688 }
689 Event::ChangeStart => {
690 editor.start_change();
691 return;
692 }
693 Event::ChangeFinish => {
694 finish_change(
695 editor,
696 &mut self.commands,
697 &mut self.changed,
698 self.save_pivot,
699 );
700 return;
701 }
702 Event::Delete => Action::Delete,
703 Event::DeleteInLine => {
704 let cursor = editor.cursor();
705 if cursor.index
706 < editor.with_buffer(|buffer| buffer.lines[cursor.line].text().len())
707 {
708 Action::Delete
709 } else {
710 return;
711 }
712 }
713 Event::Escape => Action::Escape,
714 Event::Insert(c) => Action::Insert(c),
715 Event::NewLine => Action::Enter,
716 Event::Put { register, after } => {
717 if let Some((selection, data)) = self.registers.get(®ister) {
718 editor.start_change();
719 if editor.delete_selection() {
720 editor.insert_string(data, None);
721 } else {
722 match selection {
723 Selection::None | Selection::Normal(_) | Selection::Word(_) => {
724 let mut cursor = editor.cursor();
725 if after {
726 editor.with_buffer(|buffer| {
727 let text = buffer.lines[cursor.line].text();
728 if let Some(c) = text[cursor.index..].chars().next() {
729 cursor.index += c.len_utf8();
730 }
731 });
732 editor.set_cursor(cursor);
733 }
734 editor.insert_at(cursor, data, None);
735 }
736 Selection::Line(_) => {
737 let mut cursor = editor.cursor();
738 if after {
739 cursor.line += 1;
741 } else {
742 cursor.line += 1;
744 editor.set_cursor(cursor);
745 cursor.line -= 1;
746 }
747 cursor.index = 0;
749
750 editor.insert_at(cursor, "\n", None);
752 editor.insert_at(cursor, data, None);
753
754 editor.shape_as_needed(font_system, false);
756
757 if after {
759 editor.action(
760 font_system,
761 Action::Motion {
762 motion: Motion::Down,
763 select: false,
764 },
765 );
766 } else {
767 editor.action(
768 font_system,
769 Action::Motion {
770 motion: Motion::Up,
771 select: false,
772 },
773 );
774 }
775 }
776 }
777 }
778 finish_change(
779 editor,
780 &mut self.commands,
781 &mut self.changed,
782 self.save_pivot,
783 );
784 }
785 return;
786 }
787 Event::Redraw => {
788 editor.with_buffer_mut(|buffer| buffer.set_redraw(true));
789 return;
790 }
791 Event::SelectClear => {
792 editor.set_selection(Selection::None);
793 return;
794 }
795 Event::SelectStart => {
796 let cursor = editor.cursor();
797 editor.set_selection(Selection::Normal(cursor));
798 return;
799 }
800 Event::SelectLineStart => {
801 let cursor = editor.cursor();
802 editor.set_selection(Selection::Line(cursor));
803 return;
804 }
805 Event::SelectTextObject(text_object, include) => {
806 match text_object {
807 TextObject::AngleBrackets => select_in(editor, '<', '>', include),
808 TextObject::CurlyBrackets => select_in(editor, '{', '}', include),
809 TextObject::DoubleQuotes => select_in(editor, '"', '"', include),
810 TextObject::Parentheses => select_in(editor, '(', ')', include),
811 TextObject::Search { forwards } => {
812 match &self.search_opt {
813 Some((value, _)) => {
814 if search(editor, value, forwards) {
815 let mut cursor = editor.cursor();
816 editor.set_selection(Selection::Normal(cursor));
817 cursor.index += value.len();
819 editor.set_cursor(cursor);
820 }
821 }
822 None => {}
823 }
824 }
825 TextObject::SingleQuotes => select_in(editor, '\'', '\'', include),
826 TextObject::SquareBrackets => select_in(editor, '[', ']', include),
827 TextObject::Ticks => select_in(editor, '`', '`', include),
828 TextObject::Word(word) => {
829 let mut cursor = editor.cursor();
830 let mut selection = editor.selection();
831 editor.with_buffer(|buffer| {
832 let text = buffer.lines[cursor.line].text();
833 match WordIter::new(text, word)
834 .find(|&(i, w)| i <= cursor.index && i + w.len() > cursor.index)
835 {
836 Some((i, w)) => {
837 cursor.index = i;
838 selection = Selection::Normal(cursor);
839 cursor.index += w.len();
840 }
841 None => {
842 }
844 }
845 });
846 editor.set_selection(selection);
847 editor.set_cursor(cursor);
848 }
849 _ => {
850 log::info!("TODO: {:?}", text_object);
851 }
852 }
853 return;
854 }
855 Event::SetSearch(value, forwards) => {
856 self.search_opt = Some((value, forwards));
857 return;
858 }
859 Event::ShiftLeft => Action::Unindent,
860 Event::ShiftRight => Action::Indent,
861 Event::SwapCase => {
862 log::info!("TODO: SwapCase");
863 return;
864 }
865 Event::Undo => {
866 for action in self.commands.undo() {
867 undo_2_action(editor, action);
868 }
869 return;
870 }
871 Event::Yank { register } => {
872 if let Some(data) = editor.copy_selection() {
873 self.registers.insert(register, (editor.selection(), data));
874 }
875 return;
876 }
877 Event::Motion(motion) => {
878 match motion {
879 modit::Motion::Around => {
880 return;
882 }
883 modit::Motion::Down => Action::Motion {
884 motion: Motion::Down,
885 select: false,
886 },
887 modit::Motion::End => Action::Motion {
888 motion: Motion::End,
889 select: false,
890 },
891 modit::Motion::GotoLine(line) => Action::Motion {
892 motion: Motion::GotoLine(line.saturating_sub(1)),
893 select: false,
894 },
895 modit::Motion::GotoEof => Action::Motion {
896 motion: Motion::GotoLine(
897 editor.with_buffer(|buffer| buffer.lines.len().saturating_sub(1)),
898 ),
899 select: false,
900 },
901 modit::Motion::Home => Action::Motion {
902 motion: Motion::Home,
903 select: false,
904 },
905 modit::Motion::Inside => {
906 return;
908 }
909 modit::Motion::Left => Action::Motion {
910 motion: Motion::Left,
911 select: false,
912 },
913 modit::Motion::LeftInLine => {
914 let cursor = editor.cursor();
915 if cursor.index > 0 {
916 Action::Motion {
917 motion: Motion::Left,
918 select: false,
919 }
920 } else {
921 return;
922 }
923 }
924 modit::Motion::Line => {
925 return;
927 }
928 modit::Motion::NextChar(find_c) => {
929 let mut cursor = editor.cursor();
930 editor.with_buffer(|buffer| {
931 let text = buffer.lines[cursor.line].text();
932 if cursor.index < text.len() {
933 match text[cursor.index..]
934 .char_indices()
935 .filter(|&(i, c)| i > 0 && c == find_c)
936 .next()
937 {
938 Some((i, _)) => {
939 cursor.index += i;
940 }
941 None => {}
942 }
943 }
944 });
945 editor.set_cursor(cursor);
946 return;
947 }
948 modit::Motion::NextCharTill(find_c) => {
949 let mut cursor = editor.cursor();
950 editor.with_buffer(|buffer| {
951 let text = buffer.lines[cursor.line].text();
952 if cursor.index < text.len() {
953 let mut last_i = 0;
954 for (i, c) in text[cursor.index..].char_indices() {
955 if last_i > 0 && c == find_c {
956 cursor.index += last_i;
957 break;
958 } else {
959 last_i = i;
960 }
961 }
962 }
963 });
964 editor.set_cursor(cursor);
965 return;
966 }
967 modit::Motion::NextSearch => match &self.search_opt {
968 Some((value, forwards)) => {
969 search(editor, value, *forwards);
970 return;
971 }
972 None => return,
973 },
974 modit::Motion::NextWordEnd(word) => {
975 let mut cursor = editor.cursor();
976 editor.with_buffer(|buffer| {
977 loop {
978 let text = buffer.lines[cursor.line].text();
979 if cursor.index < text.len() {
980 cursor.index = WordIter::new(text, word)
981 .map(|(i, w)| {
982 i + w
983 .char_indices()
984 .last()
985 .map(|(i, _)| i)
986 .unwrap_or(0)
987 })
988 .find(|&i| i > cursor.index)
989 .unwrap_or(text.len());
990 if cursor.index == text.len() {
991 continue;
993 }
994 } else if cursor.line + 1 < buffer.lines.len() {
995 cursor.line += 1;
997 cursor.index = 0;
998 continue;
999 }
1000 break;
1001 }
1002 });
1003 editor.set_cursor(cursor);
1004 return;
1005 }
1006 modit::Motion::NextWordStart(word) => {
1007 let mut cursor = editor.cursor();
1008 editor.with_buffer(|buffer| {
1009 loop {
1010 let text = buffer.lines[cursor.line].text();
1011 if cursor.index < text.len() {
1012 cursor.index = WordIter::new(text, word)
1013 .map(|(i, _)| i)
1014 .find(|&i| i > cursor.index)
1015 .unwrap_or(text.len());
1016 if cursor.index == text.len() {
1017 continue;
1019 }
1020 } else if cursor.line + 1 < buffer.lines.len() {
1021 cursor.line += 1;
1023 cursor.index = 0;
1024 continue;
1025 }
1026 break;
1027 }
1028 });
1029 editor.set_cursor(cursor);
1030 return;
1031 }
1032 modit::Motion::PageDown => Action::Motion {
1033 motion: Motion::PageDown,
1034 select: false,
1035 },
1036 modit::Motion::PageUp => Action::Motion {
1037 motion: Motion::PageUp,
1038 select: false,
1039 },
1040 modit::Motion::PreviousChar(find_c) => {
1041 let mut cursor = editor.cursor();
1042 editor.with_buffer(|buffer| {
1043 let text = buffer.lines[cursor.line].text();
1044 if cursor.index > 0 {
1045 match text[..cursor.index]
1046 .char_indices()
1047 .filter(|&(_, c)| c == find_c)
1048 .last()
1049 {
1050 Some((i, _)) => {
1051 cursor.index = i;
1052 }
1053 None => {}
1054 }
1055 }
1056 });
1057 editor.set_cursor(cursor);
1058 return;
1059 }
1060 modit::Motion::PreviousCharTill(find_c) => {
1061 let mut cursor = editor.cursor();
1062 editor.with_buffer(|buffer| {
1063 let text = buffer.lines[cursor.line].text();
1064 if cursor.index > 0 {
1065 match text[..cursor.index]
1066 .char_indices()
1067 .filter_map(|(i, c)| {
1068 if c == find_c {
1069 let end = i + c.len_utf8();
1070 if end < cursor.index {
1071 return Some(end);
1072 }
1073 }
1074 None
1075 })
1076 .last()
1077 {
1078 Some(i) => {
1079 cursor.index = i;
1080 }
1081 None => {}
1082 }
1083 }
1084 });
1085 editor.set_cursor(cursor);
1086 return;
1087 }
1088 modit::Motion::PreviousSearch => match &self.search_opt {
1089 Some((value, forwards)) => {
1090 search(editor, value, !*forwards);
1091 return;
1092 }
1093 None => return,
1094 },
1095 modit::Motion::PreviousWordEnd(word) => {
1096 let mut cursor = editor.cursor();
1097 editor.with_buffer(|buffer| {
1098 loop {
1099 let text = buffer.lines[cursor.line].text();
1100 if cursor.index > 0 {
1101 cursor.index = WordIter::new(text, word)
1102 .map(|(i, w)| {
1103 i + w
1104 .char_indices()
1105 .last()
1106 .map(|(i, _)| i)
1107 .unwrap_or(0)
1108 })
1109 .filter(|&i| i < cursor.index)
1110 .last()
1111 .unwrap_or(0);
1112 if cursor.index == 0 {
1113 continue;
1115 }
1116 } else if cursor.line > 0 {
1117 cursor.line -= 1;
1119 cursor.index = buffer.lines[cursor.line].text().len();
1120 continue;
1121 }
1122 break;
1123 }
1124 });
1125 editor.set_cursor(cursor);
1126 return;
1127 }
1128 modit::Motion::PreviousWordStart(word) => {
1129 let mut cursor = editor.cursor();
1130 editor.with_buffer(|buffer| {
1131 loop {
1132 let text = buffer.lines[cursor.line].text();
1133 if cursor.index > 0 {
1134 cursor.index = WordIter::new(text, word)
1135 .map(|(i, _)| i)
1136 .filter(|&i| i < cursor.index)
1137 .last()
1138 .unwrap_or(0);
1139 if cursor.index == 0 {
1140 continue;
1142 }
1143 } else if cursor.line > 0 {
1144 cursor.line -= 1;
1146 cursor.index = buffer.lines[cursor.line].text().len();
1147 continue;
1148 }
1149 break;
1150 }
1151 });
1152 editor.set_cursor(cursor);
1153 return;
1154 }
1155 modit::Motion::Right => Action::Motion {
1156 motion: Motion::Right,
1157 select: false,
1158 },
1159 modit::Motion::RightInLine => {
1160 let cursor = editor.cursor();
1161 if cursor.index
1162 < editor
1163 .with_buffer(|buffer| buffer.lines[cursor.line].text().len())
1164 {
1165 Action::Motion {
1166 motion: Motion::Right,
1167 select: false,
1168 }
1169 } else {
1170 return;
1171 }
1172 }
1173 modit::Motion::ScreenHigh => {
1174 if let Some(line_i) = editor.with_buffer(|buffer| {
1176 buffer.layout_runs().next().map(|first| first.line_i)
1177 }) {
1178 Action::Motion {
1179 motion: Motion::GotoLine(line_i),
1180 select: false,
1181 }
1182 } else {
1183 return;
1184 }
1185 }
1186 modit::Motion::ScreenLow => {
1187 if let Some(line_i) = editor.with_buffer(|buffer| {
1189 buffer.layout_runs().last().map(|last| last.line_i)
1190 }) {
1191 Action::Motion {
1192 motion: Motion::GotoLine(line_i),
1193 select: false,
1194 }
1195 } else {
1196 return;
1197 }
1198 }
1199 modit::Motion::ScreenMiddle => {
1200 let action_opt = editor.with_buffer(|buffer| {
1202 let mut layout_runs = buffer.layout_runs();
1203 if let Some(first) = layout_runs.next() {
1204 if let Some(last) = layout_runs.last() {
1205 Some(Action::Motion {
1206 motion: Motion::GotoLine(
1207 (last.line_i + first.line_i) / 2,
1208 ),
1209 select: false,
1210 })
1211 } else {
1212 None
1213 }
1214 } else {
1215 None
1216 }
1217 });
1218 match action_opt {
1219 Some(action) => action,
1220 None => return,
1221 }
1222 }
1223 modit::Motion::Selection => {
1224 return;
1226 }
1227 modit::Motion::SoftHome => Action::Motion {
1228 motion: Motion::SoftHome,
1229 select: false,
1230 },
1231 modit::Motion::Up => Action::Motion {
1232 motion: Motion::Up,
1233 select: false,
1234 },
1235 }
1236 }
1237 };
1238 editor.action(font_system, action);
1239 });
1240 }
1241
1242 fn preedit_range(&self) -> Option<core::ops::Range<usize>> {
1243 self.editor.preedit_range()
1244 }
1245
1246 fn preedit_text(&self) -> Option<String> {
1247 self.editor.preedit_text()
1248 }
1249
1250 fn cursor_position(&self) -> Option<(i32, i32)> {
1251 self.editor.cursor_position()
1252 }
1253
1254 fn set_cursor_hidden(&mut self, hidden: bool) {
1255 self.editor.set_cursor_hidden(hidden);
1256 }
1257}
1258
1259impl<'font_system, 'syntax_system, 'buffer>
1260 BorrowedWithFontSystem<'font_system, ViEditor<'syntax_system, 'buffer>>
1261{
1262 #[cfg(feature = "std")]
1264 pub fn load_text<P: AsRef<std::path::Path>>(
1265 &mut self,
1266 path: P,
1267 attrs: crate::Attrs,
1268 ) -> std::io::Result<()> {
1269 self.inner.load_text(self.font_system, path, attrs)
1270 }
1271
1272 #[cfg(feature = "swash")]
1273 pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, f: F)
1274 where
1275 F: FnMut(i32, i32, u32, u32, Color),
1276 {
1277 self.inner.draw(self.font_system, cache, f);
1278 }
1279}