1use std::sync::Arc;
4
5use parking_lot::Mutex;
6
7use anyhow::Result;
8
9use frontend::commands::{
10 document_editing_commands, document_formatting_commands, document_inspection_commands,
11 inline_element_commands, undo_redo_commands,
12};
13use crate::ListStyle;
14
15use unicode_segmentation::UnicodeSegmentation;
16
17use crate::convert::{to_i64, to_usize};
18use crate::events::DocumentEvent;
19use crate::fragment::DocumentFragment;
20use crate::inner::{CursorData, TextDocumentInner};
21use crate::{BlockFormat, FrameFormat, MoveMode, MoveOperation, SelectionType, TextFormat};
22
23pub struct TextCursor {
31 pub(crate) doc: Arc<Mutex<TextDocumentInner>>,
32 pub(crate) data: Arc<Mutex<CursorData>>,
33}
34
35impl Clone for TextCursor {
36 fn clone(&self) -> Self {
37 let (position, anchor) = {
38 let d = self.data.lock();
39 (d.position, d.anchor)
40 };
41 let data = {
42 let mut inner = self.doc.lock();
43 let data = Arc::new(Mutex::new(CursorData { position, anchor }));
44 inner.cursors.push(Arc::downgrade(&data));
45 data
46 };
47 TextCursor {
48 doc: self.doc.clone(),
49 data,
50 }
51 }
52}
53
54impl TextCursor {
55 fn read_cursor(&self) -> (usize, usize) {
58 let d = self.data.lock();
59 (d.position, d.anchor)
60 }
61
62 fn finish_edit(
66 &self,
67 inner: &mut TextDocumentInner,
68 edit_pos: usize,
69 removed: usize,
70 new_pos: usize,
71 blocks_affected: usize,
72 ) -> Vec<(DocumentEvent, Vec<Arc<dyn Fn(DocumentEvent) + Send + Sync>>)> {
73 let added = new_pos - edit_pos;
74 inner.adjust_cursors(edit_pos, removed, added);
75 {
76 let mut d = self.data.lock();
77 d.position = new_pos;
78 d.anchor = new_pos;
79 }
80 inner.modified = true;
81 inner.invalidate_text_cache();
82 inner.queue_event(DocumentEvent::ContentsChanged {
83 position: edit_pos,
84 chars_removed: removed,
85 chars_added: added,
86 blocks_affected,
87 });
88 inner.check_block_count_changed();
89 self.queue_undo_redo_event(inner)
90 }
91
92 pub fn position(&self) -> usize {
96 self.data.lock().position
97 }
98
99 pub fn anchor(&self) -> usize {
101 self.data.lock().anchor
102 }
103
104 pub fn has_selection(&self) -> bool {
106 let d = self.data.lock();
107 d.position != d.anchor
108 }
109
110 pub fn selection_start(&self) -> usize {
112 let d = self.data.lock();
113 d.position.min(d.anchor)
114 }
115
116 pub fn selection_end(&self) -> usize {
118 let d = self.data.lock();
119 d.position.max(d.anchor)
120 }
121
122 pub fn selected_text(&self) -> Result<String> {
124 let (pos, anchor) = self.read_cursor();
125 if pos == anchor {
126 return Ok(String::new());
127 }
128 let start = pos.min(anchor);
129 let len = pos.max(anchor) - start;
130 let inner = self.doc.lock();
131 let dto = frontend::document_inspection::GetTextAtPositionDto {
132 position: to_i64(start),
133 length: to_i64(len),
134 };
135 let result = document_inspection_commands::get_text_at_position(&inner.ctx, &dto)?;
136 Ok(result.text)
137 }
138
139 pub fn clear_selection(&self) {
141 let mut d = self.data.lock();
142 d.anchor = d.position;
143 }
144
145 pub fn at_block_start(&self) -> bool {
149 let pos = self.position();
150 let inner = self.doc.lock();
151 let dto = frontend::document_inspection::GetBlockAtPositionDto {
152 position: to_i64(pos),
153 };
154 if let Ok(info) = document_inspection_commands::get_block_at_position(&inner.ctx, &dto) {
155 pos == to_usize(info.block_start)
156 } else {
157 false
158 }
159 }
160
161 pub fn at_block_end(&self) -> bool {
163 let pos = self.position();
164 let inner = self.doc.lock();
165 let dto = frontend::document_inspection::GetBlockAtPositionDto {
166 position: to_i64(pos),
167 };
168 if let Ok(info) = document_inspection_commands::get_block_at_position(&inner.ctx, &dto) {
169 pos == to_usize(info.block_start) + to_usize(info.block_length)
170 } else {
171 false
172 }
173 }
174
175 pub fn at_start(&self) -> bool {
177 self.data.lock().position == 0
178 }
179
180 pub fn at_end(&self) -> bool {
182 let pos = self.position();
183 let inner = self.doc.lock();
184 let stats =
185 document_inspection_commands::get_document_stats(&inner.ctx).unwrap_or({
186 frontend::document_inspection::DocumentStatsDto {
187 character_count: 0,
188 word_count: 0,
189 block_count: 0,
190 frame_count: 0,
191 image_count: 0,
192 list_count: 0,
193 }
194 });
195 pos >= to_usize(stats.character_count)
196 }
197
198 pub fn block_number(&self) -> usize {
200 let pos = self.position();
201 let inner = self.doc.lock();
202 let dto = frontend::document_inspection::GetBlockAtPositionDto {
203 position: to_i64(pos),
204 };
205 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
206 .map(|info| to_usize(info.block_number))
207 .unwrap_or(0)
208 }
209
210 pub fn position_in_block(&self) -> usize {
212 let pos = self.position();
213 let inner = self.doc.lock();
214 let dto = frontend::document_inspection::GetBlockAtPositionDto {
215 position: to_i64(pos),
216 };
217 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
218 .map(|info| pos.saturating_sub(to_usize(info.block_start)))
219 .unwrap_or(0)
220 }
221
222 pub fn set_position(&self, position: usize, mode: MoveMode) {
226 let end = {
228 let inner = self.doc.lock();
229 document_inspection_commands::get_document_stats(&inner.ctx)
230 .map(|s| to_usize(s.character_count))
231 .unwrap_or(0)
232 };
233 let pos = position.min(end);
234 let mut d = self.data.lock();
235 d.position = pos;
236 if mode == MoveMode::MoveAnchor {
237 d.anchor = pos;
238 }
239 }
240
241 pub fn move_position(&self, operation: MoveOperation, mode: MoveMode, n: usize) -> bool {
247 let old_pos = self.position();
248 let target = self.resolve_move(operation, n);
249 self.set_position(target, mode);
250 self.position() != old_pos
251 }
252
253 pub fn select(&self, selection: SelectionType) {
255 match selection {
256 SelectionType::Document => {
257 let end = {
258 let inner = self.doc.lock();
259 document_inspection_commands::get_document_stats(&inner.ctx)
260 .map(|s| to_usize(s.character_count))
261 .unwrap_or(0)
262 };
263 let mut d = self.data.lock();
264 d.anchor = 0;
265 d.position = end;
266 }
267 SelectionType::BlockUnderCursor | SelectionType::LineUnderCursor => {
268 let pos = self.position();
269 let inner = self.doc.lock();
270 let dto = frontend::document_inspection::GetBlockAtPositionDto {
271 position: to_i64(pos),
272 };
273 if let Ok(info) =
274 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
275 {
276 let start = to_usize(info.block_start);
277 let end = start + to_usize(info.block_length);
278 drop(inner);
279 let mut d = self.data.lock();
280 d.anchor = start;
281 d.position = end;
282 }
283 }
284 SelectionType::WordUnderCursor => {
285 let pos = self.position();
286 let (word_start, word_end) = self.find_word_boundaries(pos);
287 let mut d = self.data.lock();
288 d.anchor = word_start;
289 d.position = word_end;
290 }
291 }
292 }
293
294 pub fn insert_text(&self, text: &str) -> Result<()> {
298 let (pos, anchor) = self.read_cursor();
299
300 let dto = frontend::document_editing::InsertTextDto {
302 position: to_i64(pos),
303 anchor: to_i64(anchor),
304 text: text.into(),
305 };
306
307 let queued = {
308 let mut inner = self.doc.lock();
309 let result = match document_editing_commands::insert_text(
310 &inner.ctx,
311 Some(inner.stack_id),
312 &dto,
313 ) {
314 Ok(r) => r,
315 Err(_) if pos != anchor => {
316 undo_redo_commands::begin_composite(&inner.ctx, Some(inner.stack_id));
318
319 let del_dto = frontend::document_editing::DeleteTextDto {
320 position: to_i64(pos),
321 anchor: to_i64(anchor),
322 };
323 let del_result = document_editing_commands::delete_text(
324 &inner.ctx,
325 Some(inner.stack_id),
326 &del_dto,
327 )?;
328 let del_pos = to_usize(del_result.new_position);
329
330 let ins_dto = frontend::document_editing::InsertTextDto {
331 position: to_i64(del_pos),
332 anchor: to_i64(del_pos),
333 text: text.into(),
334 };
335 let ins_result = document_editing_commands::insert_text(
336 &inner.ctx,
337 Some(inner.stack_id),
338 &ins_dto,
339 )?;
340
341 undo_redo_commands::end_composite(&inner.ctx);
342 ins_result
343 }
344 Err(e) => return Err(e),
345 };
346
347 let edit_pos = pos.min(anchor);
348 let removed = pos.max(anchor) - edit_pos;
349 self.finish_edit(
350 &mut inner,
351 edit_pos,
352 removed,
353 to_usize(result.new_position),
354 to_usize(result.blocks_affected),
355 )
356 };
357 crate::inner::dispatch_queued_events(queued);
358 Ok(())
359 }
360
361 pub fn insert_formatted_text(&self, text: &str, format: &TextFormat) -> Result<()> {
363 let (pos, anchor) = self.read_cursor();
364 let queued = {
365 let mut inner = self.doc.lock();
366 let dto = frontend::document_editing::InsertFormattedTextDto {
367 position: to_i64(pos),
368 anchor: to_i64(anchor),
369 text: text.into(),
370 font_family: format.font_family.clone().unwrap_or_default(),
371 font_point_size: format.font_point_size.map(|v| v as i64).unwrap_or(0),
372 font_bold: format.font_bold.unwrap_or(false),
373 font_italic: format.font_italic.unwrap_or(false),
374 font_underline: format.font_underline.unwrap_or(false),
375 font_strikeout: format.font_strikeout.unwrap_or(false),
376 };
377 let result = document_editing_commands::insert_formatted_text(
378 &inner.ctx,
379 Some(inner.stack_id),
380 &dto,
381 )?;
382 let edit_pos = pos.min(anchor);
383 let removed = pos.max(anchor) - edit_pos;
384 self.finish_edit(&mut inner, edit_pos, removed, to_usize(result.new_position), 1)
385 };
386 crate::inner::dispatch_queued_events(queued);
387 Ok(())
388 }
389
390 pub fn insert_block(&self) -> Result<()> {
392 let (pos, anchor) = self.read_cursor();
393 let queued = {
394 let mut inner = self.doc.lock();
395 let dto = frontend::document_editing::InsertBlockDto {
396 position: to_i64(pos),
397 anchor: to_i64(anchor),
398 };
399 let result =
400 document_editing_commands::insert_block(&inner.ctx, Some(inner.stack_id), &dto)?;
401 let edit_pos = pos.min(anchor);
402 let removed = pos.max(anchor) - edit_pos;
403 self.finish_edit(&mut inner, edit_pos, removed, to_usize(result.new_position), 2)
404 };
405 crate::inner::dispatch_queued_events(queued);
406 Ok(())
407 }
408
409 pub fn insert_html(&self, html: &str) -> Result<()> {
411 let (pos, anchor) = self.read_cursor();
412 let queued = {
413 let mut inner = self.doc.lock();
414 let dto = frontend::document_editing::InsertHtmlAtPositionDto {
415 position: to_i64(pos),
416 anchor: to_i64(anchor),
417 html: html.into(),
418 };
419 let result = document_editing_commands::insert_html_at_position(
420 &inner.ctx,
421 Some(inner.stack_id),
422 &dto,
423 )?;
424 let edit_pos = pos.min(anchor);
425 let removed = pos.max(anchor) - edit_pos;
426 self.finish_edit(
427 &mut inner,
428 edit_pos,
429 removed,
430 to_usize(result.new_position),
431 to_usize(result.blocks_added),
432 )
433 };
434 crate::inner::dispatch_queued_events(queued);
435 Ok(())
436 }
437
438 pub fn insert_markdown(&self, markdown: &str) -> Result<()> {
440 let (pos, anchor) = self.read_cursor();
441 let queued = {
442 let mut inner = self.doc.lock();
443 let dto = frontend::document_editing::InsertMarkdownAtPositionDto {
444 position: to_i64(pos),
445 anchor: to_i64(anchor),
446 markdown: markdown.into(),
447 };
448 let result = document_editing_commands::insert_markdown_at_position(
449 &inner.ctx,
450 Some(inner.stack_id),
451 &dto,
452 )?;
453 let edit_pos = pos.min(anchor);
454 let removed = pos.max(anchor) - edit_pos;
455 self.finish_edit(
456 &mut inner,
457 edit_pos,
458 removed,
459 to_usize(result.new_position),
460 to_usize(result.blocks_added),
461 )
462 };
463 crate::inner::dispatch_queued_events(queued);
464 Ok(())
465 }
466
467 pub fn insert_fragment(&self, fragment: &DocumentFragment) -> Result<()> {
469 let (pos, anchor) = self.read_cursor();
470 let queued = {
471 let mut inner = self.doc.lock();
472 let dto = frontend::document_editing::InsertFragmentDto {
473 position: to_i64(pos),
474 anchor: to_i64(anchor),
475 fragment_data: fragment.raw_data().into(),
476 };
477 let result =
478 document_editing_commands::insert_fragment(&inner.ctx, Some(inner.stack_id), &dto)?;
479 let edit_pos = pos.min(anchor);
480 let removed = pos.max(anchor) - edit_pos;
481 self.finish_edit(
482 &mut inner,
483 edit_pos,
484 removed,
485 to_usize(result.new_position),
486 to_usize(result.blocks_added),
487 )
488 };
489 crate::inner::dispatch_queued_events(queued);
490 Ok(())
491 }
492
493 pub fn selection(&self) -> DocumentFragment {
495 let (pos, anchor) = self.read_cursor();
496 if pos == anchor {
497 return DocumentFragment::new();
498 }
499 let inner = self.doc.lock();
500 let dto = frontend::document_inspection::ExtractFragmentDto {
501 position: to_i64(pos),
502 anchor: to_i64(anchor),
503 };
504 match document_inspection_commands::extract_fragment(&inner.ctx, &dto) {
505 Ok(result) => DocumentFragment::from_raw(result.fragment_data, result.plain_text),
506 Err(_) => DocumentFragment::new(),
507 }
508 }
509
510 pub fn insert_image(&self, name: &str, width: u32, height: u32) -> Result<()> {
512 let (pos, anchor) = self.read_cursor();
513 let queued = {
514 let mut inner = self.doc.lock();
515 let dto = frontend::document_editing::InsertImageDto {
516 position: to_i64(pos),
517 anchor: to_i64(anchor),
518 image_name: name.into(),
519 width: width as i64,
520 height: height as i64,
521 };
522 let result =
523 document_editing_commands::insert_image(&inner.ctx, Some(inner.stack_id), &dto)?;
524 let edit_pos = pos.min(anchor);
525 let removed = pos.max(anchor) - edit_pos;
526 self.finish_edit(&mut inner, edit_pos, removed, to_usize(result.new_position), 1)
527 };
528 crate::inner::dispatch_queued_events(queued);
529 Ok(())
530 }
531
532 pub fn insert_frame(&self) -> Result<()> {
534 let (pos, anchor) = self.read_cursor();
535 let queued = {
536 let mut inner = self.doc.lock();
537 let dto = frontend::document_editing::InsertFrameDto {
538 position: to_i64(pos),
539 anchor: to_i64(anchor),
540 };
541 document_editing_commands::insert_frame(&inner.ctx, Some(inner.stack_id), &dto)?;
542 inner.modified = true;
545 inner.invalidate_text_cache();
546 inner.queue_event(DocumentEvent::ContentsChanged {
547 position: pos.min(anchor),
548 chars_removed: 0,
549 chars_added: 0,
550 blocks_affected: 1,
551 });
552 inner.check_block_count_changed();
553 self.queue_undo_redo_event(&mut inner)
554 };
555 crate::inner::dispatch_queued_events(queued);
556 Ok(())
557 }
558
559 pub fn delete_char(&self) -> Result<()> {
561 let (pos, anchor) = self.read_cursor();
562 let (del_pos, del_anchor) = if pos != anchor {
563 (pos, anchor)
564 } else {
565 (pos, pos + 1)
566 };
567 self.do_delete(del_pos, del_anchor)
568 }
569
570 pub fn delete_previous_char(&self) -> Result<()> {
572 let (pos, anchor) = self.read_cursor();
573 let (del_pos, del_anchor) = if pos != anchor {
574 (pos, anchor)
575 } else if pos > 0 {
576 (pos - 1, pos)
577 } else {
578 return Ok(());
579 };
580 self.do_delete(del_pos, del_anchor)
581 }
582
583 pub fn remove_selected_text(&self) -> Result<String> {
585 let (pos, anchor) = self.read_cursor();
586 if pos == anchor {
587 return Ok(String::new());
588 }
589 let queued = {
590 let mut inner = self.doc.lock();
591 let dto = frontend::document_editing::DeleteTextDto {
592 position: to_i64(pos),
593 anchor: to_i64(anchor),
594 };
595 let result =
596 document_editing_commands::delete_text(&inner.ctx, Some(inner.stack_id), &dto)?;
597 let edit_pos = pos.min(anchor);
598 let removed = pos.max(anchor) - edit_pos;
599 let new_pos = to_usize(result.new_position);
600 inner.adjust_cursors(edit_pos, removed, 0);
601 {
602 let mut d = self.data.lock();
603 d.position = new_pos;
604 d.anchor = new_pos;
605 }
606 inner.modified = true;
607 inner.invalidate_text_cache();
608 inner.queue_event(DocumentEvent::ContentsChanged {
609 position: edit_pos,
610 chars_removed: removed,
611 chars_added: 0,
612 blocks_affected: 1,
613 });
614 inner.check_block_count_changed();
615 (result.deleted_text, self.queue_undo_redo_event(&mut inner))
617 };
618 crate::inner::dispatch_queued_events(queued.1);
619 Ok(queued.0)
620 }
621
622 pub fn create_list(&self, style: ListStyle) -> Result<()> {
626 let (pos, anchor) = self.read_cursor();
627 let queued = {
628 let mut inner = self.doc.lock();
629 let dto = frontend::document_editing::CreateListDto {
630 position: to_i64(pos),
631 anchor: to_i64(anchor),
632 style: style.clone(),
633 };
634 document_editing_commands::create_list(&inner.ctx, Some(inner.stack_id), &dto)?;
635 inner.modified = true;
636 inner.queue_event(DocumentEvent::ContentsChanged {
637 position: pos.min(anchor),
638 chars_removed: 0,
639 chars_added: 0,
640 blocks_affected: 1,
641 });
642 self.queue_undo_redo_event(&mut inner)
643 };
644 crate::inner::dispatch_queued_events(queued);
645 Ok(())
646 }
647
648 pub fn insert_list(&self, style: ListStyle) -> Result<()> {
650 let (pos, anchor) = self.read_cursor();
651 let queued = {
652 let mut inner = self.doc.lock();
653 let dto = frontend::document_editing::InsertListDto {
654 position: to_i64(pos),
655 anchor: to_i64(anchor),
656 style: style.clone(),
657 };
658 let result =
659 document_editing_commands::insert_list(&inner.ctx, Some(inner.stack_id), &dto)?;
660 let edit_pos = pos.min(anchor);
661 let removed = pos.max(anchor) - edit_pos;
662 self.finish_edit(&mut inner, edit_pos, removed, to_usize(result.new_position), 1)
663 };
664 crate::inner::dispatch_queued_events(queued);
665 Ok(())
666 }
667
668 pub fn char_format(&self) -> Result<TextFormat> {
672 let pos = self.position();
673 let inner = self.doc.lock();
674 let dto = frontend::document_inspection::GetTextAtPositionDto {
675 position: to_i64(pos),
676 length: 1,
677 };
678 let text_info = document_inspection_commands::get_text_at_position(&inner.ctx, &dto)?;
679 let element_id = text_info.element_id as u64;
680 let element = inline_element_commands::get_inline_element(&inner.ctx, &element_id)?
681 .ok_or_else(|| anyhow::anyhow!("element not found at position"))?;
682 Ok(TextFormat::from(&element))
683 }
684
685 pub fn block_format(&self) -> Result<BlockFormat> {
687 let pos = self.position();
688 let inner = self.doc.lock();
689 let dto = frontend::document_inspection::GetBlockAtPositionDto {
690 position: to_i64(pos),
691 };
692 let block_info = document_inspection_commands::get_block_at_position(&inner.ctx, &dto)?;
693 let block_id = block_info.block_id as u64;
694 let block = frontend::commands::block_commands::get_block(&inner.ctx, &block_id)?
695 .ok_or_else(|| anyhow::anyhow!("block not found"))?;
696 Ok(BlockFormat::from(&block))
697 }
698
699 pub fn set_char_format(&self, format: &TextFormat) -> Result<()> {
703 let (pos, anchor) = self.read_cursor();
704 let queued = {
705 let mut inner = self.doc.lock();
706 let dto = format.to_set_dto(pos, anchor);
707 document_formatting_commands::set_text_format(&inner.ctx, Some(inner.stack_id), &dto)?;
708 let start = pos.min(anchor);
709 let length = pos.max(anchor) - start;
710 inner.modified = true;
711 inner.queue_event(DocumentEvent::FormatChanged {
712 position: start,
713 length,
714 });
715 self.queue_undo_redo_event(&mut inner)
716 };
717 crate::inner::dispatch_queued_events(queued);
718 Ok(())
719 }
720
721 pub fn merge_char_format(&self, format: &TextFormat) -> Result<()> {
723 let (pos, anchor) = self.read_cursor();
724 let queued = {
725 let mut inner = self.doc.lock();
726 let dto = format.to_merge_dto(pos, anchor);
727 document_formatting_commands::merge_text_format(
728 &inner.ctx,
729 Some(inner.stack_id),
730 &dto,
731 )?;
732 let start = pos.min(anchor);
733 let length = pos.max(anchor) - start;
734 inner.modified = true;
735 inner.queue_event(DocumentEvent::FormatChanged {
736 position: start,
737 length,
738 });
739 self.queue_undo_redo_event(&mut inner)
740 };
741 crate::inner::dispatch_queued_events(queued);
742 Ok(())
743 }
744
745 pub fn set_block_format(&self, format: &BlockFormat) -> Result<()> {
747 let (pos, anchor) = self.read_cursor();
748 let queued = {
749 let mut inner = self.doc.lock();
750 let dto = format.to_set_dto(pos, anchor);
751 document_formatting_commands::set_block_format(
752 &inner.ctx,
753 Some(inner.stack_id),
754 &dto,
755 )?;
756 let start = pos.min(anchor);
757 let length = pos.max(anchor) - start;
758 inner.modified = true;
759 inner.queue_event(DocumentEvent::FormatChanged {
760 position: start,
761 length,
762 });
763 self.queue_undo_redo_event(&mut inner)
764 };
765 crate::inner::dispatch_queued_events(queued);
766 Ok(())
767 }
768
769 pub fn set_frame_format(&self, frame_id: usize, format: &FrameFormat) -> Result<()> {
771 let (pos, anchor) = self.read_cursor();
772 let queued = {
773 let mut inner = self.doc.lock();
774 let dto = format.to_set_dto(pos, anchor, frame_id);
775 document_formatting_commands::set_frame_format(
776 &inner.ctx,
777 Some(inner.stack_id),
778 &dto,
779 )?;
780 let start = pos.min(anchor);
781 let length = pos.max(anchor) - start;
782 inner.modified = true;
783 inner.queue_event(DocumentEvent::FormatChanged {
784 position: start,
785 length,
786 });
787 self.queue_undo_redo_event(&mut inner)
788 };
789 crate::inner::dispatch_queued_events(queued);
790 Ok(())
791 }
792
793 pub fn begin_edit_block(&self) {
797 let inner = self.doc.lock();
798 undo_redo_commands::begin_composite(&inner.ctx, Some(inner.stack_id));
799 }
800
801 pub fn end_edit_block(&self) {
803 let inner = self.doc.lock();
804 undo_redo_commands::end_composite(&inner.ctx);
805 }
806
807 pub fn join_previous_edit_block(&self) {
814 self.begin_edit_block();
815 }
816
817 fn queue_undo_redo_event(
821 &self,
822 inner: &mut TextDocumentInner,
823 ) -> Vec<(DocumentEvent, Vec<Arc<dyn Fn(DocumentEvent) + Send + Sync>>)> {
824 let can_undo =
825 undo_redo_commands::can_undo(&inner.ctx, Some(inner.stack_id));
826 let can_redo =
827 undo_redo_commands::can_redo(&inner.ctx, Some(inner.stack_id));
828 inner.queue_event(DocumentEvent::UndoRedoChanged { can_undo, can_redo });
829 inner.take_queued_events()
830 }
831
832 fn do_delete(&self, pos: usize, anchor: usize) -> Result<()> {
833 let queued = {
834 let mut inner = self.doc.lock();
835 let dto = frontend::document_editing::DeleteTextDto {
836 position: to_i64(pos),
837 anchor: to_i64(anchor),
838 };
839 let result =
840 document_editing_commands::delete_text(&inner.ctx, Some(inner.stack_id), &dto)?;
841 let edit_pos = pos.min(anchor);
842 let removed = pos.max(anchor) - edit_pos;
843 let new_pos = to_usize(result.new_position);
844 inner.adjust_cursors(edit_pos, removed, 0);
845 {
846 let mut d = self.data.lock();
847 d.position = new_pos;
848 d.anchor = new_pos;
849 }
850 inner.modified = true;
851 inner.invalidate_text_cache();
852 inner.queue_event(DocumentEvent::ContentsChanged {
853 position: edit_pos,
854 chars_removed: removed,
855 chars_added: 0,
856 blocks_affected: 1,
857 });
858 inner.check_block_count_changed();
859 self.queue_undo_redo_event(&mut inner)
860 };
861 crate::inner::dispatch_queued_events(queued);
862 Ok(())
863 }
864
865 fn resolve_move(&self, op: MoveOperation, n: usize) -> usize {
867 let pos = self.position();
868 match op {
869 MoveOperation::NoMove => pos,
870 MoveOperation::Start => 0,
871 MoveOperation::End => {
872 let inner = self.doc.lock();
873 document_inspection_commands::get_document_stats(&inner.ctx)
874 .map(|s| to_usize(s.character_count))
875 .unwrap_or(pos)
876 }
877 MoveOperation::NextCharacter | MoveOperation::Right => pos + n,
878 MoveOperation::PreviousCharacter | MoveOperation::Left => pos.saturating_sub(n),
879 MoveOperation::StartOfBlock | MoveOperation::StartOfLine => {
880 let inner = self.doc.lock();
881 let dto = frontend::document_inspection::GetBlockAtPositionDto {
882 position: to_i64(pos),
883 };
884 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
885 .map(|info| to_usize(info.block_start))
886 .unwrap_or(pos)
887 }
888 MoveOperation::EndOfBlock | MoveOperation::EndOfLine => {
889 let inner = self.doc.lock();
890 let dto = frontend::document_inspection::GetBlockAtPositionDto {
891 position: to_i64(pos),
892 };
893 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
894 .map(|info| to_usize(info.block_start) + to_usize(info.block_length))
895 .unwrap_or(pos)
896 }
897 MoveOperation::NextBlock => {
898 let inner = self.doc.lock();
899 let dto = frontend::document_inspection::GetBlockAtPositionDto {
900 position: to_i64(pos),
901 };
902 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
903 .map(|info| {
904 to_usize(info.block_start) + to_usize(info.block_length) + 1
906 })
907 .unwrap_or(pos)
908 }
909 MoveOperation::PreviousBlock => {
910 let inner = self.doc.lock();
911 let dto = frontend::document_inspection::GetBlockAtPositionDto {
912 position: to_i64(pos),
913 };
914 let block_start =
915 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
916 .map(|info| to_usize(info.block_start))
917 .unwrap_or(pos);
918 if block_start >= 2 {
919 let prev_dto = frontend::document_inspection::GetBlockAtPositionDto {
921 position: to_i64(block_start - 2),
922 };
923 document_inspection_commands::get_block_at_position(&inner.ctx, &prev_dto)
924 .map(|info| to_usize(info.block_start))
925 .unwrap_or(0)
926 } else {
927 0
928 }
929 }
930 MoveOperation::NextWord | MoveOperation::EndOfWord | MoveOperation::WordRight => {
931 let (_, end) = self.find_word_boundaries(pos);
932 if end == pos {
934 let inner = self.doc.lock();
936 let stats = document_inspection_commands::get_document_stats(&inner.ctx)
937 .map(|s| to_usize(s.character_count))
938 .unwrap_or(0);
939 let scan_len = (stats - pos).min(64);
940 if scan_len == 0 {
941 return pos;
942 }
943 let dto = frontend::document_inspection::GetTextAtPositionDto {
944 position: to_i64(pos),
945 length: to_i64(scan_len),
946 };
947 if let Ok(r) =
948 document_inspection_commands::get_text_at_position(&inner.ctx, &dto)
949 {
950 for (i, ch) in r.text.chars().enumerate() {
951 if ch.is_alphanumeric() || ch == '_' {
952 let word_pos = pos + i;
954 drop(inner);
955 let (_, word_end) = self.find_word_boundaries(word_pos);
956 return word_end;
957 }
958 }
959 }
960 pos + scan_len
961 } else {
962 end
963 }
964 }
965 MoveOperation::PreviousWord | MoveOperation::StartOfWord | MoveOperation::WordLeft => {
966 let (start, _) = self.find_word_boundaries(pos);
967 if start < pos {
968 start
969 } else if pos > 0 {
970 let mut search = pos - 1;
973 loop {
974 let (ws, we) = self.find_word_boundaries(search);
975 if ws < we {
976 break ws;
978 }
979 if search == 0 {
981 break 0;
982 }
983 search -= 1;
984 }
985 } else {
986 0
987 }
988 }
989 MoveOperation::Up | MoveOperation::Down => {
990 if matches!(op, MoveOperation::Up) {
993 self.resolve_move(MoveOperation::PreviousBlock, 1)
994 } else {
995 self.resolve_move(MoveOperation::NextBlock, 1)
996 }
997 }
998 }
999 }
1000
1001 fn find_word_boundaries(&self, pos: usize) -> (usize, usize) {
1007 let inner = self.doc.lock();
1008 let block_dto = frontend::document_inspection::GetBlockAtPositionDto {
1010 position: to_i64(pos),
1011 };
1012 let block_info =
1013 match document_inspection_commands::get_block_at_position(&inner.ctx, &block_dto) {
1014 Ok(info) => info,
1015 Err(_) => return (pos, pos),
1016 };
1017
1018 let block_start = to_usize(block_info.block_start);
1019 let block_length = to_usize(block_info.block_length);
1020 if block_length == 0 {
1021 return (pos, pos);
1022 }
1023
1024 let dto = frontend::document_inspection::GetTextAtPositionDto {
1025 position: to_i64(block_start),
1026 length: to_i64(block_length),
1027 };
1028 let text = match document_inspection_commands::get_text_at_position(&inner.ctx, &dto) {
1029 Ok(r) => r.text,
1030 Err(_) => return (pos, pos),
1031 };
1032
1033 let cursor_offset = pos.saturating_sub(block_start);
1035
1036 let mut last_char_start = 0;
1038 let mut last_char_end = 0;
1039
1040 for (word_byte_start, word) in text.unicode_word_indices() {
1041 let word_char_start = text[..word_byte_start].chars().count();
1043 let word_char_len = word.chars().count();
1044 let word_char_end = word_char_start + word_char_len;
1045
1046 last_char_start = word_char_start;
1047 last_char_end = word_char_end;
1048
1049 if cursor_offset >= word_char_start && cursor_offset < word_char_end {
1050 return (block_start + word_char_start, block_start + word_char_end);
1051 }
1052 }
1053
1054 if cursor_offset == last_char_end && last_char_start < last_char_end {
1056 return (block_start + last_char_start, block_start + last_char_end);
1057 }
1058
1059 (pos, pos)
1060 }
1061}