1use std::sync::Arc;
4
5use parking_lot::Mutex;
6
7use anyhow::Result;
8
9use crate::ListStyle;
10use frontend::commands::{
11 document_editing_commands, document_formatting_commands, document_inspection_commands,
12 inline_element_commands, undo_redo_commands,
13};
14
15use unicode_segmentation::UnicodeSegmentation;
16
17use crate::convert::{to_i64, to_usize};
18use crate::events::DocumentEvent;
19use crate::flow::TableCellRef;
20use crate::fragment::DocumentFragment;
21use crate::inner::{CursorData, QueuedEvents, TextDocumentInner};
22use crate::text_table::TextTable;
23use crate::{BlockFormat, FrameFormat, MoveMode, MoveOperation, SelectionType, TextFormat};
24
25pub struct TextCursor {
33 pub(crate) doc: Arc<Mutex<TextDocumentInner>>,
34 pub(crate) data: Arc<Mutex<CursorData>>,
35}
36
37impl Clone for TextCursor {
38 fn clone(&self) -> Self {
39 let (position, anchor) = {
40 let d = self.data.lock();
41 (d.position, d.anchor)
42 };
43 let data = {
44 let mut inner = self.doc.lock();
45 let data = Arc::new(Mutex::new(CursorData { position, anchor }));
46 inner.cursors.push(Arc::downgrade(&data));
47 data
48 };
49 TextCursor {
50 doc: self.doc.clone(),
51 data,
52 }
53 }
54}
55
56impl TextCursor {
57 fn read_cursor(&self) -> (usize, usize) {
60 let d = self.data.lock();
61 (d.position, d.anchor)
62 }
63
64 fn finish_edit(
68 &self,
69 inner: &mut TextDocumentInner,
70 edit_pos: usize,
71 removed: usize,
72 new_pos: usize,
73 blocks_affected: usize,
74 ) -> QueuedEvents {
75 let added = new_pos - edit_pos;
76 inner.adjust_cursors(edit_pos, removed, added);
77 {
78 let mut d = self.data.lock();
79 d.position = new_pos;
80 d.anchor = new_pos;
81 }
82 inner.modified = true;
83 inner.invalidate_text_cache();
84 inner.queue_event(DocumentEvent::ContentsChanged {
85 position: edit_pos,
86 chars_removed: removed,
87 chars_added: added,
88 blocks_affected,
89 });
90 inner.check_block_count_changed();
91 inner.check_flow_changed();
92 self.queue_undo_redo_event(inner)
93 }
94
95 pub fn position(&self) -> usize {
99 self.data.lock().position
100 }
101
102 pub fn anchor(&self) -> usize {
104 self.data.lock().anchor
105 }
106
107 pub fn has_selection(&self) -> bool {
109 let d = self.data.lock();
110 d.position != d.anchor
111 }
112
113 pub fn selection_start(&self) -> usize {
115 let d = self.data.lock();
116 d.position.min(d.anchor)
117 }
118
119 pub fn selection_end(&self) -> usize {
121 let d = self.data.lock();
122 d.position.max(d.anchor)
123 }
124
125 pub fn selected_text(&self) -> Result<String> {
127 let (pos, anchor) = self.read_cursor();
128 if pos == anchor {
129 return Ok(String::new());
130 }
131 let start = pos.min(anchor);
132 let len = pos.max(anchor) - start;
133 let inner = self.doc.lock();
134 let dto = frontend::document_inspection::GetTextAtPositionDto {
135 position: to_i64(start),
136 length: to_i64(len),
137 };
138 let result = document_inspection_commands::get_text_at_position(&inner.ctx, &dto)?;
139 Ok(result.text)
140 }
141
142 pub fn clear_selection(&self) {
144 let mut d = self.data.lock();
145 d.anchor = d.position;
146 }
147
148 pub fn at_block_start(&self) -> bool {
152 let pos = self.position();
153 let inner = self.doc.lock();
154 let dto = frontend::document_inspection::GetBlockAtPositionDto {
155 position: to_i64(pos),
156 };
157 if let Ok(info) = document_inspection_commands::get_block_at_position(&inner.ctx, &dto) {
158 pos == to_usize(info.block_start)
159 } else {
160 false
161 }
162 }
163
164 pub fn at_block_end(&self) -> bool {
166 let pos = self.position();
167 let inner = self.doc.lock();
168 let dto = frontend::document_inspection::GetBlockAtPositionDto {
169 position: to_i64(pos),
170 };
171 if let Ok(info) = document_inspection_commands::get_block_at_position(&inner.ctx, &dto) {
172 pos == to_usize(info.block_start) + to_usize(info.block_length)
173 } else {
174 false
175 }
176 }
177
178 pub fn at_start(&self) -> bool {
180 self.data.lock().position == 0
181 }
182
183 pub fn at_end(&self) -> bool {
185 let pos = self.position();
186 let inner = self.doc.lock();
187 let stats = document_inspection_commands::get_document_stats(&inner.ctx).unwrap_or({
188 frontend::document_inspection::DocumentStatsDto {
189 character_count: 0,
190 word_count: 0,
191 block_count: 0,
192 frame_count: 0,
193 image_count: 0,
194 list_count: 0,
195 table_count: 0,
196 }
197 });
198 pos >= to_usize(stats.character_count)
199 }
200
201 pub fn block_number(&self) -> usize {
203 let pos = self.position();
204 let inner = self.doc.lock();
205 let dto = frontend::document_inspection::GetBlockAtPositionDto {
206 position: to_i64(pos),
207 };
208 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
209 .map(|info| to_usize(info.block_number))
210 .unwrap_or(0)
211 }
212
213 pub fn position_in_block(&self) -> usize {
215 let pos = self.position();
216 let inner = self.doc.lock();
217 let dto = frontend::document_inspection::GetBlockAtPositionDto {
218 position: to_i64(pos),
219 };
220 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
221 .map(|info| pos.saturating_sub(to_usize(info.block_start)))
222 .unwrap_or(0)
223 }
224
225 pub fn set_position(&self, position: usize, mode: MoveMode) {
229 let end = {
231 let inner = self.doc.lock();
232 document_inspection_commands::get_document_stats(&inner.ctx)
233 .map(|s| to_usize(s.character_count))
234 .unwrap_or(0)
235 };
236 let pos = position.min(end);
237 let mut d = self.data.lock();
238 d.position = pos;
239 if mode == MoveMode::MoveAnchor {
240 d.anchor = pos;
241 }
242 }
243
244 pub fn move_position(&self, operation: MoveOperation, mode: MoveMode, n: usize) -> bool {
250 let old_pos = self.position();
251 let target = self.resolve_move(operation, n);
252 self.set_position(target, mode);
253 self.position() != old_pos
254 }
255
256 pub fn select(&self, selection: SelectionType) {
258 match selection {
259 SelectionType::Document => {
260 let end = {
261 let inner = self.doc.lock();
262 document_inspection_commands::get_document_stats(&inner.ctx)
263 .map(|s| to_usize(s.character_count))
264 .unwrap_or(0)
265 };
266 let mut d = self.data.lock();
267 d.anchor = 0;
268 d.position = end;
269 }
270 SelectionType::BlockUnderCursor | SelectionType::LineUnderCursor => {
271 let pos = self.position();
272 let inner = self.doc.lock();
273 let dto = frontend::document_inspection::GetBlockAtPositionDto {
274 position: to_i64(pos),
275 };
276 if let Ok(info) =
277 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
278 {
279 let start = to_usize(info.block_start);
280 let end = start + to_usize(info.block_length);
281 drop(inner);
282 let mut d = self.data.lock();
283 d.anchor = start;
284 d.position = end;
285 }
286 }
287 SelectionType::WordUnderCursor => {
288 let pos = self.position();
289 let (word_start, word_end) = self.find_word_boundaries(pos);
290 let mut d = self.data.lock();
291 d.anchor = word_start;
292 d.position = word_end;
293 }
294 }
295 }
296
297 pub fn insert_text(&self, text: &str) -> Result<()> {
301 let (pos, anchor) = self.read_cursor();
302
303 let dto = frontend::document_editing::InsertTextDto {
305 position: to_i64(pos),
306 anchor: to_i64(anchor),
307 text: text.into(),
308 };
309
310 let queued = {
311 let mut inner = self.doc.lock();
312 let result = match document_editing_commands::insert_text(
313 &inner.ctx,
314 Some(inner.stack_id),
315 &dto,
316 ) {
317 Ok(r) => r,
318 Err(_) if pos != anchor => {
319 undo_redo_commands::begin_composite(&inner.ctx, Some(inner.stack_id));
321
322 let del_dto = frontend::document_editing::DeleteTextDto {
323 position: to_i64(pos),
324 anchor: to_i64(anchor),
325 };
326 let del_result = document_editing_commands::delete_text(
327 &inner.ctx,
328 Some(inner.stack_id),
329 &del_dto,
330 )?;
331 let del_pos = to_usize(del_result.new_position);
332
333 let ins_dto = frontend::document_editing::InsertTextDto {
334 position: to_i64(del_pos),
335 anchor: to_i64(del_pos),
336 text: text.into(),
337 };
338 let ins_result = document_editing_commands::insert_text(
339 &inner.ctx,
340 Some(inner.stack_id),
341 &ins_dto,
342 )?;
343
344 undo_redo_commands::end_composite(&inner.ctx);
345 ins_result
346 }
347 Err(e) => return Err(e),
348 };
349
350 let edit_pos = pos.min(anchor);
351 let removed = pos.max(anchor) - edit_pos;
352 self.finish_edit(
353 &mut inner,
354 edit_pos,
355 removed,
356 to_usize(result.new_position),
357 to_usize(result.blocks_affected),
358 )
359 };
360 crate::inner::dispatch_queued_events(queued);
361 Ok(())
362 }
363
364 pub fn insert_formatted_text(&self, text: &str, format: &TextFormat) -> Result<()> {
366 let (pos, anchor) = self.read_cursor();
367 let queued = {
368 let mut inner = self.doc.lock();
369 let dto = frontend::document_editing::InsertFormattedTextDto {
370 position: to_i64(pos),
371 anchor: to_i64(anchor),
372 text: text.into(),
373 font_family: format.font_family.clone().unwrap_or_default(),
374 font_point_size: format.font_point_size.map(|v| v as i64).unwrap_or(0),
375 font_bold: format.font_bold.unwrap_or(false),
376 font_italic: format.font_italic.unwrap_or(false),
377 font_underline: format.font_underline.unwrap_or(false),
378 font_strikeout: format.font_strikeout.unwrap_or(false),
379 };
380 let result = document_editing_commands::insert_formatted_text(
381 &inner.ctx,
382 Some(inner.stack_id),
383 &dto,
384 )?;
385 let edit_pos = pos.min(anchor);
386 let removed = pos.max(anchor) - edit_pos;
387 self.finish_edit(
388 &mut inner,
389 edit_pos,
390 removed,
391 to_usize(result.new_position),
392 1,
393 )
394 };
395 crate::inner::dispatch_queued_events(queued);
396 Ok(())
397 }
398
399 pub fn insert_block(&self) -> Result<()> {
401 let (pos, anchor) = self.read_cursor();
402 let queued = {
403 let mut inner = self.doc.lock();
404 let dto = frontend::document_editing::InsertBlockDto {
405 position: to_i64(pos),
406 anchor: to_i64(anchor),
407 };
408 let result =
409 document_editing_commands::insert_block(&inner.ctx, Some(inner.stack_id), &dto)?;
410 let edit_pos = pos.min(anchor);
411 let removed = pos.max(anchor) - edit_pos;
412 self.finish_edit(
413 &mut inner,
414 edit_pos,
415 removed,
416 to_usize(result.new_position),
417 2,
418 )
419 };
420 crate::inner::dispatch_queued_events(queued);
421 Ok(())
422 }
423
424 pub fn insert_html(&self, html: &str) -> Result<()> {
426 let (pos, anchor) = self.read_cursor();
427 let queued = {
428 let mut inner = self.doc.lock();
429 let dto = frontend::document_editing::InsertHtmlAtPositionDto {
430 position: to_i64(pos),
431 anchor: to_i64(anchor),
432 html: html.into(),
433 };
434 let result = document_editing_commands::insert_html_at_position(
435 &inner.ctx,
436 Some(inner.stack_id),
437 &dto,
438 )?;
439 let edit_pos = pos.min(anchor);
440 let removed = pos.max(anchor) - edit_pos;
441 self.finish_edit(
442 &mut inner,
443 edit_pos,
444 removed,
445 to_usize(result.new_position),
446 to_usize(result.blocks_added),
447 )
448 };
449 crate::inner::dispatch_queued_events(queued);
450 Ok(())
451 }
452
453 pub fn insert_markdown(&self, markdown: &str) -> Result<()> {
455 let (pos, anchor) = self.read_cursor();
456 let queued = {
457 let mut inner = self.doc.lock();
458 let dto = frontend::document_editing::InsertMarkdownAtPositionDto {
459 position: to_i64(pos),
460 anchor: to_i64(anchor),
461 markdown: markdown.into(),
462 };
463 let result = document_editing_commands::insert_markdown_at_position(
464 &inner.ctx,
465 Some(inner.stack_id),
466 &dto,
467 )?;
468 let edit_pos = pos.min(anchor);
469 let removed = pos.max(anchor) - edit_pos;
470 self.finish_edit(
471 &mut inner,
472 edit_pos,
473 removed,
474 to_usize(result.new_position),
475 to_usize(result.blocks_added),
476 )
477 };
478 crate::inner::dispatch_queued_events(queued);
479 Ok(())
480 }
481
482 pub fn insert_fragment(&self, fragment: &DocumentFragment) -> Result<()> {
484 let (pos, anchor) = self.read_cursor();
485 let queued = {
486 let mut inner = self.doc.lock();
487 let dto = frontend::document_editing::InsertFragmentDto {
488 position: to_i64(pos),
489 anchor: to_i64(anchor),
490 fragment_data: fragment.raw_data().into(),
491 };
492 let result =
493 document_editing_commands::insert_fragment(&inner.ctx, Some(inner.stack_id), &dto)?;
494 let edit_pos = pos.min(anchor);
495 let removed = pos.max(anchor) - edit_pos;
496 self.finish_edit(
497 &mut inner,
498 edit_pos,
499 removed,
500 to_usize(result.new_position),
501 to_usize(result.blocks_added),
502 )
503 };
504 crate::inner::dispatch_queued_events(queued);
505 Ok(())
506 }
507
508 pub fn selection(&self) -> DocumentFragment {
510 let (pos, anchor) = self.read_cursor();
511 if pos == anchor {
512 return DocumentFragment::new();
513 }
514 let inner = self.doc.lock();
515 let dto = frontend::document_inspection::ExtractFragmentDto {
516 position: to_i64(pos),
517 anchor: to_i64(anchor),
518 };
519 match document_inspection_commands::extract_fragment(&inner.ctx, &dto) {
520 Ok(result) => DocumentFragment::from_raw(result.fragment_data, result.plain_text),
521 Err(_) => DocumentFragment::new(),
522 }
523 }
524
525 pub fn insert_image(&self, name: &str, width: u32, height: u32) -> Result<()> {
527 let (pos, anchor) = self.read_cursor();
528 let queued = {
529 let mut inner = self.doc.lock();
530 let dto = frontend::document_editing::InsertImageDto {
531 position: to_i64(pos),
532 anchor: to_i64(anchor),
533 image_name: name.into(),
534 width: width as i64,
535 height: height as i64,
536 };
537 let result =
538 document_editing_commands::insert_image(&inner.ctx, Some(inner.stack_id), &dto)?;
539 let edit_pos = pos.min(anchor);
540 let removed = pos.max(anchor) - edit_pos;
541 self.finish_edit(
542 &mut inner,
543 edit_pos,
544 removed,
545 to_usize(result.new_position),
546 1,
547 )
548 };
549 crate::inner::dispatch_queued_events(queued);
550 Ok(())
551 }
552
553 pub fn insert_frame(&self) -> Result<()> {
555 let (pos, anchor) = self.read_cursor();
556 let queued = {
557 let mut inner = self.doc.lock();
558 let dto = frontend::document_editing::InsertFrameDto {
559 position: to_i64(pos),
560 anchor: to_i64(anchor),
561 };
562 document_editing_commands::insert_frame(&inner.ctx, Some(inner.stack_id), &dto)?;
563 inner.modified = true;
566 inner.invalidate_text_cache();
567 inner.queue_event(DocumentEvent::ContentsChanged {
568 position: pos.min(anchor),
569 chars_removed: 0,
570 chars_added: 0,
571 blocks_affected: 1,
572 });
573 inner.check_block_count_changed();
574 inner.check_flow_changed();
575 self.queue_undo_redo_event(&mut inner)
576 };
577 crate::inner::dispatch_queued_events(queued);
578 Ok(())
579 }
580
581 pub fn insert_table(&self, rows: usize, columns: usize) -> Result<TextTable> {
587 let (pos, anchor) = self.read_cursor();
588 let (table_id, queued) = {
589 let mut inner = self.doc.lock();
590 let dto = frontend::document_editing::InsertTableDto {
591 position: to_i64(pos),
592 anchor: to_i64(anchor),
593 rows: to_i64(rows),
594 columns: to_i64(columns),
595 };
596 let result =
597 document_editing_commands::insert_table(&inner.ctx, Some(inner.stack_id), &dto)?;
598 let new_pos = to_usize(result.new_position);
599 let table_id = to_usize(result.table_id);
600 inner.adjust_cursors(pos.min(anchor), 0, new_pos - pos.min(anchor));
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: pos.min(anchor),
610 chars_removed: 0,
611 chars_added: new_pos - pos.min(anchor),
612 blocks_affected: 1,
613 });
614 inner.check_block_count_changed();
615 inner.check_flow_changed();
616 (table_id, self.queue_undo_redo_event(&mut inner))
617 };
618 crate::inner::dispatch_queued_events(queued);
619 Ok(TextTable {
620 doc: self.doc.clone(),
621 table_id,
622 })
623 }
624
625 pub fn current_table(&self) -> Option<TextTable> {
630 self.current_table_cell().map(|c| c.table)
631 }
632
633 pub fn current_table_cell(&self) -> Option<TableCellRef> {
638 let pos = self.position();
639 let inner = self.doc.lock();
640 let dto = frontend::document_inspection::GetBlockAtPositionDto {
642 position: to_i64(pos),
643 };
644 let block_info =
645 document_inspection_commands::get_block_at_position(&inner.ctx, &dto).ok()?;
646 let block = crate::text_block::TextBlock {
647 doc: self.doc.clone(),
648 block_id: block_info.block_id as usize,
649 };
650 drop(inner);
652 block.table_cell()
653 }
654
655 pub fn remove_table(&self, table_id: usize) -> Result<()> {
659 let queued = {
660 let mut inner = self.doc.lock();
661 let dto = frontend::document_editing::RemoveTableDto {
662 table_id: to_i64(table_id),
663 };
664 document_editing_commands::remove_table(&inner.ctx, Some(inner.stack_id), &dto)?;
665 inner.modified = true;
666 inner.invalidate_text_cache();
667 inner.check_block_count_changed();
668 inner.check_flow_changed();
669 self.queue_undo_redo_event(&mut inner)
670 };
671 crate::inner::dispatch_queued_events(queued);
672 Ok(())
673 }
674
675 pub fn insert_table_row(&self, table_id: usize, row_index: usize) -> Result<()> {
677 let queued = {
678 let mut inner = self.doc.lock();
679 let dto = frontend::document_editing::InsertTableRowDto {
680 table_id: to_i64(table_id),
681 row_index: to_i64(row_index),
682 };
683 document_editing_commands::insert_table_row(&inner.ctx, Some(inner.stack_id), &dto)?;
684 inner.modified = true;
685 inner.invalidate_text_cache();
686 inner.check_block_count_changed();
687 self.queue_undo_redo_event(&mut inner)
688 };
689 crate::inner::dispatch_queued_events(queued);
690 Ok(())
691 }
692
693 pub fn insert_table_column(&self, table_id: usize, column_index: usize) -> Result<()> {
695 let queued = {
696 let mut inner = self.doc.lock();
697 let dto = frontend::document_editing::InsertTableColumnDto {
698 table_id: to_i64(table_id),
699 column_index: to_i64(column_index),
700 };
701 document_editing_commands::insert_table_column(&inner.ctx, Some(inner.stack_id), &dto)?;
702 inner.modified = true;
703 inner.invalidate_text_cache();
704 inner.check_block_count_changed();
705 self.queue_undo_redo_event(&mut inner)
706 };
707 crate::inner::dispatch_queued_events(queued);
708 Ok(())
709 }
710
711 pub fn remove_table_row(&self, table_id: usize, row_index: usize) -> Result<()> {
713 let queued = {
714 let mut inner = self.doc.lock();
715 let dto = frontend::document_editing::RemoveTableRowDto {
716 table_id: to_i64(table_id),
717 row_index: to_i64(row_index),
718 };
719 document_editing_commands::remove_table_row(&inner.ctx, Some(inner.stack_id), &dto)?;
720 inner.modified = true;
721 inner.invalidate_text_cache();
722 inner.check_block_count_changed();
723 self.queue_undo_redo_event(&mut inner)
724 };
725 crate::inner::dispatch_queued_events(queued);
726 Ok(())
727 }
728
729 pub fn remove_table_column(&self, table_id: usize, column_index: usize) -> Result<()> {
731 let queued = {
732 let mut inner = self.doc.lock();
733 let dto = frontend::document_editing::RemoveTableColumnDto {
734 table_id: to_i64(table_id),
735 column_index: to_i64(column_index),
736 };
737 document_editing_commands::remove_table_column(&inner.ctx, Some(inner.stack_id), &dto)?;
738 inner.modified = true;
739 inner.invalidate_text_cache();
740 inner.check_block_count_changed();
741 self.queue_undo_redo_event(&mut inner)
742 };
743 crate::inner::dispatch_queued_events(queued);
744 Ok(())
745 }
746
747 pub fn merge_table_cells(
749 &self,
750 table_id: usize,
751 start_row: usize,
752 start_column: usize,
753 end_row: usize,
754 end_column: usize,
755 ) -> Result<()> {
756 let queued = {
757 let mut inner = self.doc.lock();
758 let dto = frontend::document_editing::MergeTableCellsDto {
759 table_id: to_i64(table_id),
760 start_row: to_i64(start_row),
761 start_column: to_i64(start_column),
762 end_row: to_i64(end_row),
763 end_column: to_i64(end_column),
764 };
765 document_editing_commands::merge_table_cells(&inner.ctx, Some(inner.stack_id), &dto)?;
766 inner.modified = true;
767 inner.invalidate_text_cache();
768 inner.check_block_count_changed();
769 self.queue_undo_redo_event(&mut inner)
770 };
771 crate::inner::dispatch_queued_events(queued);
772 Ok(())
773 }
774
775 pub fn split_table_cell(
777 &self,
778 cell_id: usize,
779 split_rows: usize,
780 split_columns: usize,
781 ) -> Result<()> {
782 let queued = {
783 let mut inner = self.doc.lock();
784 let dto = frontend::document_editing::SplitTableCellDto {
785 cell_id: to_i64(cell_id),
786 split_rows: to_i64(split_rows),
787 split_columns: to_i64(split_columns),
788 };
789 document_editing_commands::split_table_cell(&inner.ctx, Some(inner.stack_id), &dto)?;
790 inner.modified = true;
791 inner.invalidate_text_cache();
792 inner.check_block_count_changed();
793 self.queue_undo_redo_event(&mut inner)
794 };
795 crate::inner::dispatch_queued_events(queued);
796 Ok(())
797 }
798
799 pub fn set_table_format(
803 &self,
804 table_id: usize,
805 format: &crate::flow::TableFormat,
806 ) -> Result<()> {
807 let queued = {
808 let mut inner = self.doc.lock();
809 let dto = format.to_set_dto(table_id);
810 document_formatting_commands::set_table_format(&inner.ctx, Some(inner.stack_id), &dto)?;
811 inner.modified = true;
812 inner.queue_event(DocumentEvent::FormatChanged {
813 position: 0,
814 length: 0,
815 kind: crate::flow::FormatChangeKind::Block,
816 });
817 self.queue_undo_redo_event(&mut inner)
818 };
819 crate::inner::dispatch_queued_events(queued);
820 Ok(())
821 }
822
823 pub fn set_table_cell_format(
825 &self,
826 cell_id: usize,
827 format: &crate::flow::CellFormat,
828 ) -> Result<()> {
829 let queued = {
830 let mut inner = self.doc.lock();
831 let dto = format.to_set_dto(cell_id);
832 document_formatting_commands::set_table_cell_format(
833 &inner.ctx,
834 Some(inner.stack_id),
835 &dto,
836 )?;
837 inner.modified = true;
838 inner.queue_event(DocumentEvent::FormatChanged {
839 position: 0,
840 length: 0,
841 kind: crate::flow::FormatChangeKind::Block,
842 });
843 self.queue_undo_redo_event(&mut inner)
844 };
845 crate::inner::dispatch_queued_events(queued);
846 Ok(())
847 }
848
849 pub fn remove_current_table(&self) -> Result<()> {
854 let table = self
855 .current_table()
856 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
857 self.remove_table(table.id())
858 }
859
860 pub fn insert_row_above(&self) -> Result<()> {
863 let cell_ref = self
864 .current_table_cell()
865 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
866 self.insert_table_row(cell_ref.table.id(), cell_ref.row)
867 }
868
869 pub fn insert_row_below(&self) -> Result<()> {
872 let cell_ref = self
873 .current_table_cell()
874 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
875 self.insert_table_row(cell_ref.table.id(), cell_ref.row + 1)
876 }
877
878 pub fn insert_column_before(&self) -> Result<()> {
881 let cell_ref = self
882 .current_table_cell()
883 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
884 self.insert_table_column(cell_ref.table.id(), cell_ref.column)
885 }
886
887 pub fn insert_column_after(&self) -> Result<()> {
890 let cell_ref = self
891 .current_table_cell()
892 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
893 self.insert_table_column(cell_ref.table.id(), cell_ref.column + 1)
894 }
895
896 pub fn remove_current_row(&self) -> Result<()> {
899 let cell_ref = self
900 .current_table_cell()
901 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
902 self.remove_table_row(cell_ref.table.id(), cell_ref.row)
903 }
904
905 pub fn remove_current_column(&self) -> Result<()> {
908 let cell_ref = self
909 .current_table_cell()
910 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
911 self.remove_table_column(cell_ref.table.id(), cell_ref.column)
912 }
913
914 pub fn merge_selected_cells(&self) -> Result<()> {
921 let pos_cell = self
922 .current_table_cell()
923 .ok_or_else(|| anyhow::anyhow!("cursor position is not inside a table"))?;
924
925 let (_pos, anchor) = self.read_cursor();
927 let anchor_cell = {
928 let inner = self.doc.lock();
930 let dto = frontend::document_inspection::GetBlockAtPositionDto {
931 position: to_i64(anchor),
932 };
933 let block_info = document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
934 .map_err(|_| anyhow::anyhow!("cursor anchor is not inside a table"))?;
935 let block = crate::text_block::TextBlock {
936 doc: self.doc.clone(),
937 block_id: block_info.block_id as usize,
938 };
939 drop(inner);
940 block
941 .table_cell()
942 .ok_or_else(|| anyhow::anyhow!("cursor anchor is not inside a table"))?
943 };
944
945 if pos_cell.table.id() != anchor_cell.table.id() {
946 return Err(anyhow::anyhow!(
947 "position and anchor are in different tables"
948 ));
949 }
950
951 let start_row = pos_cell.row.min(anchor_cell.row);
952 let start_col = pos_cell.column.min(anchor_cell.column);
953 let end_row = pos_cell.row.max(anchor_cell.row);
954 let end_col = pos_cell.column.max(anchor_cell.column);
955
956 self.merge_table_cells(pos_cell.table.id(), start_row, start_col, end_row, end_col)
957 }
958
959 pub fn split_current_cell(&self, split_rows: usize, split_columns: usize) -> Result<()> {
962 let cell_ref = self
963 .current_table_cell()
964 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
965 let cell = cell_ref
967 .table
968 .cell(cell_ref.row, cell_ref.column)
969 .ok_or_else(|| anyhow::anyhow!("cell not found"))?;
970 self.split_table_cell(cell.id(), split_rows, split_columns)
972 }
973
974 pub fn set_current_table_format(&self, format: &crate::flow::TableFormat) -> Result<()> {
977 let table = self
978 .current_table()
979 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
980 self.set_table_format(table.id(), format)
981 }
982
983 pub fn set_current_cell_format(&self, format: &crate::flow::CellFormat) -> Result<()> {
986 let cell_ref = self
987 .current_table_cell()
988 .ok_or_else(|| anyhow::anyhow!("cursor is not inside a table"))?;
989 let cell = cell_ref
990 .table
991 .cell(cell_ref.row, cell_ref.column)
992 .ok_or_else(|| anyhow::anyhow!("cell not found"))?;
993 self.set_table_cell_format(cell.id(), format)
994 }
995
996 pub fn delete_char(&self) -> Result<()> {
998 let (pos, anchor) = self.read_cursor();
999 let (del_pos, del_anchor) = if pos != anchor {
1000 (pos, anchor)
1001 } else {
1002 (pos, pos + 1)
1003 };
1004 self.do_delete(del_pos, del_anchor)
1005 }
1006
1007 pub fn delete_previous_char(&self) -> Result<()> {
1009 let (pos, anchor) = self.read_cursor();
1010 let (del_pos, del_anchor) = if pos != anchor {
1011 (pos, anchor)
1012 } else if pos > 0 {
1013 (pos - 1, pos)
1014 } else {
1015 return Ok(());
1016 };
1017 self.do_delete(del_pos, del_anchor)
1018 }
1019
1020 pub fn remove_selected_text(&self) -> Result<String> {
1022 let (pos, anchor) = self.read_cursor();
1023 if pos == anchor {
1024 return Ok(String::new());
1025 }
1026 let queued = {
1027 let mut inner = self.doc.lock();
1028 let dto = frontend::document_editing::DeleteTextDto {
1029 position: to_i64(pos),
1030 anchor: to_i64(anchor),
1031 };
1032 let result =
1033 document_editing_commands::delete_text(&inner.ctx, Some(inner.stack_id), &dto)?;
1034 let edit_pos = pos.min(anchor);
1035 let removed = pos.max(anchor) - edit_pos;
1036 let new_pos = to_usize(result.new_position);
1037 inner.adjust_cursors(edit_pos, removed, 0);
1038 {
1039 let mut d = self.data.lock();
1040 d.position = new_pos;
1041 d.anchor = new_pos;
1042 }
1043 inner.modified = true;
1044 inner.invalidate_text_cache();
1045 inner.queue_event(DocumentEvent::ContentsChanged {
1046 position: edit_pos,
1047 chars_removed: removed,
1048 chars_added: 0,
1049 blocks_affected: 1,
1050 });
1051 inner.check_block_count_changed();
1052 inner.check_flow_changed();
1053 (result.deleted_text, self.queue_undo_redo_event(&mut inner))
1055 };
1056 crate::inner::dispatch_queued_events(queued.1);
1057 Ok(queued.0)
1058 }
1059
1060 pub fn create_list(&self, style: ListStyle) -> Result<()> {
1064 let (pos, anchor) = self.read_cursor();
1065 let queued = {
1066 let mut inner = self.doc.lock();
1067 let dto = frontend::document_editing::CreateListDto {
1068 position: to_i64(pos),
1069 anchor: to_i64(anchor),
1070 style: style.clone(),
1071 };
1072 document_editing_commands::create_list(&inner.ctx, Some(inner.stack_id), &dto)?;
1073 inner.modified = true;
1074 inner.queue_event(DocumentEvent::ContentsChanged {
1075 position: pos.min(anchor),
1076 chars_removed: 0,
1077 chars_added: 0,
1078 blocks_affected: 1,
1079 });
1080 self.queue_undo_redo_event(&mut inner)
1081 };
1082 crate::inner::dispatch_queued_events(queued);
1083 Ok(())
1084 }
1085
1086 pub fn insert_list(&self, style: ListStyle) -> Result<()> {
1088 let (pos, anchor) = self.read_cursor();
1089 let queued = {
1090 let mut inner = self.doc.lock();
1091 let dto = frontend::document_editing::InsertListDto {
1092 position: to_i64(pos),
1093 anchor: to_i64(anchor),
1094 style: style.clone(),
1095 };
1096 let result =
1097 document_editing_commands::insert_list(&inner.ctx, Some(inner.stack_id), &dto)?;
1098 let edit_pos = pos.min(anchor);
1099 let removed = pos.max(anchor) - edit_pos;
1100 self.finish_edit(
1101 &mut inner,
1102 edit_pos,
1103 removed,
1104 to_usize(result.new_position),
1105 1,
1106 )
1107 };
1108 crate::inner::dispatch_queued_events(queued);
1109 Ok(())
1110 }
1111
1112 pub fn char_format(&self) -> Result<TextFormat> {
1116 let pos = self.position();
1117 let inner = self.doc.lock();
1118 let dto = frontend::document_inspection::GetTextAtPositionDto {
1119 position: to_i64(pos),
1120 length: 1,
1121 };
1122 let text_info = document_inspection_commands::get_text_at_position(&inner.ctx, &dto)?;
1123 let element_id = text_info.element_id as u64;
1124 let element = inline_element_commands::get_inline_element(&inner.ctx, &element_id)?
1125 .ok_or_else(|| anyhow::anyhow!("element not found at position"))?;
1126 Ok(TextFormat::from(&element))
1127 }
1128
1129 pub fn block_format(&self) -> Result<BlockFormat> {
1131 let pos = self.position();
1132 let inner = self.doc.lock();
1133 let dto = frontend::document_inspection::GetBlockAtPositionDto {
1134 position: to_i64(pos),
1135 };
1136 let block_info = document_inspection_commands::get_block_at_position(&inner.ctx, &dto)?;
1137 let block_id = block_info.block_id as u64;
1138 let block = frontend::commands::block_commands::get_block(&inner.ctx, &block_id)?
1139 .ok_or_else(|| anyhow::anyhow!("block not found"))?;
1140 Ok(BlockFormat::from(&block))
1141 }
1142
1143 pub fn set_char_format(&self, format: &TextFormat) -> Result<()> {
1147 let (pos, anchor) = self.read_cursor();
1148 let queued = {
1149 let mut inner = self.doc.lock();
1150 let dto = format.to_set_dto(pos, anchor);
1151 document_formatting_commands::set_text_format(&inner.ctx, Some(inner.stack_id), &dto)?;
1152 let start = pos.min(anchor);
1153 let length = pos.max(anchor) - start;
1154 inner.modified = true;
1155 inner.queue_event(DocumentEvent::FormatChanged {
1156 position: start,
1157 length,
1158 kind: crate::flow::FormatChangeKind::Character,
1159 });
1160 self.queue_undo_redo_event(&mut inner)
1161 };
1162 crate::inner::dispatch_queued_events(queued);
1163 Ok(())
1164 }
1165
1166 pub fn merge_char_format(&self, format: &TextFormat) -> Result<()> {
1168 let (pos, anchor) = self.read_cursor();
1169 let queued = {
1170 let mut inner = self.doc.lock();
1171 let dto = format.to_merge_dto(pos, anchor);
1172 document_formatting_commands::merge_text_format(
1173 &inner.ctx,
1174 Some(inner.stack_id),
1175 &dto,
1176 )?;
1177 let start = pos.min(anchor);
1178 let length = pos.max(anchor) - start;
1179 inner.modified = true;
1180 inner.queue_event(DocumentEvent::FormatChanged {
1181 position: start,
1182 length,
1183 kind: crate::flow::FormatChangeKind::Character,
1184 });
1185 self.queue_undo_redo_event(&mut inner)
1186 };
1187 crate::inner::dispatch_queued_events(queued);
1188 Ok(())
1189 }
1190
1191 pub fn set_block_format(&self, format: &BlockFormat) -> Result<()> {
1193 let (pos, anchor) = self.read_cursor();
1194 let queued = {
1195 let mut inner = self.doc.lock();
1196 let dto = format.to_set_dto(pos, anchor);
1197 document_formatting_commands::set_block_format(&inner.ctx, Some(inner.stack_id), &dto)?;
1198 let start = pos.min(anchor);
1199 let length = pos.max(anchor) - start;
1200 inner.modified = true;
1201 inner.queue_event(DocumentEvent::FormatChanged {
1202 position: start,
1203 length,
1204 kind: crate::flow::FormatChangeKind::Block,
1205 });
1206 self.queue_undo_redo_event(&mut inner)
1207 };
1208 crate::inner::dispatch_queued_events(queued);
1209 Ok(())
1210 }
1211
1212 pub fn set_frame_format(&self, frame_id: usize, format: &FrameFormat) -> Result<()> {
1214 let (pos, anchor) = self.read_cursor();
1215 let queued = {
1216 let mut inner = self.doc.lock();
1217 let dto = format.to_set_dto(pos, anchor, frame_id);
1218 document_formatting_commands::set_frame_format(&inner.ctx, Some(inner.stack_id), &dto)?;
1219 let start = pos.min(anchor);
1220 let length = pos.max(anchor) - start;
1221 inner.modified = true;
1222 inner.queue_event(DocumentEvent::FormatChanged {
1223 position: start,
1224 length,
1225 kind: crate::flow::FormatChangeKind::Block,
1226 });
1227 self.queue_undo_redo_event(&mut inner)
1228 };
1229 crate::inner::dispatch_queued_events(queued);
1230 Ok(())
1231 }
1232
1233 pub fn begin_edit_block(&self) {
1237 let inner = self.doc.lock();
1238 undo_redo_commands::begin_composite(&inner.ctx, Some(inner.stack_id));
1239 }
1240
1241 pub fn end_edit_block(&self) {
1243 let inner = self.doc.lock();
1244 undo_redo_commands::end_composite(&inner.ctx);
1245 }
1246
1247 pub fn join_previous_edit_block(&self) {
1254 self.begin_edit_block();
1255 }
1256
1257 fn queue_undo_redo_event(&self, inner: &mut TextDocumentInner) -> QueuedEvents {
1261 let can_undo = undo_redo_commands::can_undo(&inner.ctx, Some(inner.stack_id));
1262 let can_redo = undo_redo_commands::can_redo(&inner.ctx, Some(inner.stack_id));
1263 inner.queue_event(DocumentEvent::UndoRedoChanged { can_undo, can_redo });
1264 inner.take_queued_events()
1265 }
1266
1267 fn do_delete(&self, pos: usize, anchor: usize) -> Result<()> {
1268 let queued = {
1269 let mut inner = self.doc.lock();
1270 let dto = frontend::document_editing::DeleteTextDto {
1271 position: to_i64(pos),
1272 anchor: to_i64(anchor),
1273 };
1274 let result =
1275 document_editing_commands::delete_text(&inner.ctx, Some(inner.stack_id), &dto)?;
1276 let edit_pos = pos.min(anchor);
1277 let removed = pos.max(anchor) - edit_pos;
1278 let new_pos = to_usize(result.new_position);
1279 inner.adjust_cursors(edit_pos, removed, 0);
1280 {
1281 let mut d = self.data.lock();
1282 d.position = new_pos;
1283 d.anchor = new_pos;
1284 }
1285 inner.modified = true;
1286 inner.invalidate_text_cache();
1287 inner.queue_event(DocumentEvent::ContentsChanged {
1288 position: edit_pos,
1289 chars_removed: removed,
1290 chars_added: 0,
1291 blocks_affected: 1,
1292 });
1293 inner.check_block_count_changed();
1294 inner.check_flow_changed();
1295 self.queue_undo_redo_event(&mut inner)
1296 };
1297 crate::inner::dispatch_queued_events(queued);
1298 Ok(())
1299 }
1300
1301 fn resolve_move(&self, op: MoveOperation, n: usize) -> usize {
1303 let pos = self.position();
1304 match op {
1305 MoveOperation::NoMove => pos,
1306 MoveOperation::Start => 0,
1307 MoveOperation::End => {
1308 let inner = self.doc.lock();
1309 document_inspection_commands::get_document_stats(&inner.ctx)
1310 .map(|s| to_usize(s.character_count))
1311 .unwrap_or(pos)
1312 }
1313 MoveOperation::NextCharacter | MoveOperation::Right => pos + n,
1314 MoveOperation::PreviousCharacter | MoveOperation::Left => pos.saturating_sub(n),
1315 MoveOperation::StartOfBlock | MoveOperation::StartOfLine => {
1316 let inner = self.doc.lock();
1317 let dto = frontend::document_inspection::GetBlockAtPositionDto {
1318 position: to_i64(pos),
1319 };
1320 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
1321 .map(|info| to_usize(info.block_start))
1322 .unwrap_or(pos)
1323 }
1324 MoveOperation::EndOfBlock | MoveOperation::EndOfLine => {
1325 let inner = self.doc.lock();
1326 let dto = frontend::document_inspection::GetBlockAtPositionDto {
1327 position: to_i64(pos),
1328 };
1329 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
1330 .map(|info| to_usize(info.block_start) + to_usize(info.block_length))
1331 .unwrap_or(pos)
1332 }
1333 MoveOperation::NextBlock => {
1334 let inner = self.doc.lock();
1335 let dto = frontend::document_inspection::GetBlockAtPositionDto {
1336 position: to_i64(pos),
1337 };
1338 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
1339 .map(|info| {
1340 to_usize(info.block_start) + to_usize(info.block_length) + 1
1342 })
1343 .unwrap_or(pos)
1344 }
1345 MoveOperation::PreviousBlock => {
1346 let inner = self.doc.lock();
1347 let dto = frontend::document_inspection::GetBlockAtPositionDto {
1348 position: to_i64(pos),
1349 };
1350 let block_start =
1351 document_inspection_commands::get_block_at_position(&inner.ctx, &dto)
1352 .map(|info| to_usize(info.block_start))
1353 .unwrap_or(pos);
1354 if block_start >= 2 {
1355 let prev_dto = frontend::document_inspection::GetBlockAtPositionDto {
1357 position: to_i64(block_start - 2),
1358 };
1359 document_inspection_commands::get_block_at_position(&inner.ctx, &prev_dto)
1360 .map(|info| to_usize(info.block_start))
1361 .unwrap_or(0)
1362 } else {
1363 0
1364 }
1365 }
1366 MoveOperation::NextWord | MoveOperation::EndOfWord | MoveOperation::WordRight => {
1367 let (_, end) = self.find_word_boundaries(pos);
1368 if end == pos {
1370 let inner = self.doc.lock();
1372 let stats = document_inspection_commands::get_document_stats(&inner.ctx)
1373 .map(|s| to_usize(s.character_count))
1374 .unwrap_or(0);
1375 let scan_len = (stats - pos).min(64);
1376 if scan_len == 0 {
1377 return pos;
1378 }
1379 let dto = frontend::document_inspection::GetTextAtPositionDto {
1380 position: to_i64(pos),
1381 length: to_i64(scan_len),
1382 };
1383 if let Ok(r) =
1384 document_inspection_commands::get_text_at_position(&inner.ctx, &dto)
1385 {
1386 for (i, ch) in r.text.chars().enumerate() {
1387 if ch.is_alphanumeric() || ch == '_' {
1388 let word_pos = pos + i;
1390 drop(inner);
1391 let (_, word_end) = self.find_word_boundaries(word_pos);
1392 return word_end;
1393 }
1394 }
1395 }
1396 pos + scan_len
1397 } else {
1398 end
1399 }
1400 }
1401 MoveOperation::PreviousWord | MoveOperation::StartOfWord | MoveOperation::WordLeft => {
1402 let (start, _) = self.find_word_boundaries(pos);
1403 if start < pos {
1404 start
1405 } else if pos > 0 {
1406 let mut search = pos - 1;
1409 loop {
1410 let (ws, we) = self.find_word_boundaries(search);
1411 if ws < we {
1412 break ws;
1414 }
1415 if search == 0 {
1417 break 0;
1418 }
1419 search -= 1;
1420 }
1421 } else {
1422 0
1423 }
1424 }
1425 MoveOperation::Up | MoveOperation::Down => {
1426 if matches!(op, MoveOperation::Up) {
1429 self.resolve_move(MoveOperation::PreviousBlock, 1)
1430 } else {
1431 self.resolve_move(MoveOperation::NextBlock, 1)
1432 }
1433 }
1434 }
1435 }
1436
1437 fn find_word_boundaries(&self, pos: usize) -> (usize, usize) {
1443 let inner = self.doc.lock();
1444 let block_dto = frontend::document_inspection::GetBlockAtPositionDto {
1446 position: to_i64(pos),
1447 };
1448 let block_info =
1449 match document_inspection_commands::get_block_at_position(&inner.ctx, &block_dto) {
1450 Ok(info) => info,
1451 Err(_) => return (pos, pos),
1452 };
1453
1454 let block_start = to_usize(block_info.block_start);
1455 let block_length = to_usize(block_info.block_length);
1456 if block_length == 0 {
1457 return (pos, pos);
1458 }
1459
1460 let dto = frontend::document_inspection::GetTextAtPositionDto {
1461 position: to_i64(block_start),
1462 length: to_i64(block_length),
1463 };
1464 let text = match document_inspection_commands::get_text_at_position(&inner.ctx, &dto) {
1465 Ok(r) => r.text,
1466 Err(_) => return (pos, pos),
1467 };
1468
1469 let cursor_offset = pos.saturating_sub(block_start);
1471
1472 let mut last_char_start = 0;
1474 let mut last_char_end = 0;
1475
1476 for (word_byte_start, word) in text.unicode_word_indices() {
1477 let word_char_start = text[..word_byte_start].chars().count();
1479 let word_char_len = word.chars().count();
1480 let word_char_end = word_char_start + word_char_len;
1481
1482 last_char_start = word_char_start;
1483 last_char_end = word_char_end;
1484
1485 if cursor_offset >= word_char_start && cursor_offset < word_char_end {
1486 return (block_start + word_char_start, block_start + word_char_end);
1487 }
1488 }
1489
1490 if cursor_offset == last_char_end && last_char_start < last_char_end {
1492 return (block_start + last_char_start, block_start + last_char_end);
1493 }
1494
1495 (pos, pos)
1496 }
1497}